【问题标题】:Different behaviour between same implementations of TensorFlow and KerasTensorFlow 和 Keras 的相同实现之间的不同行为
【发布时间】:2019-01-03 21:11:45
【问题描述】:

我的机器上有 TensorFlow 1.9 和 Keras 2.0.8。在用一些玩具数据训练神经网络时,TensorFlow 和 Keras 之间的训练曲线非常不同,我不明白为什么。

对于 Keras 实现,网络学习良好,损失不断减少,而对于 TensorFlow 实现,网络没有学习任何东西,损失也没有减少。我试图确保两种实现都使用相同的超参数。 为什么行为如此不同?

网络本身有两个输入:图像和向量。然后将它们通过它们自己的层,然后再连接。

这是我的实现。

张量流:

# Create the placeholders
input1 = tf.placeholder("float", [None, 64, 64, 3])
input2 = tf.placeholder("float", [None, 4])
label = tf.placeholder("float", [None, 4])

# Build the TensorFlow network
# Input 1
x1 = tf.layers.conv2d(inputs=input1, filters=30, kernel_size=[5, 5], strides=(2, 2), padding='valid', activation=tf.nn.relu)
x1 = tf.layers.conv2d(inputs=x1, filters=30, kernel_size=[5, 5], strides=(2, 2), padding='valid', activation=tf.nn.relu)
x1 = tf.layers.flatten(x1)
x1 = tf.layers.dense(inputs=x1, units=30)
# Input 2
x2 = tf.layers.dense(inputs=input2, units=30, activation=tf.nn.relu)
# Output
x3 = tf.concat(values=[x1, x2], axis=1)
x3 = tf.layers.dense(inputs=x3, units=30)
prediction = tf.layers.dense(inputs=x3, units=4)

# Define the optimisation
loss = tf.reduce_mean(tf.square(label - prediction))
train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)

# Train the model
sess = tf.Session()
sess.run(tf.global_variables_initializer())
training_feed = {input1: training_input1_data, input2: training_input2_data, label: training_label_data}
validation_feed = {input1: validation_input1_data, input2: validation_input2_data, label: validation_label_data}
for epoch_num in range(30):
    train_loss, _ = sess.run([loss, train_op], feed_dict=training_feed)
    val_loss = sess.run(loss, feed_dict=validation_feed)

Keras:

# Build the keras network
# Input 1
input1 = Input(shape=(64, 64, 3), name='input1')
x1 = Conv2D(filters=30, kernel_size=5, strides=(2, 2), padding='valid', activation='relu')(input1)
x1 = Conv2D(filters=30, kernel_size=5, strides=(2, 2), padding='valid', activation='relu')(x1)
x1 = Flatten()(x1)
x1 = Dense(units=30, activation='relu')(x1)
# Input 2
input2 = Input(shape=(4,), name='input2')
x2 = Dense(units=30, activation='relu')(input2)
# Output
x3 = keras.layers.concatenate([x1, x2])
x3 = Dense(units=30, activation='relu')(x3)
prediction = Dense(units=4, activation='linear', name='output')(x3)

# Define the optimisation
model = Model(inputs=[input1, input2], outputs=[prediction])
adam = optimizers.Adam(lr=0.001)
model.compile(optimizer=adam, loss='mse')

# Train the model
training_inputs = {'input1': training_input1_data, 'input2': training_input2_data}
training_labels = {'output': training_label_data}
validation_inputs = {'input1': validation_images, 'input2': validation_state_diffs}
validation_labels = {'output': validation_label_data}
callback = PlotCallback()
model.fit(x=training_inputs, y=training_labels, validation_data=(validation_inputs, validation_labels), batch_size=len(training_label_data[0]), epochs=30)

这里是训练曲线(每个实现两次运行)。

张量流:

Keras:

【问题讨论】:

  • 你确定batch size是一样的吗?
  • 是的,我相信是的。对于 TensorFlow,整个“training_label_data”是使用 feed 输入的。对于 Keras,参数设置为“batch_size=len(training_label_data[0])”。所以这两种实现都使用了整个训练集。
  • 好吧,从你的 TF 实现我猜是len(training_label_data[0])=4
  • 我不知道你为什么认为我的 TF 代码应该暗示这一点......训练示例的数量实际上是 100,但这在任何一个实现中都没有指定——它是从数据中推断出来的变量本身。
  • 您能否: 1. 打印这些值。 2.尝试将TF的batch size改为len(training_label_data[0])会发生什么

标签: python tensorflow keras


【解决方案1】:

