升级 1/4:用 RELU 替换你所有的 sigmoid,然后你会得到一个更快的初始收敛并且当我们继续增加层的时候也避免了一些后续问题的产生。仅仅在代码中简单地用 tf.nn.relu 来替换 tf.nn.sigmoid 就可以了。 一个更好的优化器 在一个特别多维的空间里,就像当前这个情况——我们有 10K 量级的权值和偏置值——「鞍点 (saddle points)」会频繁出现。这些点不是局部最小值点,但它的梯度却是零,那么梯度降的优化会卡在这里。TensorFlow 有一系列可以用的优化器,包括一些带有一定的惯性,能够安全越过鞍点的优化器。 升级 2/4:现在将你的 tf.train.GradientDescentOptimiser 替换为 tf.train.AdamOptimizer。 随机初始化 准确性一直卡在 0.1?你把你的权值初始化成随机值了没?对于偏置值,如果用 ReLU 的话,最好的办法就是把它们都初始化成小的正值,这样神经元一开始就会工作在 ReLU 的非零区域内。 W = tf.Variable(tf.truncated_normal([K, L] ,stddev=0.1)) B = tf.Variable(tf.ones([L])/10) 升级 3/4:现在检查是否你所有的权值和偏置值都被初始化好了。上图所示的 0.1 会作为偏置值。 不定值(NaN) 如果你看到你的精确曲线陡然下滑并且调试口输出的交叉熵是 NaN,不用感到头疼,你其实是正在尝试计算 log(0),而这肯定是个不定值(NaN)。还记得吗,交叉熵的计算涉及到对 softmax 层的输出取对数。鉴于 softmax 基本上是一个指数,它肯定不是 0,我们如果用 32 位精度的浮点运算就还好,exp(-100) 基本上可以算作是 0 了。 很幸运,TensorFlow 有一个非常方便的函数可以在单步内计算 softmax 和交叉熵,它是以一种数值上较为稳定的方式实现的。如果要使用它,你需要在应用 softmax 之前将原始的权重和加上你最后一层的偏置隔离开来(在神经网络的术语里叫「logits」)。 如果你模型的最后一行是这样的: Y = tf.nn.softmax(tf.matmul(Y4, W5) + B5) 你需要把它替换成: Ylogits = tf.matmul(Y4, W5) + B5Y = tf.nn.softmax(Ylogits) 并且你现在能以一种安全的方式计算交叉熵了: cross_entropy = tf.nn.softmax_cross_entropy_with_logits(Ylogits, Y_) 同样加上下面这行代码使得测试和训练的交叉熵能够同框显示: cross_entropy = tf.reduce_mean(cross_entropy)*100 升级 4/4:请把 tf.nn.softmax_cross_entropy_with_logits 加到你的代码里。你也可以跳过这一步,等你真在你的输出里看到 NaN 以后再来做这步。现在,你已经准备好实现「深度」了。 9、实验:学习速率衰退 通过两个、三个或者四个中间层,你现在可以将准确度提升至接近 98%,当然,你的迭代次数要达到 5000 次以上。不过你会发现你并不总是会得到这样的结果。
这些曲线很嘈杂,看看测试精确度吧:它在全百分比范围内跳上跳下。这意味着即使 0.003 的学习率我们还是太快了。但我们不能仅仅将学习率除以十或者永远不停地做训练。一个好的解决方案是开始很快随后将学习速率指数级衰减至比如说 0.0001。 这个小改变的影响是惊人的。你会看到大部分的噪声消失了并且测试精确度持续稳定在 98% 以上。 再看看训练精确度曲线。在好多个 epoch 里都达到了 100%(一个 epoch=500 次迭代=全部训练图片训练一次)。第一次我们能很好地识别训练图片了。 请把学习率衰退加到你的代码里。为了把一个不同的学习率在每次迭代时传给 AdamOptimizer,开奖,你需要定义一个新的占位符(placeholder)并在每次迭代时通过 feed_dict 赋给它一个新的参数。 (责任编辑:本港台直播) |