Пользовательская активация с пользовательским градиентом не работает - PullRequest
0 голосов
/ 03 октября 2019

Я пытаюсь написать код для простого обучения нейронной сети. Цель состоит в том, чтобы определить пользовательскую функцию активации, и вместо того, чтобы Keras автоматически брал ее производную для обратного распространения, я заставляю Keras использовать мою собственную функцию градиента для своей пользовательской активации:

import numpy as np
import tensorflow as tf
import math
import keras
from keras.models import Model, Sequential
from keras.layers import Input, Dense, Activation
from keras import regularizers
from keras import backend as K
from keras.backend import tf
from keras import initializers
from keras.layers import Lambda

@tf.custom_gradient
def custom_activation(x):

    def grad(dy):
        return dy * 0

    result=(K.sigmoid(x) *2-1 )
    return result, grad 

x_train=np.array([[1,2],[3,4],[3,4]]);

inputs = Input(shape=(2,))
output_1 = Dense(20, kernel_initializer='glorot_normal')(inputs)
layer = Lambda(lambda x: custom_activation)(output_1)
output_2 = Dense(2, activation='linear',kernel_initializer='glorot_normal')(layer)
model2 = Model(inputs=inputs, outputs=output_2)

model2.compile(optimizer='adam',loss='mean_squared_error')
model2.fit(x_train,x_train,epochs=20,validation_split=0.1,shuffle=False)

Поскольку градиент имеетбыло определено равным нулю, я ожидаю, что потери не изменятся после всех эпох. Вот обратный след ошибки, которую я получаю:

Using TensorFlow backend.
WARNING:tensorflow:From C:\ProgramData\Anaconda3\lib\site-packages\tensorflow\python\framework\op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
Traceback (most recent call last):
  File "C:/p/CE/mytest.py", line 43, in <module>
    layer = Lambda(lambda x: custom_activation)(output_1)
  File "C:\ProgramData\Anaconda3\lib\site-packages\keras\engine\base_layer.py", line 474, in __call__
    output_shape = self.compute_output_shape(input_shape)
  File "C:\ProgramData\Anaconda3\lib\site-packages\keras\layers\core.py", line 656, in compute_output_shape
    return K.int_shape(x)
  File "C:\ProgramData\Anaconda3\lib\site-packages\keras\backend\tensorflow_backend.py", line 593, in int_shape
    return tuple(x.get_shape().as_list())
AttributeError: 'function' object has no attribute 'get_shape'

Обновление: Я использовал ответ Маноджа Мохана, и теперь код работает. Я ожидаю увидеть неизменные потери среди эпох, поскольку градиент определен равным нулю. Но это меняется. Почему? Я что-то упустил?

Пример:

Epoch 1/20
2019-10-03 10:31:34.193232: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2

2/2 [==============================] - 0s 68ms/step - loss: 8.3184 - val_loss: 13.7232
Epoch 2/20

2/2 [==============================] - 0s 496us/step - loss: 8.2783 - val_loss: 13.6368

Ответы [ 2 ]

1 голос
/ 03 октября 2019

Заменить

layer = Lambda(lambda x: custom_activation)(output_1)

на

layer = Lambda(custom_activation)(output_1)

Я ожидаю увидеть неизменные потери среди эпох, поскольку градиент определен равным нулю. Но это меняется. Почему?

Градиент был обновлен до нуля в промежуточном слое. Таким образом, градиенты не будут течь назад оттуда. Но от выхода до промежуточного слоя, градиент будет течь, и веса будут обновлены. Эта измененная архитектура будет выводить постоянные потери в разные эпохи.

inputs = Input(shape=(2,))
output_1 = Dense(20, kernel_initializer='glorot_normal')(inputs)
output_2 = Dense(2, activation='linear',kernel_initializer='glorot_normal')(output_1)
layer = Lambda(custom_activation)(output_2)  #should be last layer
model2 = Model(inputs=inputs, outputs=layer) 
0 голосов
/ 03 октября 2019

Вот еще один способ сделать это, получив идею от здесь :

import numpy as np
import random
import tensorflow as tf
import math
import keras
from keras.models import Model, Sequential
from keras.layers import Input, Dense, Activation
from keras import regularizers
from keras import backend as K
from keras.backend import tf
from keras import initializers


@tf.custom_gradient
def custom_activation(x):
    result = (K.sigmoid(x) * 2 - 1)
    def grad(dy):
        grad=0;
        return dy * grad

    return result, grad



class CustomLayer(tf.keras.layers.Layer):
    def __init__(self):
        super(CustomLayer, self).__init__()

    def call(self, x):
        return custom_activation(x)


x_train=np.array([[1,2],[3,4],[3,4]]);  


inputs = tf.keras.layers.Input(shape=(2,))
output_1 = tf.keras.layers.Dense(20, kernel_initializer='glorot_normal')(inputs)
layer = CustomLayer()(output_1)
output_2 = tf.keras.layers.Dense(2, activation='linear',kernel_initializer='glorot_normal')(layer)
model2 = tf.keras.models.Model(inputs=inputs, outputs=output_2)

model2.compile(optimizer='adam',loss='mean_squared_error')
model2.fit(x_train,x_train,epochs=10,validation_split=0.1,shuffle=False)
...