在仔细检查了您的实现之后,我发现除了批量大小之外,所有超参数都匹配。我不同意@Ultraviolet 的回答,因为tf.layers.conv2d 的默认kernel_initializer 也是Xavier(参见conv2d 的TF 实现)。

学习曲线不匹配有以下两个原因:

  1. 来自 Keras 实现(版本 2)的参数接收的更新比 TF 实现(版本 1)的参数多得多。在版本 1 中,您将在每个时期同时将完整数据集馈送到网络中。这导致只有 30 次亚当更新。相比之下,版本 2 执行30 * ceil(len(training_label_data)/batch_size) adam 更新,batch_size=4

  2. 版本 2 的更新比版本 1 的更新噪声更大,因为梯度是在更少的样本上平均的。

【讨论】:

  • +1 用于发现批量大小问题。但是,我仍然不确定为什么在 keras 中说 None 并将初始化设置为 xavier?
  • 查看这个我没有注意到任何地方说Noneglorot_uniform,这是 keras 的默认设置。 github.com/tensorflow/tensorflow/blob/…
  • 谢谢@rvinas。你认为如果优化器是普通梯度下降而不是 Adam,那么批量大小仍然会产生影响吗?我的意思是:较小的批次大小是否有助于优化,因为(i)Adam 有自己的内部参数,每个批次都会更新,因此较小的批次大小意味着对这些内部参数的更新更多,还是因为(ii ) 较小的批量大小意味着梯度更随机,因此优化更容易找到局部最小值之外的路径?还是两者兼而有之?
  • @Ultraviolet 注意tf.layers.conv2d实例化了L227中定义的类,它继承自keras.layers.Conv2D(并且Keras的kernel_initializer参数设置为TFkernel_initializerL311 )。
  • 是的,但我们确实在这里将其设置为 None 并且 none 设置为初始化器 github.com/tensorflow/tensorflow/blob/…
【解决方案2】:

我没有注意到您的两种实现之间有任何区别。假设没有,我认为,

  • 首先,他们以不同的初始损失开始。 这表明图的初始化是不同的。 因为您没有提到任何初始化程序。查看文档 (tensorflow Conv2D, Keras Conv2D) 我发现 默认初始化器不同。

    tensorflow 使用 no initializer 另一方面 Keras 使用 Xavier 初始化器

  • 第二件事是(这是我的假设)tensorflow 损失最初急剧下降,但后来与Keras 相比并没有减少太多。由于设计的网络不是很健壮也不是很深,因为初始化不好tensorflow陷入局部最小值。

  • 第三,两者之间可能存在一些细微差别,因为 默认参数可能会有所不同。通常,包装框架尝试 处理一些默认参数,以便我们需要更少的调整来获得 最佳权重。
    我使用了 FastAI 框架,基于 pytorchKeras框架进行一定的分类 使用相同的 VGG 网络的问题。我在 FastAI 方面取得了显着进步。因为 它的默认参数最近用最新的最佳参数进行了调整 实践。

编辑:

我没有注意到批量大小不同,这是这里最重要的超参数之一。 @rvinas 在他的回答中说得很清楚。

【讨论】:

  • TensorFlow 和 Keras 对 conv2d 使用相同的初始化程序:github.com/tensorflow/tensorflow/blob/r1.9/tensorflow/python/…
  • 我不知道内部实现,但文档说,内核初始化器对于 tensorflow 是 None 但对于 Keras 它是 Xavier
  • 我知道文档说kernel_initializer 默认是None,但这并不意味着变量没有初始化器。所有变量都需要以某种方式初始化,这种方式在 Keras 和 TensorFlow 中都是 Xavier 初始化。其实tf.layers.conv2d是直接使用keras.layers.Conv2D,所以在TF中设置kernel_initializer=None会导致使用默认的Keraskernel_initializer
  • 谢谢,这个答案帮助我调试。我尝试增加网络的大小(如您在第二点中所建议的那样),然后这使 TensorFlow 实现能够学习,因此它之前陷入了局部最小值。两种实现之间仍然存在显着差异,可能是由于初始化,但至少现在我的 TensorFlow 实现实际上减少了损失。
  • 我还尝试在 TensorFlow 实现中减小批量大小(在我最初的实现中,Keras 的批量大小实际上要小得多)。这也允许 TensorFlow 实现避免局部最小值,从而减少损失。
猜你喜欢
  • 1970-01-01
  • 2019-04-23
  • 1970-01-01
  • 2021-06-19
  • 2021-11-12
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
  • 2018-11-29
相关资源
最近更新 更多