我在尝试使用神经网络,试图理解根据需要解决的问题类型来设计其架构的最佳实践。
我生成了一个非常简单的数据集,由一个凸区域组成,如下图所示:
当我使用具有L = 1或L = 2隐藏层的架构时(加上输出层),一切正常,但一旦我添加第三个隐藏层(L = 3),我的性能就下降到略高于随机水平。
我知道网络的复杂性增加(权重和参数的数量)往往会导致过拟合数据,但我认为这不是我的问题本质,原因有二:
- 我在训练集上的表现也大约是60%(而过拟合通常意味着你有很低的训练误差和很高的测试误差),
- 而且我有大量的数据样本(不要看图表,那只是我上传的一个玩具图表)。
有谁能帮我理解为什么在这样一个简单的任务中添加一个额外的隐藏层会导致性能下降吗?
这是我性能随使用层数变化的图像:
由于评论添加的部分:
- 我使用的是假设值在0到1之间的sigmoid函数,
L(s) = 1 / 1 + exp(-s)
- 我使用提前停止(在40000次反向传播迭代后)作为停止学习的标准。我知道这不是停止的最佳方式,但我认为对于这样一个简单的分类任务来说是可以的,如果你认为这是我无法收敛的主要原因,我可能会实施一些更好的标准。
回答:
至少从表面上看,这似乎是所谓的“梯度消失”问题的一个案例。
激活函数
你的神经元根据逻辑sigmoid函数激活,f(x) = 1 / (1 + e^-x) :
这个激活函数经常被使用,因为它有几个很好的特性。其中一个很好的特性是f(x)的导数可以使用函数本身的值来计算,即f'(x) = f(x)(1 – f(x))。这个函数在x接近零时有非零值,但当|x|变大时很快变为零:
梯度下降
在具有逻辑激活的前馈神经网络中,误差通常通过网络向后传播,使用第一导数作为学习信号。网络中权重的常规更新与归因于该权重的误差、当前权重值以及逻辑函数的导数成正比。
delta_w(w) ~= w * f'(err(w)) * err(w)
作为三个可能非常小的值的乘积,这样的网络中的第一导数可能会非常迅速地变小,如果网络中的权重超出了逻辑函数导数的“中间”范围。此外,这种迅速消失的导数会因增加更多的层而加剧,因为一层的误差会被“分开”并分配给该层中的每个单元。这反过来又进一步减少了下层中的梯度。
在具有超过两个隐藏层的网络中,这可能成为训练网络的一个严重问题,因为一阶梯度信息会让你认为权重无法有用地改变。
然而,有一些解决方案可以帮助!在我看来,这些解决方案涉及改变你的学习方法,使用比一阶梯度下降更复杂的方法,通常会包含一些二阶导数信息。
动量
近似使用一些二阶信息的最简单解决方案是将动量项包含在你的网络参数更新中。不是使用以下方式更新参数:
w_new = w_old - learning_rate * delta_w(w_old)
而是加入一个动量项:
w_dir_new = mu * w_dir_old - learning_rate * delta_w(w_old)w_new = w_old + w_dir_new
直观上,你希望使用过去导数的信息来帮助决定你是完全遵循新的导数(通过设置mu = 0来实现),还是继续按照前一次更新的方向前进,并根据新的梯度信息进行调整(通过设置mu > 0来实现)。
你实际上可以通过使用“内斯特罗夫加速梯度”来做得更好:
w_dir_new = mu * w_dir_old - learning_rate * delta_w(w_old + mu * w_dir_old)w_new = w_old + w_dir_new
我认为这里的想法是,不是计算在“旧”参数值w
处的导数,而是在根据标准动量项移动到那里后计算“新”设置的w
处的导数。点击这里(PDF)在神经网络的背景下阅读更多内容。
无Hessian
将二阶梯度信息纳入你的神经网络训练算法的教科书方式是使用牛顿法来计算目标函数相对于参数的一阶和二阶导数。然而,二阶导数,称为Hessian矩阵,通常非常大且计算成本高昂。
不是计算整个Hessian,过去几年的聪明研究表明了一种方法,可以计算特定搜索方向中的Hessian值。你可以使用这个过程来识别比仅使用一阶梯度更好的参数更新。
你可以通过阅读研究论文(PDF)或查看样本实现来详细了解这一点。
其他
还有许多其他优化方法可能对这个任务有用——共轭梯度(PDF——绝对值得一读),Levenberg-Marquardt(PDF),L-BFGS——但从我在研究文献中看到的,动量和无Hessian方法似乎是最常见的。