如何避免 Theano 计算梯度趋向于 NaN

How to avoid that Theano computing gradient going toward NaN

我正在尝试使用 5 个卷积层的 CNN - 2 个隐藏层 - 1 个 Softmax。

架构是:

cv0->relu->cv1->relu-cv2->relu->cv3->relu->cv4->relu->cv5->hid1->relu->hid2->relu->logistic softmax

通过对图像中的 66 个补丁标记应用随机梯度。出于测试目的,训练仅应用于具有 20 个 epoch 的单张图像。

从网络中识别出错误在每次迭代中激增,因此梯度在第 4 轮的第 3 轮后计算 nan。

正如您所见,在误差爆炸到非常高的值之后,梯度产生了 nan 并传播到所有网络中。

查看来自不同层权重值的单个节点以查看发生了什么:

layer8(softmax):

layer6 (hid1):

layer0 (cv0):

初始化时

[[-0.01704694 -0.01683052 -0.0894756 ]
 [ 0.12275343 -0.05518051 -0.09202443]
 [-0.11599202 -0.04718829 -0.04359322]]

而第 3 个纪元是

[[-24165.15234375 -26490.89257812 -24820.1484375 ]
 [-27381.8203125  -26653.3359375  -24762.28710938]
 [-23120.56835938 -21189.44921875 -24513.65039062]]

很明显,权重值正在爆炸式增长。

学习率为 0.01 所以为了解决这个问题,我将学习率改为 0.001,Nan 有时消失,网络收敛,有时不收敛,网络充满了 NaN。再次尝试使用 0.0001 的较小学习率,但我还没有看到 NaN。我从每次重新 运行 代码的结果中看到的结果确实不同,我认为这首先与权重初始化有关。

所以我尝试了不同的权重初始化:

对于带有 relu 的 Conv 层

W_bound_6 = numpy.sqrt(6. / (fan_in + fan_out))
W_bound_2 = numpy.sqrt(2. / (fan_in + fan_out))
W_values = numpy.asarray(
                numpy.random.randn(filter_shape[0], filter_shape[1], filter_shape[2], filter_shape[3]) * W_bound_2,
                dtype=theano.config.floatX)

以及隐藏层和softamx层

W_bound_2 = numpy.sqrt(2. / (filter_shape[0] + filter_shape[1]))
W_values = numpy.asarray(
                numpy.random.randn(filter_shape[0], filter_shape[1]) * W_bound_2,
                dtype=theano.config.floatX
            )

并将 b 全部归零。

差别不大,我仍然看不出结果有什么不同。

我将问题发布到:

成本函数

-T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]), y])

更新函数

updates = [
        (param_i, param_i - learning_rate * grad_i)
        for param_i, grad_i in zip(classifier.params, grads)
    ]

我想这可能是 "dead relu" 导致数学错误的问题。负对数似然成本函数涉及不期望为零的自然对数计算。 Relu 函数可以输出零,零的自然对数是欠定义的,所以它 returns NaN。在最后一层尝试使用不输出负数和零的函数或尝试另一个成本函数。

我正在寻找不同的方法来避免这个问题,但正在寻找其他人提出的正式解决方案并阅读一些理论解决方案我会在这里写下我的答案以帮助其他人遇到同样的问题。

这个问题背后的原因是使用了 softmax 和交叉熵。因此,当您计算梯度并跳零或 inf 时,您会得到 nan,它正在传播反向词抛出所有网络参数。

很少有人建议避免这个问题

  • 如果错误开始增加,则之后会出现 NaN:发散到期 学习率太高
  • 如果 NaN 突然出现:饱和单元产生不可微分 由于 log(0)
  • 导致的梯度 NaN 计算
  • NaN 由于浮点问题(高权重)或激活 输出 0/0, inf/inf, inf*weight...

解决方案:

  1. 降低学习率
  2. 更改权重初始化
  3. 使用 L2 范数
  4. 安全 softmax(小值添加到 log(x))
  5. 渐变剪裁

在我的案例中,学习率解决了这个问题,但我仍在努力进一步优化它