I want to make CNN model including multiple inputs. But there is not any examples about that. Without Keras, Can I make multiple inputs model ?
My model has 3 inputs. After the first input(atom_in_fea[8,9]) passes through the embedding(linear or dense in keras) layer, the output should merge with the second(nbr_fea_idx[8,12]) and third input(nbr_fea[8,12,4]).
atom_in_fea[8,9] -> embedding layer -> atom_in_fea[8,4]
[] is their shape.
There is my code
python, tensorflow
atom_nbr_fea = atom_in_fea[nbr_fea_idx, :]
final = tf.concat([atom_in_fea.unsqueeze(1).expand(8, 12, 4),atom_nbr_fea, nbr_fea], 2)
atom_nbr_fea shape is [8,12,4]
Final shape is [8,12,12]
I built a model with Keras but there was limited.
If I can solve this problem, it's fine whether it's Keras or TensorFlow. Thank you
Related
To solve the issue that I've posted here : Adjust the output of a CNN as an input for TimeDistributed tensorflow layer which is about input data format of the Time distributed tensorflow layer, I think about another idea: instead of passing two inputs to a CNN model, what if , before designing the CNN model, I merge the two inputs in one input using pandas or numpy, and then pass it to the CNN model and then AFTER the INPUT LAYER and BEFORE the CONVOLUTION LAYER, I add a customized layer that separate feature that I concatenate them !! Is this possible ? the following picture explain more what I am talking about:
Thank you #Marco for the help. Exactly like Marco says, I separate the input using index slicing and was done using a Lambda layer. This is the code:
input_layer1=tf.keras.Input(shape=(input_shape))
separate_features1 = tf.keras.layers.Lambda(lambda x : tf.transpose(x,[0,1,2,3])[:,:-1,:,:])(input_layer1)
separate_features2 = tf.keras.layers.Lambda(lambda x : tf.transpose(x,[0,1,2,3])[:,-1:,:,:])(input_layer1)
This is the model architecture:
I have data from three sensors, and have broken it into 21,190 938-sample sequences. I have stored this in a tensor, xt, with dimensions (21190, 938, 3), and created a tensor of labels, yt, with dimensions (21190,1). With this data, I would like to create a CNN-LSTM binary classifier. I believe I have found the solution to my problem, but I wanted to post it here to ensure that I am correctly understanding the error I was encountering, and have made the proper fix.
My intention is for the convolution layer to apply filters across the time (938-length) dimension, and for the output from each stride of those filters in the convolution layer to be fed into the LSTM layer as a sample in a temporal sequence. I have put together the network based on an example I found online that seemed to be working with a similar dataset (time series from multiple sensors) and objective (using the CNN to identify/extract features, and the LSTM to find patterns in the sequence of features); but based on my reading of the Tensorflow documentation for the TimeDistributed wrapper, and the errors I am getting, I'm not confident that I have my network set up properly.
Here is the code I am using to create and train the CNN-LSTM:
model = models.Sequential()
model.add(layers.TimeDistributed(layers.Conv1D(filters=32,kernel_size=64),input_shape=(None,938,3)))
model.add(layers.TimeDistributed(layers.Conv1D(filters=32,kernel_size=64)))
model.add(layers.TimeDistributed(layers.Dropout(0.5)))
model.add(layers.TimeDistributed(layers.MaxPooling1D(pool_size=2)))
model.add(layers.TimeDistributed(layers.Flatten()))
model.add(layers.LSTM(100))
model.add(layers.Dense(2))
model.add(layers.Softmax())
model.compile(
loss = losses.SparseCategoricalCrossentropy(from_logits=True),
optimizer="adam",
metrics=["accuracy"],
)
model.fit(
xt,
yt,
epochs=10,
)
When I run this, I get the following error:
ValueError: Input 0 of layer sequential is incompatible with the
layer: expected ndim=4, found ndim=3. Full shape received: (None, 938,
3)
If I remove the TimeDistributed wrappers and the Flatten layer, the network trains with no errors. Am I correct in understanding that using TimeDistributed causes the network to try to apply 1D convolutions on a single sample at a time from each of the three channels, and that this is why I am getting the error? And am I correct that by removing the wrappers, I will get the functionality I desire, with each stride in the convolution layer providing one time-step of data to the LSTM? Thanks in advance for any input.
Issue is with input data shape.
Working sample code
import tensorflow as tf
inputs = tf.keras.Input(shape=(None, 938, 3))
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Conv1D(filters=32,kernel_size=64),input_shape=(None,938,3)))
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Conv1D(filters=32,kernel_size=64)))
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Dropout(0.5)))
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.MaxPooling1D(pool_size=2)))
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Flatten()))
model.add(tf.keras.layers.LSTM(100))
model.add(tf.keras.layers.Dense(2))
model.add(tf.keras.layers.Softmax())
output = model(inputs)
Output
output.shape
TensorShape([None, 2])
I am trying to get a better understanding of CNNs and so I am using keras to basically make a small CNN and want to go through the calculations by hand.
I downloaded the images from the GTSRB database, then using PIL library package converted the image set to greyscale and resized to (6 x 6).
The code below shows the CNN I've created.
It includes 1 convolution layer (with 2 filters of size 2x2), 1 max pooling layer (2x2), a flattening layer and a dense layer at the end.
model = keras.models.Sequential()
model.add(keras.layers.Conv2D(2, kernel_size=(2,2),activation='relu', input_shape=(6,6,1)))
model.add(keras.layers.MaxPool2D(pool_size=(2,2)))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(len(sign_label_list),activation='relu'))
I then trained the network and saved the model and weights.
I read online that for checking the weights (h5 file type), I need a tool to view the weights. So I downloaded HDFView tool.
Now I am trying to view the weights for each of the filters, but I can only see the weights of 1 of the filters.
Filter weights
How would I get the weights of both the filters?
Does anyone know if there is a way to view the weights through python?
Originally, I wanted to test with only 1 filter but I get nan when I view the weights.
Looking through the documentation and Keras FAQ found here.
The suggested way to view weights for a particular layer is to do this:
weights,biases = model.layers[0].get_weights()
I then printed the weights to the console using print(weights) and this displayed the values of all filters.
However, I still had trouble viewing the weights of multiple filters using the HDFView tool.
I have trained a fully convolutional neural network with Keras. I have used the Functional API and have defined the input layer as Input(shape=(128,128,3)), corresponding to the size of the images in my training set.
However, I want to use the trained model on images of variable sizes (which should be ok because the network is fully convolutional). To do this, I need to change my input layer to Input(shape=(None,None,3)). The obvious way to solve the problem would have been to train my model directly with an input shape of (None,None,3) but I use a custom loss function where I need to specify the size of my training images.
I have tried to define a new input layer and assign it to my model like this :
from keras.engine import InputLayer
input_layer = InputLayer(input_shape=(None, None, 3), name="input")
model.layers[0] = input_layer
This actually changes the size of the input layers accordingly but the following layers still expect (128,128,filters) inputs.
Is there a way to change all of the inputs values at once ?
Create a new model, exactly the same, except for new input shape; and tranfer weights:
newModel.set_weights(oldModel.get_weights())
If anything goes wrong, then it might not be fully convolutional (ex: contains a Flatten layer).
I would like to follow the Convolutional Neural Net (CNN) approach here. However, this code in github uses Pytorch, whereas I am using Keras.
I want to reproduce boxes 6,7 and 8 where pre-trained weights from VGG-16 on ImageNet is downloaded and is used to make the CNN converge faster.
In particular, there is a portion (box 8) where weights are downloaded and skipped from VGG-16 that have no counterpart in SegNet (the CNN model). In my work, I am using a CNN model called U-Net instead of Segnet. The U-Net Keras code that I am using can be found here.
I am new to Keras and would appreciate any insight in Keras code on how I can go about downloading and skipping the VGG weights that have no counterpart with my U-Netmodel.
The technique you are addressing is called "Transfer Learning" - when a pre-trained model on a different dataset is used as part of the model as a starting point for better convergence. The intuition behind it is simple: we assume that after training on such a large and rich dataset as ImageNet, the convolution kernels of the model will learn useful representations.
In your specific case, you want to stack VGG16 weights in the bottom and deconvolution blocks on the top. I will go step-by-step, as you pointed out that you are new to Keras. This answer is organized as a step-by-step tutorial and will provide small snippets for you to use in your own code.
Loading weights
In the PyTorch code you linked to above, the model was first defined, and only then the weights are copied. I found this approach abundant, as it contains lots of not necessary code. Here, we will load VGG16 first, and then stack the other layers on the top.
from keras import applications
from keras.layers import Input
# Loading without top layers, since you only need convolution. Note that by not
# specifying the shape of top layers, the input tensor shape is (None, None, 3),
# so you can use them for any size of images.
vgg_model = applications.VGG16(weights='imagenet', include_top=False)
# If you want to specify input tensor shape, e.g. 256x256 with 3 channels:
input_tensor = Input(shape=(256, 256, 3))
vgg_model = applications.VGG16(weights='imagenet',
include_top=False,
input_tensor=input_tensor)
# To see the models' architecture and layer names, run the following
vgg_model.summary()
Defining the U-Net computation graph with VGG16 on the bottom
As pointed in previous paragraph, you do not need to define a model and copy the weights over. Just stack other layers on top of vgg_model:
# Import the layers to be used in U-Net
from keras.layers import ...
# From the U-Net code you provided
def make_conv_block(nb_filters, input_tensor, block):
...
# Creating dictionary that maps layer names to the layers
layers = dict([(layer.name, layer) for layer in vgg_model.layers])
# Getting output tensor of the last VGG layer that we want to include.
# I don't know much about U-Net, but according to the code you provided,
# you don't need the last pooling layer, right?
vgg_top = layers['block5_conv3'].output
# Now getting bottom layers for multi-scale skip-layers
block1_conv2 = layers['block1_conv2'].output
block2_conv2 = layers['block2_conv2'].output
block3_conv3 = layers['block3_conv3'].output
block4_conv3 = layers['block4_conv3'].output
# Stacking the remaining layers of U-Net on top of it (modified from
# the U-Net code you provided)
up6 = Concatenate()([UpSampling2D(size=(2, 2))(vgg_top), block4_conv3])
conv6 = make_conv_block(256, up6, 6)
up7 = Concatenate()([UpSampling2D(size=(2, 2))(conv6), block3_conv3])
conv7 = make_conv_block(128, up7, 7)
up8 = Concatenate()([UpSampling2D(size=(2, 2))(conv7), block2_conv2])
conv8 = make_conv_block(64, up8, 8)
up9 = Concatenate()([UpSampling2D(size=(2, 2))(conv8), block1_conv2])
conv9 = make_conv_block(32, up9, 9)
conv10 = Conv2D(nb_labels, (1, 1), name='conv_10_1')(conv9)
x = Reshape((nb_rows * nb_cols, nb_labels))(conv10)
x = Activation('softmax')(x)
outputs = Reshape((nb_rows, nb_cols, nb_labels))(x)
I want to emphasize that what we've done in this paragraph is just defining the computation graph for U-Net. This code is written specifically for VGG16, but you can modify it for other architectures as you wish.
Creating a model
After the previous step, we've got a computational graph (I assume that you use Tensorflow backend for Keras. If you're using Theano, I recommend you to switch to Tensorflow since this framework has achieved a state of maturity now). Now, we need to do the following things:
Create a model on top of this computation graph
Freeze the bottom layers, since you don't want to wreck your pre-trained weights
# Creating new model. Please note that this is NOT a Sequential() model
# as in commonly found tutorials on the internet.
from keras.models import Model
custom_model = Model(inputs=vgg_model.input, outputs=outputs)
# Make sure that the pre-trained bottom layers are not trainable.
# Here, I freeze all the layers of VGG16 (layers 0-18, including the
# pooling ones.
for layer in custom_model.layers[:19]:
layer.trainable = False
# Do not forget to compile it before training
custom_model.compile(loss='your_loss',
optimizer='your_optimizer',
metrics=['your_metrics'])
"I got confused"
Assuming that you're new to Keras and to Deep Learning in general (as you admitted in your question), I recommend the following articles to read to further understand the process of Fine Tuning and Transfer Learning on Keras:
How CNNs see the world - a great short article that will give you intuitive understanding of the dirty magic behind Transfer Learning.
Building powerful image classification models using very little data - This one will give you more insight on how to adjust learning rates and "release" the frozen layers.
When you're learning a framework, documentation is your best friend. Fortunately, Keras has an incredible documentation.
Q&A
The deconvolution blocks we put on top of VGG are from the UNET achitecture (i.e. up6 to conv10)? Please confirm.
Yes, it's the same as here, just with different names of the skip-connection layers (e.g. block1_conv2 instead of conv1)
We leave out the conv layers (i.e., conv1 to conv5). Can you please share with me as to why this is so?
We don't leave or throw any layers from the VGG network. The VGG16 network architecture and the bottom architecture of U-Net (up to conv5) is very similar. In fact, they are made of 5 blocks of the following format:
+-----------------+-------------------+
| VGG conv blocks | U-Net conv blocks |
+-----------------+-------------------+
| blockX_conv1 | convN |
| ... | poolN |
| blockX_convN | |
| blockX_pool | |
+-----------------+-------------------+
Here is a better visualization. So, the only difference between VGG16 and bottom part of U-Net is that each block of VGG16 contains multiple convolution layers instead of one. That's why, the alternative of connecting conv3 to conv6 is connecting block3_conv3 to conv6. The U-Net architecture remains the same, just with more convolution layers on the bottom.
Is there anyway to incorporate the Max pooling in the conv layers (in your opinion what are we doing here by leaving them out, and would you say it is insignificant?)
We don't leave them out. The only pooling layer that I threw away is block5_pool (which is the last layer in bottom part of VGG16) - because in the original U-Net (refer to the code) it seems like the last convolution block in the bottom part is not followed by a pooling layer (we have conv5 but don't have pool5). I kept all the layers of VGG16.
We see Maxpooling being used on the convolution blocks. Would we also just simply drop these pooling layers (as we are doing here with Unet) if we wanted to combine Segnet with VGG?
As I explained in the question above, we are not dropping any pooling layers.
However, you would need to stack a different type of pooling layers instead of the simple MaxPooling2D that is used in the default VGG16, because SegNet preserves max-indexes. This can be achieved with tf.nn.max_pool_with_argmax and using the trick of replacing middle layers of Keras model (I won't cover the detailed information in this answer to keep it clean). The replacement is harmless and doesn't require re-training because pooling layers don't contain any trained weights.
The U-NET from here is different from what I am using, can you tell what is the impact of such a difference between the two?
It is a more shallow U-Net. The one in your original question has 5 convolution blocks on the bottom (conv1 - conv5), while the later only has 3. Choose how many blocks you need depending on the data (e.g. for simple data as cells you might want to use only 2-3 blocks, while gray matter or tissue segmentation might require 5 blocks for better quality. See this link to have an insight of what convolution kernels "see".
Also, what do you think about the VGGSegnet from here. Does it use the trick of the middle layers you mentioned in Q&A? And is it the equivalent of the Pytorch code I initially posted?
Interesting. It is an incorrect implementation, and is not equivalent to the Pytorch code you posted. I have opened an issue in that repository.
Final question....is it always a rule in Transfer Learning to put the pretrained model (i.e., the model w/ pretrained weights) at the bottom?
Generally it is. Think of the convolution kernels as "features": the first layer detects small edges, colors. The following layers combines those edges and colors into more complicated detections, like "yellow lines" or "blue circle". Then the upper convolution layers detects more abstract shapes as "eyes", "nose", etc. based on detections of lower layers. So replacing the bottom layers (while the upper layers depends on the bottom representation) is illogic.
A solution sketch for this would look like the following:
Initialize VGG-16 and load the ImageNet weights by using the appropriate weights='imagenet' flag:
vgg_16 = keras.applications.vgg16.VGG16(weights='imagenet')
Initialize your model:
model = Model() # or Sequential()
... # Define and compile your model
For each layer that you want to copy over:
i_vgg = ... # Index of the layer you want to copy
i_mod = ... # Index of the corresponding layer in your model
weights = vgg_16.layers[i_vgg].get_weights()
model.layers[i_mod].set_weights(weights)
If you don't want to spend time to find out the index of each layer, you can assign a name to the relevant layers using the name='some_name' parameter in the layer constructor and then access the weights as follows:
layer_dict = dict([(layer.name, layer) for layer in model.layers])
weights = layer_dict['some_name'].get_weights()
layer_dict['some_name'].set_weights(weights)
Sources:
Loading VGG
Getting weights from layer (FChollet is the creator of Keras)
Getting and setting weights
Cheers