我正在使用Tensorflow / Keras(Windows上的TF版本2.1,Python 3.7)编写一个完全连接的层,但是我发现,如果在重张量之前对其进行重塑,那么Tensorflow似乎并不是即使我将形状重新调整为自己的形状也能够计算梯度。考虑下面的层代码:
import tensorflow as tf
import numpy as np
class FCLayer(tf.keras.layers.Layer):
def __init__(self,output_size,cause_error = False):
super(FCLayer,self).__init__()
self.output_size = output_size
self.cause_error = cause_error
def build(self,input_shape):
self.input_size = input_shape[1]
weights = self.add_weight(shape=(self.input_size,
self.output_size),
initializer='random_normal',
trainable=True)
if self.cause_error:
self.weights2 = tf.reshape( weights,
shape = (self.input_size,
self.output_size))
else:
self.weights2 = weights
def call(self, inputs):
return tf.matmul(inputs, self.weights2)
如果将它与cause_error = True一起使用,则在mnist上训练4个时期(以下包括特定的训练代码)时,我得到以下输出:
Train on 60000 samples, validate on 10000 samples
Epoch 1/4
WARNING:tensorflow:Gradients do not exist for variables ['sequential/dummy_layer/Variable:0'] when minimizing the loss.
WARNING:tensorflow:Gradients do not exist for variables ['sequential/dummy_layer/Variable:0'] when minimizing the loss.
60000/60000 [==============================] - 1s 20us/sample - loss: 2.4131 - accuracy: 0.0722 - val_loss: 2.3963 - val_accuracy: 0.0834
Epoch 2/4
60000/60000 [==============================] - 1s 12us/sample - loss: 2.4122 - accuracy: 0.0722 - val_loss: 2.3953 - val_accuracy: 0.0836
Epoch 3/4
60000/60000 [==============================] - 1s 12us/sample - loss: 2.4112 - accuracy: 0.0724 - val_loss: 2.3944 - val_accuracy: 0.0838
Epoch 4/4
60000/60000 [==============================] - 1s 13us/sample - loss: 2.4102 - accuracy: 0.0725 - val_loss: 2.3933 - val_accuracy: 0.0839
这只是一个警告,但很明显,该模型并没有真正改善,显然需要这些渐变。
如果我将cause_error = False设置为False,我将获得预期的输出(无警告,适度改进):
Train on 60000 samples, validate on 10000 samples
Epoch 1/4
60000/60000 [==============================] - 1s 16us/sample - loss: 2.3671 - accuracy: 0.1527 - val_loss: 2.3445 - val_accuracy: 0.1508
Epoch 2/4
60000/60000 [==============================] - 1s 12us/sample - loss: 2.3293 - accuracy: 0.1596 - val_loss: 2.3072 - val_accuracy: 0.1610
Epoch 3/4
60000/60000 [==============================] - 1s 13us/sample - loss: 2.2939 - accuracy: 0.1683 - val_loss: 2.2722 - val_accuracy: 0.1720
Epoch 4/4
60000/60000 [==============================] - 1s 13us/sample - loss: 2.2609 - accuracy: 0.1784 - val_loss: 2.2397 - val_accuracy: 0.1847
我怀疑我需要以某种方式告诉Tensorflow跟踪渐变,但不确定如何进行。当我使用tf.matmul时,它似乎是自动执行的,而且我很确定这种代码曾经在TF 1中可用。
我过去执行的特定代码为(改编自mnist教程):
batch_size = 128
num_classes = 10
epochs = 4
# input image dimensions
img_rows, img_cols = 28, 28
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train.reshape(x_train.shape[0], img_rows* img_cols)
x_test = x_test.reshape(x_test.shape[0], img_rows*img_cols)
input_shape = (img_rows * img_cols)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
# convert class vectors to binary class matrices
y_train = tf.keras.utils.to_categorical(y_train, num_classes)
y_test = tf.keras.utils.to_categorical(y_test, num_classes)
model = tf.keras.models.Sequential()
dummy_layer = FCLayer(10, cause_error = True)
model.add( dummy_layer )
model.add( tf.keras.layers.Dense(10, activation='softmax') )
model.compile(loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adadelta(),
metrics=['accuracy'])
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
答案 0 :(得分:2)
问题与急于执行TF 2.0有关-诸如tf.reshape
之类的任何操作都会在遇到它们时立即运行。对于特定模型,build
仅被调用一次。现在,正在发生的事情是您正在创建张量weights2
,它是tf.Variable
weights
的重塑版本,但 not 本身不是{{1 }}(op通常返回张量,而不是变量)。由于这是在急切执行中发生的,因此不会保留任何“记录”,并且tf.Variable
与weights2
没有任何联系。因此,在模型调用中使用它时,weights
无法更新。在weights
中不会发生这种情况,因为在这里else
只是指实际weights2
tf.Variable
的另一个名称。
解决此问题的两种方法:
在weights
中使用assign
进行整形(注意,我使用build
是因为self.w
是Keras层的保留名称):< / p>
self.weights
这不会引起任何错误/警告,但可能不是您想要的,因为您正在修改丢失的原始def build(self,input_shape):
self.input_size = input_shape[1]
self.w = self.add_weight(shape=(self.input_size,
self.output_size),
initializer='random_normal',
trainable=True)
if self.cause_error:
self.w.assign(tf.reshape(self.w,
shape = (self.input_size,
self.output_size)))
。我想您想在每个电话上使用weights
的修改版本。在这种情况下,请使用weights
方法:
call
之所以可行,是因为现在class FCLayer(tf.keras.layers.Layer):
def __init__(self,output_size,cause_error = False):
super(FCLayer,self).__init__()
self.output_size = output_size
self.cause_error = cause_error
def build(self,input_shape):
self.input_size = input_shape[1]
self.w = self.add_weight(shape=(self.input_size,
self.output_size),
initializer='random_normal',
trainable=True)
def call(self, inputs):
weights2 = tf.reshape(self.w, (self.input_size, self.output_size)
return tf.matmul(inputs, weights2)
操作是模型调用图的一部分,也就是说,我们可以回溯reshape
实际上来自weights2
,并且梯度可以流动。
答案 1 :(得分:0)
这种行为的原因很可能是build
函数上缺少@tf.function装饰器,即
@tf.function
def build(self, input_shape):
self.input_size = input_shape[1]
weights = self.add_weight(shape=(self.input_size,
self.output_size),
initializer='random_normal',
trainable=True)
if self.cause_error:
self.weights2 = tf.reshape(weights,
shape=(self.input_size,
self.output_size))
else:
self.weights2 = weights
为什么如此重要? Python Tensorflow API只是C / C ++实际实现的接口。当您在Python中提供自定义操作(如tf.reshape
)作为图形的一部分执行时,您必须指示模块将这部分代码编译为“本机” Tensorflow。
您的reshape
并没有真正改变任何形状都没关系。您已经“中断”了默认执行路径并“注入了” Python代码。 @tf.function
应该解决它。