Closed benjaminklein closed 9 years ago
We added that some time ago. See the Merge layer: http://keras.io/layers/core/#merge
Note that in concat mode, on GPU, you will have to use the latest version of Theano (get it from the repo, not pip), due a bug with past Theano versions.
Not sure that this is what I wanted: Let say that my input contains two images. I would like one to be passed through CNN1 and the second to be passed through CNN2. Then I can merge them using the merge layer. But how can I use the library in order to handle the two different inputs? Basically I would like to have more than one input or to be able to split the input (using a split layer) to a few layers.. so each sub-input could be passed to a different network...
That's what the Merge layer allows you to do. You can have two different networks, with different inputs (like 2 images) and you can merge their output into a single tensor.
So let say that I want to train such a model. I have two networks: CNN1 and CNN2.
My training data contain pairs of images: (i1, i2). I want that i1 will be passed through CNN1 and i2 will be passed through CNN2.
How should I design the network and how should I perform the training such that CNN1 will be applied only on i1 and CNN2 will be applied only on i2?
Thank you.
The code snippet example in the doc page I linked provides all the info you need.
You will train your model with list of inputs:
model.fit([X_CNN1, X_CNN2], y)
Which will feed at time t X_CNN1[t]
to CNN1 and X_CNN2[t]
to CNN2.
Thank you!
I had a similar requirement. What if i need to pass two inputs into the same node. But these two inputs aren't of the same dimension. Say 1 is an image, and the other is some hidden representation of another image in a lower dimension. I cannot concatenate them since they are of different dimensions. Is there any support to access two totally different inputs within a layer. ( i am modifying keras source to add a new node type, LSTM2. But I only have access to one input, i.e. x)
@karishmamalkan I need this feature too. Did you get any update on this?
@jwgu Hi, There was no method to pass multiple inputs to the RNN except to concatenate,dot etc as described by the merge layer. But i found a work around. If you want two inputs, both of which need to be multiplied by trainable weights, then you can use a Graph layer as follows:
Supposed you have two inputs x1 and x2 at each step of the RNN/LSTM. Your RNN function looks like: h(t) = (Wh * h(t-1) + W1 * x1 + W2 *x2),
then you can have a
and then you can pass this sequence into the RNN layer.
Supposedly this Merge Layer should support merging outputs from multiple (>2) sources, right? I tried concatenating 3 layers, however it failed... Here is the code for merging:
from keras.layers import Merge
left_branch = Sequential()
left_branch.add(Dense(32, input_dim=784))
middle_branch = Sequential()
middle_branch.add(Dense(32, input_dim=784))
right_branch = Sequential()
right_branch.add(Dense(32, input_dim=784))
merged = Merge([left_branch, middle, right_branch], mode='concat')
final_model = Sequential()
final_model.add(merged)
final_model.add(Dense(10, activation='softmax'))
Here is the code for model fit (of course model compiled before):
final_model.fit([X_left, X_middle, X_right], y)
But I get the following error:
Traceback (most recent call last):
File "MyCNN_multiple_Img.py", line 170, in <module>
callbacks=[TensorBoard(log_dir=logpath,histogram_freq=1,write_graph=False)]
File "/usr/bin/lib/python2.7/site-packages/keras/models.py", line 429, in fit
sample_weight=sample_weight)
File "/usr/bin/lib/python2.7/site-packages/keras/engine/training.py", line 1036, in fit
batch_size=batch_size)
File "/usr/bin/lib/python2.7/site-packages/keras/engine/training.py", line 963, in _standardize_user_data
exception_prefix='model input')
File "/husr/bin/lib/python2.7/site-packages/keras/engine/training.py", line 51, in standardize_input_data
'...')
Exception: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 2 arrays but instead got the following list of 3 arrays: [array([[[[ 0.49803922, 0.42352942, 0.36862746, ..., 0.68627453,
0.83529413, 0.85490197],
[ 0.87843138, 0.98431373, 1. , ..., 0.4509804 ,
0.42352942, 0.44...
Is it possible to merge more than two outputs? If so, could some one tell me how should I change my code? Thank you!
It is possible to merge three tensors, basically just the way you describe but without the syntax errors. You are not sharing the code that you are actually using (it wouldn't even run!), but the code you are actually using presumably involves only two inputs. The following code runs fine:
from keras.models import Sequential
from keras.layers import Dense, Merge
left_branch = Sequential()
left_branch.add(Dense(32, input_dim=784))
middle_branch = Sequential()
middle_branch.add(Dense(32, input_dim=784))
right_branch = Sequential()
right_branch.add(Dense(32, input_dim=784))
merged = Merge([left_branch, middle_branch, right_branch], mode='concat')
final_model = Sequential()
final_model.add(merged)
final_model.add(Dense(10, activation='softmax'))
print final_model.inputs
Thank you very much @fchollet ! Sorry didn't put my full code on since it's too long...
But now I think I've figured out what the problem was: Since in my case, both left_branch and middle branch consist of a stack of convolution+padding+max-pooling layers (copied VGG16 architecture), there is a long code for constructing such a sequential model. So, for laziness...I constructed the sequential model for only once, specifically, I did the following:
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D, ZeroPadding2D
model = Sequential()
model.add(ZeroPadding2D((1, 1), input_shape=(3, 224, 224)))
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(128, 3, 3, activation='relu', name='conv2_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(128, 3, 3, activation='relu', name='conv2_2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_2'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_2'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_2'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))
Then I say:
left_branch=model
left_branch.add(Flatten(input_shape=model.output_shape[1:]))
left_branch.add(Dense(output_dim=4096, activation='relu'))
middle_branch=left_branch ## Here is for laziness
right_branch = Sequential()
right_branch.add(Dense(output_dim=400, input_dim=200,activation='linear'))
So far, I thought I've constructed the two branches, with left and middle branching having the same structure. So I merge them
from keras.layers import Merge
merged = Merge([left_branch, middle_branch, right_branch], mode='concat')
final_model = Sequential()
final_model.add(merged)
final_model.add(Dense(output_dim=1, activation='sigmoid'))
Then I compile and train the model:
final_model.compile(loss='mean_squared_error', optimizer='adadelta', metrics=['accuracy'])
from keras.callbacks import TensorBoard
logpath=mypath+'/mylog/log1'
final_model.fit([img_left, img_middle, text_right], y,
nb_epoch=60, batch_size=20, shuffle=True,
callbacks=[TensorBoard(log_dir=logpath,histogram_freq=1,write_graph=False)] )
And the following error raised:
Traceback (most recent call last):
File "MyCNN_multiple_Img.py", line 170, in <module>
callbacks=[TensorBoard(log_dir=logpath,histogram_freq=1,write_graph=False)]
File "/usr/bin/lib/python2.7/site-packages/keras/models.py", line 429, in fit
sample_weight=sample_weight)
File "/usr/bin/lib/python2.7/site-packages/keras/engine/training.py", line 1036, in fit
batch_size=batch_size)
File "/usr/bin/lib/python2.7/site-packages/keras/engine/training.py", line 963, in _standardize_user_data
exception_prefix='model input')
File "/husr/bin/lib/python2.7/site-packages/keras/engine/training.py", line 51, in standardize_input_data
'...')
Exception: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 2 arrays but instead got the following list of 3 arrays: [array([[[[ 0.49803922, 0.42352942, 0.36862746, ..., 0.68627453,
0.83529413, 0.85490197],
[ 0.87843138, 0.98431373, 1. , ..., 0.4509804 ,
0.42352942, 0.44...
I then printed the input shape of final_model and got: [(None, 3, 224, 224), (None, 200)] while I was expecting: [(None, 3, 224, 224), (None, 3, 224, 224), (None, 200)]
So I assume a safe way of merging multiple sources is to, for each source, go from the very beginning towards the end to construct a sequential model, then merge them (after I did so, the code worked)? If we have some branches that have the same configuration/structure, is it possible to go through the construction only once, for all those branches (the best I can come up with is to make a function of constructing the sequential model...)? Thanks!
@fchollet regarding your answer The code snippet example in the doc page I linked provides all the info you need.
You will train your model with list of inputs:
model.fit([X_CNN1, X_CNN2], y) Which will feed at time t X_CNN1[t] to CNN1 and X_CNN2[t] to CNN2.
Does model.fit accepts multiple inputs for validation data? For example, Is the following line legitimate? model.fit([X_CNN1, X_CNN2], y, .... , validation_data=([X_CNN1_val, X_CNN2_val], y_val))
@karishmamalkan Thanks for your tip on how to provide two different inputs for an RNN. However in this way, later on, a new set of weights are applied to the merged input by the recurrent class, is that right? Is it fine to have these extra set of parameters in the network? Sorry if my question sounds naive, as I am new to Keras and in general, to deep learning.
Hi sorry for hacking into this thread. I currently need to implement a layer that needs two inputs but has its own trainable parameters. For example, a linear-chain CRF layer takes tokens and tags as inputs, but it has its own trainable parameters (emission matrix and transition matrix). In this case, should I write a layer that is inherited from Merge?
Hi benjaminklein, I need to ask you if you found any answer for your question because I need to do the same thing but I don't know how.
@fchollet @benjaminklein How does the problem of 2 CNNs is solved in Keras 2? @fchollet : The link doesn't contain any code snippet anymore. @ersinyar @fatemaaa Have you people figured it out already?
@fchollet: Can we merge layers with inputs of different sizes ? For example, I would like to merge two LSTM layers, first layer with input sequence shape (30,2) and the other layer with input sequence shape (15,1). I would then like to merge these two layers and pass the output to a second layer.
Please let me know !
I am trying to add three models on passenger data, below is code. I am getting the following error.
OUTPUT [<tf.Tensor 'dense_249_input:0' shape=(?, 15) dtype=float32>, <tf.Tensor 'conv1d_50_input:0' shape=(?, 15, 1) dtype=float32>, <tf.Tensor 'lstm_27_input:0' shape=(?, 15, 1) dtype=float32>] (80, 15) (80, 15, 1) (80, 15, 1) ValueError: Error when checking target: expected dense_258 to have shape (None, 10) but got array with shape (80, 1)
` import pandas as pd import math import numpy as np from pandas import read_csv from pandas import datetime from pandas import Series from pandas import DataFrame from keras.models import Sequential from keras.layers import Dense, Merge from keras.layers import LSTM, Dropout, SimpleRNN from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error from keras.layers import Convolution1D, Conv2D, Dense, Flatten from keras import optimizers from keras import losses from keras.layers.pooling import MaxPooling1D from keras.layers.convolutional import ZeroPadding1D
def create_dataset(dataset, look_back): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), 0] dataX.append(a) dataY.append(dataset[i + look_back, 0]) return numpy.array(dataX), numpy.array(dataY)
numpy.random.seed(7)
dataframe = pd.read_csv('/home/shivampanchal/PycharmProjects/WeatherPrediction/data/pass.csv')
dataframe['yy_mnh']=pd.to_datetime(dataframe['Month']) dataframe=dataframe.drop('Month',axis=1) dataframe=dataframe.sort_values(by=['yy_mnh']) dataframe=dataframe.drop('yy_mnh',axis=1) dataset = dataframe.values dataset = dataset.astype('float32')
scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset)
train_size = int(len(dataset) * 0.67) test_size = len(dataset) - train_size train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
look_back = 15
trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back)
trainX_NN = np.reshape(trainX, (trainX.shape[0], trainX.shape[1])) testX_NN = np.reshape(testX, (testX.shape[0], testX.shape[1]))
trainX_CNN = np.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1)) testX_CNN = np.reshape(testX, (testX.shape[0], testX.shape[1], 1))
trainX_LSTM = numpy.reshape(trainX, (trainX.shape[0], look_back , 1)) testX_LSTM = numpy.reshape(testX, (testX.shape[0], look_back, 1))
batch_size = 5 ###################### model1 = Sequential() model1.add(Dense(10, input_dim=trainX_NN.shape[1], activation='relu')) model1.add(Dense(8, activation='relu')) model1.add(Dense(1)) ###################### model2 = Sequential()
model2.add(Convolution1D(40, 2, input_shape=(trainX_CNN.shape[1], 1), activation='relu'))
model2.add(Convolution1D(20, 3, activation='relu')) model2.add(Flatten()) model2.add(Dense(10)) model2.add(Dense(5)) model2.add(Dense(1)) ###################### model3 = Sequential() model3.add(LSTM(32, input_shape=(look_back, 1),activation='relu')) model3.add(Dense(16)) model3.add(Dense(8)) model3.add(Dense(1))
merged = Merge([model1, model2, model3], mode='concat')
final_model = Sequential() final_model.add(merged) final_model.add(Dense(10, activation='relu'))
print final_model.inputs final_model.compile(optimizer='adam', loss='mean_squared_error')
print(trainX_NN.shape) print(trainX_CNN.shape) print(trainX_LSTM.shape)
final_model.fit([trainX_NN, trainX_CNN, trainX_LSTM], trainY, epochs=10, batch_size=batch_size, verbose=2) `
This works well.
validation_data=({'daily_input': X_test,
'constant_input':X_test_constant},
{'classify_output': Y_test})
training_data = ({'daily_input': X_train,
'constant_input':X_train_constant},
{'classify_output': Y_train})
model.fit(
training_data[0],
training_data[1],
epochs=900,
batch_size=batch_size,
verbose = 1,
validation_data=validation_data,
class_weight = class_weight)
Perhaps adding a "data" parameter as an option to define "X" and "y", could make this even more clear and consistent and thus we can use the "training_data " dict as the sole input.
I have two images, first image and its label is good, second images and its label is bad. I want to pass both images at a time to deep learning model for training. While testing I will have two images (unlabelled) and I want to detect which one is good and which one is bad. Could you please tell how to do it?
I built an architecture using merge and tried to fit the model:
# fine-tune the model history = model.fit_generator( [train_generator1, train_generator2], steps_per_epoch=nb_train_samples//32, epochs=nb_epoch, validation_data=[validation_generator1,validation_generator2], validation_steps = nb_validation_samples//32, callbacks=[reduce_lr])
but got the following error
`--------------------------------------------------------------------------- AttributeError Traceback (most recent call last)
Hi, i have similar problem with this issue. i want to merge a thermal and visible image features using deep learning. and i tried to train model u have explained. model.fit([X_CNN1, X_CNN2], y) but it shows me the following error:
****Traceback*** IndexError Traceback (most recent call last)
hi, I have a doubt. In my code,
model.fit(x=[X_train_1,X_train_2],y= Y]
the error is x=ndim is not applicable on a list, how do I rectify this??? The model is compiling fine. but fit() is giving an error.
@fchollet Hi , I have met a problem. I want to merge two output of different networks with different weights. And the weights are computed by another network. What should I do to finish this? Thank you for your help.
If you are using customize callback metric (in my case, F1-macro for multiclass classification), you may encounter the problem like this
class Metrics(Callback):
def on_train_begin(self, logs={}):
self.f1s = []
self.recalls = []
self.precisions = []
def on_epoch_end(self, epoch, logs={}):
x_input = self.validation_data[0]
predict = np.asarray(self.model.predict(x_input))
targ = self.validation_data[1]
predict = np.argmax(predict, axis=1)
targ = np.argmax(targ, axis=1)
_f1 = f1_score(targ, predict, average='macro')
_recall = recall_score(targ, predict, average='macro')
_precision = precision_score(targ, predict, average='macro')
self.f1s.append(_f1)
self.recalls.append(_recall)
self.precisions.append(_precision)
logger.info(" — val_f1: %.4f — val_precision: %.4f — val_recall %f" %(_f1, _precision, _recall))
if self.best_f1 > _f1:
self.wait += 1
if self.wait >= self.patience:
logger.info("Epoch %d: early stopping threshold" % epoch)
self.model.stop_training = True
self.model.set_weights(self.best_weights)
else:
self.best_f1 = _f1
self.best_weights = self.model.get_weights()
self.wait = 0
return
class Metrics(Callback):
def on_train_begin(self, logs={}):
self.f1s = []
self.recalls = []
self.precisions = []
def on_epoch_end(self, epoch, logs={}):
x_input = [self.validation_data[0], self.validation_data[1]]
predict = np.asarray(self.model.predict(x_input))
targ = self.validation_data[2]
predict = np.argmax(predict, axis=1)
targ = np.argmax(targ, axis=1)
_f1 = f1_score(targ, predict, average='macro')
_recall = recall_score(targ, predict, average='macro')
_precision = precision_score(targ, predict, average='macro')
self.f1s.append(_f1)
self.recalls.append(_recall)
self.precisions.append(_precision)
logger.info(" — val_f1: %.4f — val_precision: %.4f — val_recall %f" %(_f1, _precision, _recall))
return
you can see the difference at x_input
parts, I expect
self.validation_data[0] = [main_input, auxiliary_input]
but in reality, it will be
self.validation_data[0], self.validation_data[1] = main_input, auxiliary_input
Hope this could help those who have encountered the same problem.
@fchollet how I would go about concatenating a 3 element array input with image inputs? Here is what I have
`rgb_img_input = Input(tensor=rgb_img)
d_img_input = Input(tensor=d_img)
vel_input = Input(tensor=vel)
conv_rgb_1=Conv2D(32,(4, 4), strides=(4, 4), activation='relu',input_shape=rgb_img_input.shape , data_format = "channels_last")(rgb_img_input)
conv_rgb_2=Conv2D(64,(3, 3), strides=(2, 2), activation='relu')(conv_rgb_1)
conv_d_1=Conv2D(32,(4, 4), strides=(4, 4), activation='relu',input_shape=d_img_input.shape , data_format = "channels_last")(d_img_input)
conv_d_2=Conv2D(64,(3, 3), strides=(2, 2), activation='relu')(conv_d_1)
flat_rgb=Flatten()(conv_rgb_2)
flat_d=Flatten()(conv_d_2)
flat_vel=tf.reshape((vel_input),(-1,3))
merge = concatenate([flat_d, flat_rgb,flat_vel])
hidden1 = Dense(256, activation='relu')(merge)
hidden2 = Dense(256, activation='relu')(hidden1)
predictions = Dense(nb_actions, kernel_initializer='zeros', activation='linear')(hidden2)`
Running this gives the error
ValueError: A Concatenate layer requires inputs with matching shapes except for the concat axis. Got inputs shapes: [(None, 10944), (None, 10944), (1, 3)]
Concatenating the images without the velocity array works fine. I tried to reshape it with different way but nothing worked.
...
# fine-tune the model history = model.fit_generator( [train_generator1, train_generator2], steps_per_epoch=nb_train_samples//32, epochs=nb_epoch, validation_data=[validation_generator1,validation_generator2], validation_steps = nb_validation_samples//32, callbacks=[reduce_lr])
but got the following error
`--------------------------------------------------------------------------- AttributeError Traceback ...
@scstu is it compulsory to use fit_generator
method instead of just fit
? I thought the current version already make fit
scalable. I'm also using images DataIterator
built with ImageDataGenerator
class, but getting the following error instead
ValueError: Failed to find data adapter that can handle input: (<class 'list'> containing values of types {"<class 'keras_preprocessing.image.directory_iterator.DirectoryIterator'>"}), <class 'NoneType'>
when I tried model.fit( [train_gen1, train_gen2], ... )
Hi, Any plans to add support for multiple inputs?
For example - Having two images - each will be passed through a different CNN and the final representations of the two will be merged at the end.
Thank you!