16

我正在查看面向初学者的 Tensorflow MNIST 示例,并在这一部分中发现:

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

将批量大小从 100 更改为 204 以上会导致模型无法收敛。它可以达到 204,但在 205 和我尝试过的任何更高的数字时,准确度最终会小于 10%。这是一个错误,是关于算法的,还是其他的?

这是为 OS X 运行他们的二进制安装,似乎是 0.5.0 版。

4

4 回答 4

28

您在初学者示例中使用的是非常基本的线性模型?

这是调试它的一个技巧 - 在增加批量大小时观察交叉熵(第一行来自示例,第二行是我刚刚添加的):

cross_entropy = -tf.reduce_sum(y_*tf.log(y))
cross_entropy = tf.Print(cross_entropy, [cross_entropy], "CrossE")

在批量大小为 204 时,您将看到:

I tensorflow/core/kernels/logging_ops.cc:64] CrossE[92.37558]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[90.107414]

但是在 205 处,你会从一开始就看到这样的序列:

I tensorflow/core/kernels/logging_ops.cc:64] CrossE[472.02966]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[475.11697]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1418.6655]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1546.3833]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1684.2932]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1420.02]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[1796.0872]
I tensorflow/core/kernels/logging_ops.cc:64] CrossE[nan]

Ack - NaN 出现了。基本上,大批量会产生如此巨大的梯度,以至于你的模型正在失控——它应用的更新太大,并且大大超出了它应该去的方向。

在实践中,有几种方法可以解决这个问题。您可以将学习率从 0.01 降低到例如 0.005,这会导致最终准确度为 0.92。

train_step = tf.train.GradientDescentOptimizer(0.005).minimize(cross_entropy)

或者您可以使用更复杂的优化算法(Adam、Momentum 等)来尝试做更多的事情来确定梯度的方向。或者你可以使用一个更复杂的模型,它有更多的自由参数来分散那个大的梯度。

于 2015-11-11T05:30:05.850 回答
16

@dga 给出了一个很好的答案,但我想扩展一点。

当我写初学者教程时,我实现了这样的成本函数:

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

我这样写是因为它看起来最类似于交叉熵的数学定义。但实际上这样做可能会更好:

cross_entropy = -tf.reduce_mean(y_*tf.log(y))

为什么使用平均值而不是总和会更好?好吧,如果我们求和,那么批量大小加倍会使成本加倍,梯度幅度也会加倍。除非我们调整我们的学习率(或者像@dga 建议的那样使用为我们调整它的算法),否则我们的训练将会爆炸!但是如果我们使用均值,那么我们的学习率就会变得与我们的批量大小无关,这很好。

我鼓励您查看 Adam ( tf.train.AdamOptimizer())。它通常比 SGD 更能容忍摆弄东西。

于 2015-11-11T06:19:15.697 回答
15

Nan 发生在 0*log(0) 发生时:

代替:

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

和:

cross_entropy = -tf.reduce_sum(y_*tf.log(y + 1e-10))
于 2015-12-18T21:54:10.770 回答
2

@dga 很好地向您解释了这种行为的原因(cross_entropy 变得太大),因此算法将无法收敛。有几种方法可以解决这个问题。他已经建议降低学习率。

梯度下降是最基本的算法。几乎所有其他优化器都将正常工作:

train_step = tf.train.AdagradOptimizer(0.01).minimize(cross_entropy)
train_step = tf.train.AdamOptimizer().minimize(cross_entropy)
train_step = tf.train.FtrlOptimizer(0.01).minimize(cross_entropy)
train_step = tf.train.RMSPropOptimizer(0.01, 0.1).minimize(cross_entropy)

另一种方法是使用tf.nn.softmax_cross_entropy_with_logits来处理数值不稳定性。

于 2015-11-15T12:35:54.803 回答