Search code examples
pythontensorflowmachine-learningkerasconv-neural-network

What is the proper way to deal with dimensionality in Conv1D Layers in tensorflow/keras?


I've been at my wits end trying to solve this issue with Conv1D in Keras. Basically, I have a vector bathyZ that is 100 x 1. I'd like to do some convolution on it before merging it with 2 scalar inputs Tperiod and AMP_WK to predict another 100 x 1 vector. (bathyZ has spatial variability I'm looking to pick up). This is my first time using tensorflow datasets and parsing/deserializing. I consistently run into shape errors that I can't seem to understand though.

Mainly, it seems like the first dense layer isn't receiving the correct dimensions in its inputs, and I can't figure out why, since the input dimensions for everything seem to make sense to me. Why is it expecting 6402 and getting (None, 165)? I'm debugging on a single record, hence the small batch size, if that was a concern. It should work regardless though, right? My understanding is that my input should be (None,100,1) to allow for different batch sizes for a 100 x 1 sequence. I've tried reshaping a few ways, but none of them have seemed to work, so maybe I'm missing something more fundamental

Error message:

ValueError: Exception encountered when calling Functional.call().

Input 0 of layer "dense_110" is incompatible with the layer: expected axis -1 of input shape to have value 6402, but received input with shape (None, 165)

Arguments received by Functional.call():
  • inputs={'bathyZ': 'tf.Tensor(shape=(None, 100, 1), dtype=float32)', 'AMP_WK': 'tf.Tensor(shape=(None, 1), dtype=float32)', 'Tperiod': 'tf.Tensor(shape=(None, 1), dtype=float32)'}
  • training=True
  • mask={'bathyZ': 'None', 'AMP_WK': 'None', 'Tperiod': 'None'}

Code:

feature_description = {
        'bathyZ': tf.io.FixedLenFeature([], tf.string),
        'bathyZ_shape': tf.io.FixedLenFeature([3], tf.int64),
        'AMP_WK': tf.io.FixedLenFeature([], tf.float32),
        'Tperiod': tf.io.FixedLenFeature([], tf.float32),
        'skew': tf.io.FixedLenFeature([], tf.string),
        'skew_shape': tf.io.FixedLenFeature([3], tf.int64),
    }
    
    def _parse_function(proto):
        # Parse
        parsed_features = tf.io.parse_single_example(proto, feature_description)
    
        # Decode/reshape the serialized tensors
        bathyZ = parsed_features['bathyZ']
        bathyZ = tf.io.parse_tensor(bathyZ, out_type=tf.float32)
        bathyZ = tf.reshape(bathyZ, [100, 1])
    
        skew = parsed_features['skew']
        skew = tf.io.parse_tensor(skew, out_type=tf.float32)
        skew = tf.reshape(skew, [100, 1])
    
        # Get other inputs, reshape
        AMP_WK = parsed_features['AMP_WK']
        Tperiod = parsed_features['Tperiod']
    
        AMP_WK = tf.reshape(AMP_WK, [1])
        Tperiod = tf.reshape(Tperiod, [1])
        
        # Create tuple
        inputs = {'bathyZ': bathyZ, 'AMP_WK': AMP_WK, 'Tperiod': Tperiod}
        outputs = {'skew': skew}
        
        return inputs, outputs
    
    # Create a TFRecordDataset and map the parsing function
    tfrecord_path = 'ML_0004.tfrecord'
    dataset = tf.data.TFRecordDataset(tfrecord_path)
    dataset = dataset.map(_parse_function)
        
        
    # Model
    
    
    def create_model():
        # Tensor input branch (shape: 100 timesteps, 1 feature)
        bathyZ = Input(shape=(100, 1), name='bathyZ')
        x = layers.Conv1D(32, 3, activation='relu', padding='same')(bathyZ)
        x = layers.Conv1D(64, 3, activation='relu', padding='same')(x)
        x = layers.Flatten()(x)
    
        # Scalar inputs
        AMP_WK = Input(shape=(1,), name='AMP_WK')
        Tperiod = Input(shape=(1,), name='Tperiod')
        
        # Combine all branches
        combined = layers.concatenate([x, AMP_WK, Tperiod])
    
        # Fully connected layer
        z = layers.Dense(64, activation='relu')(combined)
        z = layers.Dense(128, activation='relu')(z)
    
        # Output layer (tensor output, same shape as input tensor)
        skew = layers.Dense(100, activation='linear', name='skew')(z)
    
        # Create the model
        model = models.Model(inputs=[bathyZ, AMP_WK, Tperiod], outputs=skew)
        return model
    
    # Example usage:
    model = create_model()
    model.compile(optimizer='adam', loss='mse')
    model.summary()
    dataset = dataset.batch(1)  
    model.fit(dataset)

I tried reshaping various input tensors in the parsing and preprocessing steps to different versions of [1,100], [1,100,1] and so on, and tracking how the shape evolves. But I always run into some sort of dimension error


Solution

  • For some reason, despite the inputs being stored as a key-value, the order in which they are given to model = models.Model matters. If you reverse the order, and make it model = models.Model(inputs=[Tperiod, AMP_WK, bathyZ], outputs=skew), it works.