将tf.contrib.opt.ScipyOptimizerInterface与tf.keras.layers一起使用,损失不会改变

时间:2019-02-28 17:05:30

标签: python tensorflow keras tf.keras

我想在tensorflow中使用外部优化器接口来使用牛顿优化器,因为tf.train仅具有一阶梯度下降优化器。同时,我想使用tf.keras.layers构建我的网络,因为在构建大型复杂网络时,它比使用tf.Variables更容易。我将通过以下简单的一维线性回归示例展示我的问题:

import tensorflow as tf
from tensorflow.keras import backend as K
import numpy as np

#generate data
no = 100
data_x = np.linspace(0,1,no)
data_y = 2 * data_x + 2 + np.random.uniform(-0.5,0.5,no)
data_y = data_y.reshape(no,1)
data_x = data_x.reshape(no,1)

# Make model using keras layers and train
x = tf.placeholder(dtype=tf.float32, shape=[None,1])
y = tf.placeholder(dtype=tf.float32, shape=[None,1])

output = tf.keras.layers.Dense(1, activation=None)(x)

loss = tf.losses.mean_squared_error(data_y, output)
optimizer = tf.contrib.opt.ScipyOptimizerInterface(loss, method="L-BFGS-B")

sess = K.get_session()
sess.run(tf.global_variables_initializer())

tf_dict = {x : data_x, y : data_y}
optimizer.minimize(sess, feed_dict = tf_dict, fetches=[loss], loss_callback=lambda x: print("Loss:", x))

运行此命令时,损失根本不会改变。使用tf.train中的任何其他优化器时,它都可以正常工作。另外,当使用tf.layers.Dense()代替tf.keras.layers.Dense()时,它确实可以使用ScipyOptimizerInterface起作用。所以真正的问题是tf.keras.layers.Dense()和tf.layers.Dense()之间有什么区别。我看到tf.layers.Dense()创建的变量的类型为tf.float32_ref,而tf.keras.layers.Dense()创建的变量的类型为tf.float32。就我现在而言,_ref表示该张量是可变的。所以也许就是这个问题?但是话又说回来,tf.train中的任何其他优化程序都可以在keras层上正常工作。

谢谢

2 个答案:

答案 0 :(得分:0)

我认为问题出在线路上

output = tf.keras.layers.Dense(1, activation=None)(x)

这种格式的 output 不是图层,而是图层的输出,这可能会阻止包装程序收集图层的权重和偏差并将其馈送到优化器。尝试将其写成两行,例如

output = tf.keras.layers.Dense(1, activation=None)
res = output(x)

如果您要保留原始格式,则可能必须手动收集所有可训练对象,然后通过var_list选项将它们输入优化器

optimizer = tf.contrib.opt.ScipyOptimizerInterface(loss, var_list = [Trainables], method="L-BFGS-B")

希望这会有所帮助。

答案 1 :(得分:0)

经过大量的挖掘,我找到了可能的解释。

ScipyOptimizerInterface使用feed_dicts在优化过程中模拟变量的更新。它仅在最后执行分配操作。相反,tf.train优化器始终会分配操作。 ScipyOptimizerInterface的代码并不复杂,因此您可以轻松地进行验证。

现在的问题是,使用feed_dict辅助变量的工作大部分是偶然的。这是link,我从中了解到这一点。换句话说,通过Feed dict分配变量(这是ScipyOptimizerInterface所做的)是一种执行更新的简单方法。

现在,这种破解方法大多数都可以使用,除非无效。 tf.keras.layers.Dense使用ResourceVariables建模模型的权重。这是简单变量的改进版本,具有更清晰的读/写语义。问题在于,在新的语义下,提要字典更新会在损失计算之后发生。上面的链接提供了一些说明。

现在tf.layers目前是tf.keras.layer的薄包装,所以我不确定为什么它会起作用。也许在代码中的某些地方进行了兼容性检查。

解决这个问题的方法有些简单。

  • 要么避免使用使用ResourceVariables的组件。这可能有点困难。
  • 修补ScipyOptimizerInterface以始终对变量进行赋值。由于所有必需的代码都在一个文件中,因此这相对容易。

已做出一些努力以使界面热切工作(默认情况下使用ResourceVariables)。看看这个link