Manually change weights of Keras convolutional layer

Viewed 1327

There is a way to change manually the weights for the tf.layers.Conv2d (https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/layers/Conv2D)? Because this class takes in only the input, the number of kernels to use, etc... and the weights are automatically stored and computed by Tensorflow, but I would a way (like in tf.nn.conv2d - https://www.tensorflow.org/api_docs/python/tf/nn/conv2d) to pass directly the weights to the class.

Anyone have a suggestion?

Maybe one could be to load and change manually the value for the variable associate at that layer? I found this solution very bad but it could work.

Thank you.

2 Answers

Let's say you have a basic convolutional neural network like this:

import tensorflow as tf
import numpy as np

model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(filters=16, kernel_size=(3, 3), 
                           strides=(1, 1), activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2)),
    tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), 
                           strides=(1, 1), activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dropout(5e-1),
    tf.keras.layers.Dense(10, activation='softmax')
])

All the convolutional layers will, by default, have the name 'conv2d...' something.

list(map(lambda x: x.name, model.layers))
['conv2d_19',
 'max_pooling2d_19',
 'conv2d_20',
 'max_pooling2d_20',
 'flatten_8',
 'dense_16',
 'dropout_8',
 'dense_17']

Using that, you can iterate through all the convolutional layers.

for layer in filter(lambda x: 'conv2d' in x.name, model.layers):
    print(layer)
<tensorflow.python.keras.layers.convolutional.Conv2D object at 0x00000295BE4EB048>
<tensorflow.python.keras.layers.convolutional.Conv2D object at 0x00000295C1617448>

For all these layers, you can obtain the weights shape, and the bias shape.

for layer in filter(lambda x: 'conv' in x.name, model.layers):
    weights_shape, bias_shape = map(lambda x: x.shape, layer.get_weights())

Then you can use layer.set_weights() with the values you want, since you know the correct shape. Let's say 0.12345. Let's do that with np.full, which fills an array of the specified shape with any value you want.

for layer in filter(lambda x: 'conv2d' in x.name, model.layers):
    weights_shape, bias_shape = map(lambda x: x.shape, layer.get_weights())
    layer.set_weights([np.full(weights_shape, 0.12345),
                       np.full(bias_shape,    0.12345)])

The weights now:

[array([[[[0.12345, 0.12345, 0.12345, ..., 0.12345, 0.12345, 0.12345],
          [0.12345, 0.12345, 0.12345, ..., 0.12345, 0.12345, 0.12345],
          [0.12345, 0.12345, 0.12345, ..., 0.12345, 0.12345, 0.12345],
          ...,
          [0.12345, 0.12345, 0.12345, ..., 0.12345, 0.12345, 0.12345],
          [0.12345, 0.12345, 0.12345, ..., 0.12345, 0.12345, 0.12345],
          [0.12345, 0.12345, 0.12345, ..., 0.12345, 0.12345, 0.12345]]]],
       dtype=float32),
 array([0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345,
        0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345,
        0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345,
        0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345, 0.12345,
        0.12345, 0.12345, 0.12345, 0.12345], dtype=float32)]

Fully copy/pastable example:

import tensorflow as tf
import numpy as np

model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(filters=16, kernel_size=(3, 3), 
                           strides=(1, 1), activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2)),
    tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), 
                           strides=(1, 1), activation='relu'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dropout(5e-1),
    tf.keras.layers.Dense(10, activation='softmax')
])

model.build(input_shape=(None, 28, 28, 1))

for layer in filter(lambda x: 'conv2d' in x.name, model.layers):
    weights_shape, bias_shape = map(lambda x: x.shape, layer.get_weights())
    layer.set_weights([np.full(weights_shape, 0.12345),
                       np.full(bias_shape,    0.12345)])

Thanks Nicholas for the suggestion.

I'm not using Keras for the network modelling, I need in fact to use Tensorflow directly, in particular with tf-slim library.

The solution that you proposed could work for replacing the weights, but the problem to overcome so is that I need also to change how these weights are used to compute the convolution operation. To be more concrete I want to pass to the Conv layer a vector of weights that is representative in some way of the previous weights matrix, so before making the convolution I need to reconstruct a matrix and pass it to the layer.

Any suggestion to accomplish this?

Thank you.

Related