TensorFlow 与深度学习 (8) 特别注意之处

随着层数的增加,神经网络越来越难以收敛。但现在我们知道如何控制它们的行为了。这里是一些只用一行就可以实现的改进,这些小技巧会帮到你,当你看到准确度曲线出现如下情况的时候:

RELU 激活函数

在深度网络里,sigmoid 激活函数确实能带来很多问题。它把所有的值都挤到了 0 到 1 之间,而且当你重复做的时候,神经元的输出和它们的梯度可能会完全消失。值得一提的是,出于历史原因,一些现代神经网络使用了 RELU(Rectified Linear Unit,修正线性单元,俗称斜坡函数——译者注),它大致是如下这个样子:

Update 1/4:用 RELU 替换你所有的 sigmoid,然后你会得到一个更快的初始收敛,并且也避免了继续增加 layer 时产生的一些后续问题。仅仅在代码中简单地用 tf.nn.relu 来替换 tf.nn.sigmoid 即可。

更好的 optimizer

在一个维度很高的空间里,就像现在这个情况——我们有 10K 量级的权值和偏置值——鞍点 (saddle points)会频繁出现。这些点不是局部最小值,但它的梯度却是零,那么梯度降的优化会卡在这里。TensorFlow 有一系列可以用的优化器,包括一些带有一定的惯性、能够安全越过鞍点的优化器。

Update 2/4: 现在将你的 tf.train.GradientDescentOptimiser 替换为 tf.train.AdamOptimizer

随机初始化

精确度一直卡在 0.1?你的权值初始化成随机值了吗?对于偏置,如果用 ReLU 的话,最好的办法就是把它们都初始化成小的正值,这样神经元一开始就会工作在 ReLU 的非零区域内。

1
2
W = tf.Variable(tf.truncated_normal([K, L], stddev=0.1))
B = tf.Variable(tf.ones([L])/10)

Update 3/4: 检查是否你所有的权值和偏置值都被正确地初始化了。上图所示,0.1 对偏置是一个合适的初始值。

NaN ???

如果你看到你的精确曲线陡然下滑并且命令行输出的交叉熵是 NaN,别慌,你其实是正在尝试计算 log(0),而这肯定是个非数(NaN, Not a Number)。还记得吗,交叉熵的计算涉及到对 softmax 层的输出取对数。鉴于 softmax 基本上是一个指数,它肯定不是 0,我们如果用 32 位精度的浮点运算就还好,exp(-100) 基本上可以算作是 0 了。

幸运的是,TensorFlow 有一个方便的函数可以在一步内计算 softmax 和交叉熵,它是以一种数值上较为稳定的方式实现的。如果要使用它,你需要在应用 softmax 之前将原始的权重和加上你最后一层的偏置隔离开来(在神经网络的术语里叫 logits)。

如果你模型的最后一行是这样的:

1
Y = tf.nn.softmax(tf.matmul(Y4, W5) + B5)

你需要把它替换成:

1
2
Ylogits = tf.matmul(Y4, W5) + B5
Y = tf.nn.softmax(Ylogits)

你现在能以一种安全的方式计算交叉熵了:

1
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(Ylogits, Y_)

同样地,加上这行代码使得测试和训练的交叉熵能够显示在统一尺度:

1
cross_entropy = tf.reduce_mean(cross_entropy)*100

Update 4/4: 请把 tf.nn.softmax_cross_entropy_with_logits 加到你的代码里。你也可以跳过这一步,等你遇到 NaN 以后再来做这步。

你现在可以进入更深入的学习了。