14

I have a sequential model that I built in Keras. I try to figure out how to change the shape of the input. In the following example

model = Sequential()
model.add(Dense(32, input_shape=(500,)))
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='rmsprop',
      loss='categorical_crossentropy',
      metrics=['accuracy'])

let's say that I want to build a new model with different input shape, conceptual this should looks like this:

model1 = model
model1.layers[0] = Dense(32, input_shape=(250,))

is there a way to modify the model input shape?

itamar kanter
  • 1,170
  • 3
  • 10
  • 25

3 Answers3

14

Somewhat related, so hopefully someone will find this useful: If you have an existing model where the input is a placeholder that looks like (None, None, None, 3) for example, you can load the model, replace the first layer with a concretely shaped input. Transformation of this kind is very useful when for example you want to use your model in iOS CoreML (In my case the input of the model was a MLMultiArray instead of CVPixelBuffer, and the model compilation failed)

from keras.models import load_model
from keras import backend as K
from keras.engine import InputLayer
import coremltools

model = load_model('your_model.h5')

# Create a new input layer to replace the (None,None,None,3) input layer :
input_layer = InputLayer(input_shape=(272, 480, 3), name="input_1")

# Save and convert :
model.layers[0] = input_layer
model.save("reshaped_model.h5")    
coreml_model = coremltools.converters.keras.convert('reshaped_model.h5')    
coreml_model.save('MyPredictor.mlmodel')
ohad serfaty
  • 644
  • 6
  • 11
5

Think about what changing the input shape in that situation would mean.

Your first model

model.add(Dense(32, input_shape=(500,)))

Has a dense layer that really is a 500x32 matrix.

If you changed your input to 250 elements, your layers's matrix and input dimension would mismatch.

If, however, what you were trying to achieve was to reuse your last layer's trained parameters from your first 500 element input model, you could get those weights by get_weights. Then you could rebuild a new model and set values at the new model with set_weights.

model1 = Sequential()
model1.add(Dense(32, input_shape=(250,)))
model1.add(Dense(10, activation='softmax'))
model1.layers[1].set_weights(model1.layers[1].get_weights())

Keep in mind that model1 first layer (aka model1.layers[0]) would still be untrained

maz
  • 1,980
  • 14
  • 17
  • 1
    Is there a way to keep the entire model and just changed the input layer? actually I have a complex model that I want to train both with a full set of inputs and with a partial sets of input and to compare the results. I look for a way to modify the input dimension in modlel1 while keep the rest of the model identical (except for for the input dimension and for the firist layer of course) – itamar kanter Feb 12 '17 at 14:11
  • I think not. As I said, the dimensions would mismatch. If you want to find out how well your model does with partial inputs, I would suggest training a different model solely with your partial inputs. What you could do though if you really want to use the same model for both situations with different size inputs would be to use a RNN instead of a MLP. – maz Feb 12 '17 at 16:14
4

Here is another solution without defining each layer of the model from scratch. The key for me was to use "_layers" instead of "layers". The latter only seems to return a copy.

import keras
import numpy as np

def get_model():
    old_input_shape = (20, 20, 3)
    model = keras.models.Sequential()
    model.add(keras.layers.Conv2D(9, (3, 3), padding="same", input_shape=old_input_shape))
    model.add(keras.layers.MaxPooling2D((2, 2)))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(1, activation="sigmoid"))
    model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0001), metrics=['acc'], )
    model.summary()
    return model

def change_model(model, new_input_shape=(None, 40, 40, 3)):
    # replace input shape of first layer
    model._layers[1].batch_input_shape = new_input_shape

    # feel free to modify additional parameters of other layers, for example...
    model._layers[2].pool_size = (8, 8)
    model._layers[2].strides = (8, 8)

    # rebuild model architecture by exporting and importing via json
    new_model = keras.models.model_from_json(model.to_json())
    new_model.summary()

    # copy weights from old model to new one
    for layer in new_model.layers:
        try:
            layer.set_weights(model.get_layer(name=layer.name).get_weights())
        except:
            print("Could not transfer weights for layer {}".format(layer.name))

    # test new model on a random input image
    X = np.random.rand(10, 40, 40, 3)
    y_pred = new_model.predict(X)
    print(y_pred)

    return new_model

if __name__ == '__main__':
    model = get_model()
    new_model = change_model(model)
gebbissimo
  • 2,137
  • 2
  • 25
  • 35
  • 2
    I'm using TensorFlow 2.8.0 and in this version `_layers` and `batch_input_shape` are not available. Just a small change was required to get it working. I wanted to update the 0th layer of the model i.e. the `Input` node (layer) of my functional model. Do: `model.layers[0]._batch_input_shape = new_input_shape` And rest of the code remains the same. – Vaibhav Singh Mar 31 '22 at 09:47