I have a pre-trained sequential CNN model which I trained on images of 224x224x3. The following is the architecture:
model = Sequential()
model.add(Conv2D(filters = 64, kernel_size = (5, 5), strides = 1, activation = 'relu', input_shape = (224, 224, 3)))
model.add(MaxPool2D(pool_size = (3, 3)))
model.add(Dropout(0.2))
model.add(Conv2D(filters = 128, kernel_size = (3, 3), strides = 1, activation = 'relu'))
model.add(MaxPool2D(pool_size = (2, 2)))
model.add(Dropout(0.2))
model.add(Conv2D(filters = 256, kernel_size = (2, 2), strides = 1, activation = 'relu'))
model.add(MaxPool2D(pool_size = (2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation = 'relu', use_bias=False))
model.add(Dense(num_classes, activation = 'softmax'))
model.summary()
For reference, here is the model summary: model summary
I want to retrain this model on images of size 40x40x3. However, I am facing the following error: "ValueError: Input 0 of layer dense_12 is incompatible with the layer: expected axis -1 of input shape to have value 200704 but received input with shape (None, 256)".
What should I do to resolve this error?
Note: I am using Tensorflow version 2.4.1
The problem is, in your pre-trained model you have a flattened shape of 200704 as input shape (line no 4 from last), and then the output size is 128 for the dense layer (line 3 from the last). And now you wanna use the same pre-trained model for the image of 40X40, it will not work. The reasons are :
1- Your model is input image shape-dependent. it's not an end-to-end conv model, as you use dense layers in between, which makes the model input image size-dependent.
2- The flatten size of the 40x40 image after all the conv layers are 256, not 200704.
Solution
1- Either you change the flatten part with adaptive average pooling layer and then your last dense layer with softmax is fine. And again retrain your old model on 224x224 images. Following that you can train on your 40x40 images.
2- Or the easiest way is to just use a subset of your pre-trained model till the flatten part (exclude the flatten part) and then add a flatten part with dense layer and classification layer (layer with softmax). For this method you have to write a custom model, like here, just the first part will be the subset of the pre-trained model, and flatten and classification part will be additional. And then you can train the whole model over the new dataset. You can also take the benefit of transfer-learning using this method, by allowing the backward gradient to flow only through the newly created linear layer and not through the pre-trained layers.
Related
I am developing LSTM Program for NLP Problem.
Shape of my data and Label is = (10,20,1)
My Model code looks like this :
model.add(Embedding(18,17,input_length=20,weights=[embedding_weights])) ( Shape of Embedding (18,17))
# encoder layer
model.add(LSTM(100, activation='relu', input_shape=(20, 1)))
# repeat vector
model.add(RepeatVector(20))
# decoder layer
model.add(LSTM(100, activation='relu', return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(optimizer='adam', loss='mse')
I am getting following error
"input_length" is 20, but received input has shape (None, 20, 1)
I am playing with a model which should take a 8x8 chess board as input, encoded as a 224x224 grey image, and then output a 64x13 one-hot-encoded logistic regression = probabilities of pieces on the squares.
Now, after the Convolutional layers I don't quite know, how to proceed to get a 2D-Dense layer as a result/target.
I tried adding a Dense(64,13) as a layer to my Sequential model, but I get the error "Dense` can accept only 1 positional arguments ('units',)"
Is it even possible to train for 2D-targets?
EDIT1:
Here is the relevant part of my code, simplified:
# X.shape = (10000, 224, 224, 1)
# Y.shape = (10000, 64, 13)
model = Sequential([
Conv2D(8, (3,3), activation='relu', input_shape=(224, 224, 1)),
Conv2D(8, (3,3), activation='relu'),
# some more repetitive Conv + Pooling Layers here
Flatten(),
Dense(64,13)
])
TypeError: Dense can accept only 1 positional arguments ('units',), but you passed the following positional arguments: [64, 13]
EDIT2: As Anand V. Singh suggested, I changed Dense(64, 13) to Dense(832), which works fine. Loss = mse.
Wouldn't it be better to use "sparse_categorical_crossentropy" as loss and 64x1 encoding (instead of 64x13) ?
In Dense you only pass the number of layers you expect as output, if you want (64x13) as output, put the layer dimension as Dense(832) (64x13 = 832) and then reshape later. You will also need to reshape Y so as to accurately calculate loss, which will be used for back propagation.
# X.shape = (10000, 224, 224, 1)
# Y.shape = (10000, 64, 13)
Y = Y.reshape(10000, 64*13)
model = Sequential([
Conv2D(8, (3,3), activation='relu', input_shape=(224, 224, 1)),
Conv2D(8, (3,3), activation='relu'),
# some more repetitive Conv + Pooling Layers here
Flatten(),
Dense(64*13)
])
That should get the job done, if it doesn't post where it fails and we can proceed further.
A Reshape layer allows you to control the output shape.
Flatten(),
Dense(64*13),
Reshape((64,13))#2D
I'm training an CNN with LSTM, where I use TimeDistributed but apparently it wants an extra dimension for the data. I don't know how to add it.
My thought is that the problem is in ImageGenerator, but I don't know how to reshape images generated from it.
cnn_model = Sequential()
cnn_model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(128,128,3)))
cnn_model.add(MaxPooling2D(pool_size=(2, 2)))
cnn_model.add(Conv2D(32, (3, 3), activation='relu'))
cnn_model.add(MaxPooling2D(pool_size=(2, 2)))
cnn_model.add(Conv2D(64, (3, 3), activation='relu'))
cnn_model.add(MaxPooling2D(pool_size=(2, 2)))
cnn_model.add(Conv2D(128, (3, 3), activation='relu'))
cnn_model.add(MaxPooling2D(pool_size=(2, 2)))
cnn_model.add(Flatten())
model = Sequential()
model.add(TimeDistributed(cnn_model, input_shape=(16, 128, 128,3)))
model.add(LSTM(128, return_sequences=True, dropout=0.5))
# model.add(Dropout(0.2)) #added
model.add(Dense(4, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
batch_size = 16
train_datagen = ImageDataGenerator(rescale=1. / 255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
'train/', # this is the target directory
target_size=(128,128),
batch_size=batch_size,
class_mode='categorical',
shuffle=True,
classes=['class_0', 'class_1','class_2','class_3'])
validation_generator = test_datagen.flow_from_directory(
'test/',
target_size=(128,128),
batch_size=batch_size,
class_mode='categorical',
shuffle=True,
classes=['class_0', 'class_1','class_2','class_3'])
model.fit_generator(
train_generator,
steps_per_epoch=47549 // batch_size,
epochs=5,
validation_data=validation_generator,
validation_steps=5444 // batch_size)
But I'm getting the following error message
ValueError: Error when checking input: expected time_distributed_136_input to have 5 dimensions, but got array with shape (16, 128, 128, 3)
Data folder is as follows:
-- train
-- class 0
-- vid 1
-- frame1.jpg
-- frame2.jpg
-- frame3.jpg
-- class 1
-- frame1.jpg
-- frame2.jpg
-- frame3.jpg
-- class 2
-- class 3
-- test
(same as train)
Thanks for any help.
You're fogetting the first dimension of every tensor, which is the batch size. You don't define a batch size unless it's absolutely necessary, thus the input shapes don't consider it.
When you defined input_shape=(16,128,128,3), this means that your data must have five dimensions: (examples, 16, 128, 128, 3)
And the examples dimension is missing in your data.
If you say they're movies, you should have data like (movies, frames, height, width, channels), probably. Then this would be accepted by input_shape=(frames, height, width, channels).
After several trials, I ended up using the same code, but with a tweaked version of the Keras "ImageDataGenerator" Class to add an extra dimension to the data, so that it becomes 5D.
(This is also valid for the use of Conv3D)
For anyone who face the same problem, you can find my tweaked version of the ImageDataGenerator class here.
It's the same as the main Keras ImageDataGenerator but I added an option to take more than one image/frame on each iteration. This is by changing the parameter frames_per_step to specify the number of frames/images you want to include in each iteration.
So here's how to use it:
from tweaked_ImageGenerator_v2 import ImageDataGenerator
datagen = ImageDataGenerator()
train_data=datagen.flow_from_directory('path/to/data', target_size=(x, y), batch_size=32, frames_per_step=4)
I think your problem is with your model. You define the input shape of your TimeDistributed in your model as input_shape=(16, 128, 128,3) which I guess it should be input_shape=(128, 128,3).
change this line :
model.add(TimeDistributed(cnn_model, input_shape=(16, 128, 128,3)))
to:
model.add(TimeDistributed(cnn_model, input_shape=(128, 128,3)))
And I hope it will work.
I want to train my model on video data for gesture recognition, proposed using LSTM's and TimeDistributed layers. Would this be ideal way to tackle my problem?
# Convolution
pool_size = 4
# LSTM
lstm_output_size = 1
print('Build model...')
model = Sequential()
model.add(TimeDistributed(Dense(62), input_shape=(img_width, img_height,3)))
model.add(Conv2D(32, (3, 3)))
model.add(Dropout(0.25))
model.add(Conv2D(32, (3, 3)))
model.add(MaxPooling2D(pool_size=pool_size))
# model.add(Dense(1))
model.add(TimeDistributed(Flatten()))
model.add(CuDNNLSTM(256, return_sequences=True))
model.add(CuDNNLSTM(256, return_sequences=True))
model.add(CuDNNLSTM(256, return_sequences=True))
model.add(CuDNNLSTM(lstm_output_size))
model.add(Dense(units = 1, activation = 'sigmoid'))
print('Train...')
model.summary()
# Run epochs of sampling data then training
For temporal sequence data LSTM networks are generally the right choice. If you want to analyze video then a combination with 2d convolutions sounds reasonable to me. However, you have to apply TimeDistributed on all layers which dont expect sequence data. In your example that means all laysers expect LSTM.
# Convolution
pool_size = 4
# LSTM
lstm_output_size = 1
print('Build model...')
model = Sequential()
model.add(TimeDistributed(Dense(62), input_shape=(img_width, img_height,3)))
model.add(TimeDistributed(Conv2D(32, (3, 3))))
model.add(Dropout(0.25))
model.add(TimeDistributed(Conv2D(32, (3, 3))))
model.add(TimeDistributed(MaxPooling2D(pool_size=pool_size)))
# model.add(Dense(1))
model.add(TimeDistributed(Flatten()))
model.add(CuDNNLSTM(256, return_sequences=True))
model.add(CuDNNLSTM(256, return_sequences=True))
model.add(CuDNNLSTM(256, return_sequences=True))
model.add(CuDNNLSTM(lstm_output_size))
model.add(Dense(units = 1, activation = 'sigmoid'))
print('Train...')
model.summary()
# run epochs of sampling data then training
The last Dense Layer can stay this way because the final lstm doesnt output a sequence.
I have a simple single-layer CNN network that I have trained. I would now like to use this model without the top in order to visualize the CNN filters, as illustrated in the Keras.io blog post here.
This is my simple model:
model = Sequential()
model.add(Conv2D(24, (5, 5), padding='valid',input_shape=(1, 17,17), activation='relu',name='conv2d_1'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(8, activation='relu',name='dens_1'))
model.add(Dense(2, activation='softmax',name='dens_2'))
I would like to load the model using load_model, and then create a topless model with a generic input size (1, None,None), such as the model illustrated below. Is this possible? I prefer to not have to explicitly re-define all layers in the model.
model_no_top = Sequential()
model_no_top.add(Conv2D(24, (5, 5), padding='valid',input_shape=(1, None, None), activation='relu',name='conv2d_1'))
model_no_top.add(MaxPooling2D(pool_size=(2, 2)))
model_no_top.add(Dropout(0.5))
I've loaded the model and grabbed the layers that I want, but the input_shape is the same as the original model.
model = load_model(model_path)
model_no_top = Sequential()
for layer in model.layers:
if 'flatten' not in layer.name and 'dens' not in layer.name:
model_no_top.add(layer)