Utsav Patel
Utsav Patel

Reputation: 61

Keras Custom Layer: ValueError: An operation has `None` for gradient

I am trying to implement Graph Convolution Layer using Keras custom layer that is mentioned in the following paper: GCNN.

When I am trying to train my model, It gives me the following error:

Traceback (most recent call last):
File "main.py", line 35, in <module>
model.fit(train_images, train_labels, validation_data=(test_images, test_labels), epochs=50, batch_size=32)
File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 1010, in fit
self._make_train_function()
File "/usr/local/lib/python2.7/dist-packages/keras/engine/training.py", line 509, in _make_train_function
loss=self.total_loss)
File "/usr/local/lib/python2.7/dist-packages/keras/legacy/interfaces.py", line 91, in wrapper
return func(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/keras/optimizers.py", line 256, in get_updates
grads = self.get_gradients(loss, params)
File "/usr/local/lib/python2.7/dist-packages/keras/optimizers.py", line 91, in get_gradients
raise ValueError('An operation has `None` for gradient. '
ValueError: An operation has `None` for gradient. Please make sure that all of your ops have a gradient defined (i.e. are differentiable). Common ops without gradient: K.argmax, K.round, K.eval.

I don't know how to get rid of this problem.

Can someone explain me briefly what should I do?

I have gone through Keras official documentation about writing custom layer but it didn't specify about it. Link

Following is the code for my custom layer.

class GraphConvolutionalLayer(Layer):

def __init__(self, A, num_input_features, num_output_features, **kwargs):
    self.A = A
    self.num_input_features = num_input_features
    self.num_output_features = num_output_features

    self.num_vertices = A.get_shape().as_list()[0]
    self.input_spec = (self.num_vertices, num_input_features)

    super(GraphConvolutionalLayer, self).__init__(**kwargs)

def build(self, input_shape):
    self.k0 = self.add_weight(name='k0', 
                                  shape=(self.num_output_features, self.num_input_features),
                                  initializer='uniform',
                                  trainable=True)
    self.k1 = self.add_weight(name='k1', 
                          shape=(self.num_output_features, self.num_input_features),
                          initializer='uniform',
                          trainable=True)

    self.H = tf.einsum('ab,cd->abcd', tf.convert_to_tensor(self.k0, dtype=tf.float32), tf.eye(self.num_vertices)) 
    self.built = True

def call(self, Vin):

    Vin2 = tf.reshape(tf.transpose(Vin, [0, 2, 1]), [Vin.get_shape().as_list()[1] * Vin.get_shape().as_list()[2], -1])

    H_tmp = tf.reshape(tf.transpose(self.H, [0, 2, 1, 3]), [ self.num_output_features, self.num_vertices, self.num_vertices * self.num_input_features])

    Vout = tf.transpose(K.dot(H_tmp, Vin2), [2, 1, 0])

    return Vout

def compute_output_shape(self, input_shape):
    return (self.num_vertices, self.num_output_features)

Following is the code for the main file.

main_input = Input(shape=train_images[0].shape)
Vout1 = GraphConvolutionalLayer(A, 1, 4)(main_input)
Vout2 = GraphConvolutionalLayer(A, 4, 8)(Vout1)
Vout3 = Flatten()(Vout2)
Vout4 = Dense(10, activation='sigmoid')(Vout3)
print(train_images.shape, train_labels.shape)

model = Model(inputs=main_input, outputs=Vout4)
print(model.summary())
model.compile(optimizer='rmsprop', loss='binary_crossentropy')
model.fit(train_images, train_labels, validation_data=(test_images, test_labels), epochs=50, batch_size=32)

Upvotes: 5

Views: 3979

Answers (2)

Utsav Patel
Utsav Patel

Reputation: 61

Here, I take uniform as an initializer. When I changed it, I didn't get any error. I don't know why this happened but I could be able to solve my error just changing that line.

Upvotes: 1

Sharky
Sharky

Reputation: 4543

As error states, some of your function is non-differentiable. It' not easy to say why exactly it happens. For example, take a look

List of Differentiable Ops in Tensorflow

How to make sure your computation graph is differentiable

Edit: Consider example, where I use standard cifar10 data.

class GraphConvolutionalLayer(layers.Layer):
    def __init__(self, A, num_input_features, num_output_features, **kwargs):
        #self.A = A
        self.num_input_features = num_input_features
        self.num_output_features = num_output_features

        self.num_vertices = A
        self.input_spec = (self.num_vertices, num_input_features)

        super(GraphConvolutionalLayer, self).__init__(**kwargs)

    def build(self, input_shape):
        self.k0 = self.add_weight(name='k0',
                                  shape=(self.num_output_features, self.num_input_features),
                                  initializer='uniform',
                                  trainable=True)

        self.H = tf.einsum('ab,cd->abcd', tf.convert_to_tensor(self.k0, dtype=tf.float32), tf.eye(self.num_vertices))
        self.H = tf.reshape(self.H, [32*32, 3])
        self.built = True

    def call(self, Vin):

        Vin2 = tf.reshape(Vin, [Vin.get_shape().as_list()[1] * Vin.get_shape().as_list()[1],Vin.get_shape().as_list()[-1]])
        Vin2 = tf.transpose(Vin2)
        Vout = tf.matmul(self.H, Vin2)
        return Vout

def input_fn():
    train, test = tf.keras.datasets.cifar10.load_data()
    dataset = tf.data.Dataset.from_tensor_slices((train[0], train[1]))
    dataset = dataset.batch(1)
    return dataset

main_input = layers.Input(shape=[32, 32, 3])
Vout1 = GraphConvolutionalLayer(32, 3, 1)(main_input)    
Vout3 = layers.Flatten()(Vout1)
Vout4 = layers.Dense(10, activation='sigmoid')(Vout3)
model = Model(inputs=main_input, outputs=Vout4)
model.compile(optimizer='rmsprop', loss='binary_crossentropy')
model.fit(input_fn(), epochs=50, steps_per_epoch=10)

In this case gradients are computed. So the problem clearly is not in how you construct GraphConvolutionalLayer but in some internal operation, which depends on data. You need to check every op one by one with your data shapes.

P.S. You can try substituting einsum with matmul, cause the former is simply a syntactic wrap for the latter.

Upvotes: 0

Related Questions