2 神经网络:技巧和窍门
前面的部分讨论了神经网络的技术原理,理论和实践结合起来才能发挥大作用,现在咱们介绍一些神经网络在实际应用中常见的技巧和窍门。
2.1 梯度检验
我们已经介绍了如何用微积分计算神经网络模型中参数的误差梯度。现在我们介绍另一种不使用误差反向传播,而近似估计梯度的方法:
其中,
从微分的定义来看,上述公式显然是正确的,但是怎么将其应用到求解误差梯度呢?对于一个给定的数据集,当我们正向扰动参数的第 i 个元素时(可以简单理解成
加上一个极小的正数),咱们基于前向传导可以计算出误差项
。同理,当我们负向扰动参数
的第 i 个元素时,咱们基于前向传导可以计算出新的误差项
。因此,其实通过做两次前向运算,我们就可以根据上面的公式估计出任何给定参数的梯度。当然了,其实只做一次前向传导所需要的运算量也不小了,所以在估计梯度时,这种方法比较耗时,但是,在用于验证反向传播的实现时,这种方法很赞,也用得很多。
梯度检验的简单实现可以参照下述方式:
def eval_numerical_gradient(f, x):
"""
a naive implementation of numerical gradient of f at x
- f should be a function that takes a single argument
- x is the point (numpy array) to evaluate the gradient
at
"""
fx = f(x) # evaluate function value at original point
grad = np.zeros(x.shape)
h = 0.00001
# iterate over all indexes in x
it = np.nditer(x, flags=[’multi_index’],
op_flags=[’readwrite’])
while not it.finished:
# evaluate function at x+h
ix = it.multi_index
old_value = x[ix]
x[ix] = old_value + h # increment by h
fxh = f(x) # evaluate f(x + h)
x[ix] = old_value # restore to previous value (very important!)
# compute the partial derivative
grad[ix] = (fxh - fx) / h # the slope
it.iternext() # step to next dimension
return grad
以下为页边注
梯度检验 :其实一般情况下,解析梯度是一个更快的梯度求解方法,不过容易出错,而梯度检验是个很好的比较解析梯度和数值型梯度的方法。数值型梯度可以用下述公式去计算:
其中,和
可以通过正向和负向微调
后两次前向传导来计算得到,这种方法的代码实现可以参阅 Snippet 2.1。
以上为页边注
2.2 正则化
像大多数分类器一样,神经网络也容易产生过拟合,这会导致其在验证集和测试集上的结果并不一定那么理想。为了解决这个问题,简单一点咱们可以应用 L2 正则化,加上正则化项的损失函数可以通过下述公式来计算:
在上述公式中,是矩阵
的 F 范数(frobenius norm),
是用于在加权和目标函数中进行正则化的相对权重。加上这个正则化项,意在通过作用到损失的平方来惩罚那些在数值上特别大的权重
(译者注:也就是让权重的分配更均匀一些)
。这样一来,目标函数(也就是分类器)的随意度 (译者注:也就是可用于拟合的复杂度)
就被降低了,约束了拟合函数的假设空间,因此减少了发生过拟合的可能性。施加这样一种约束条件可以用先验贝叶斯思想来理解,即最优的权重分配是所有权重都接近 0。你想知道有多接近?对啦,这正是所控制的——大的
会倾向于使所有权重都趋于 0。值得注意的是,偏移量
不会被正则化,也不会被计算入上述的损失项(试着想想为什么?)。
2.3 神经单元
前面的内容里,我们已经讨论过了包含 sigmoid 神经元(sigmoidal neurons)来实现非线性分类的神经网络算法,然而在许多应用中,使用其他激励(激活) 函数(activation functions)可以设计出更好的神经网络。这里列举了一些常用选择的函数表达式和梯度定义,它们是可以和上文讨论过的 sigmoid 函数(sigmoidal functions)互相替代的。
Sigmoid :这是通常拿来做例子的函数,我们已经讨论过它,其激励(激活) 函数为:
其中,
的梯度为:
以下为页边注
图 9:Sigmoid 非线性的响应
以上为页边注
Tanh :tanh 函数是除了 sigmoid 函数之外的另一种选择,在实际中,它的收敛速度更快。tanh 函数与 sigmoid 函数最主要的不同是 tanh 函数的输出结果在-1 和 1 之间,而 sigmoid 函数的输出结果在 0 和 1 之间。
其中, 的梯度为:
以下为页边注
图 10:非线性的响应
以上为页边注
Hard Tanh :hard tanh(硬双曲余弦正切) 函数在有些时候要优于 tanh 函数,因为它在计算上更为简便。然而当 z 大于 1 时,hard tanh 函数会在数值上形成饱和(译者注:即恒等于 1)。hard tanh 的激活函数为:
其微分也可以用分段函数来表达:
以下为页边注
图 11:hard tanh 非线性的响应以上为页边注
Soft Sign :Soft Sign 函数是另一个可以被用来替代 Tanh 函数的非线性函数,因为它也不会像硬限幅函数(hard clipped functions)那样过早饱和。其函数表达式为:
其微分表达式为:
其中是符号函数,即根据
的符号返回
或
。
以下为页边注
图 12:soft sign 非线性的响应
以上为页边注
ReLU :ReLU(修正线性单元,Recti?ed Linear Unit)函数是激活函数的一个流行选择,因为即使对特别大的,它也不会饱和,并且已经发现它在计算机视觉应用中非常好用。其函数表达式为:
其微分表达式为:
以下为页边注
图 13:ReLU 非线性的响应
以上为页边注
Leaky ReLU :对于非正数的,传统设计上的 ReLU 单元不会回传误差——而 leaky ReLU 修正了这一点,使得
是负数时,很小的误差也会反向传播回传回去。其函数表达式为:
其中,
因此其微分表达式可以被表示为:
以下为页边注
图 14:leaky ReLU 非线性的响应
以上为页边注
2.4 Xavier 参数初始化
在《理解训练深层前馈神经网络的困难(Understanding the Difficulty of Training Deep Feedforward Neural Networks)》(2010) 一文中,Xavier 等人研究了不同权重和偏差的初始化方案对训练动力(training dynamics)的影响。实证研究结果表明,对于 sigmoid 和 tanh 激活单元,当矩阵的权重以均匀分布在以下值域范围内被随机初始化时,有着更低的错误率和更快的收敛速度:
其中,是
关联的输入单元的数量(fan-in),
是
关联的输出单元的数量(fan-out)。
在这种参数初始化方案里,偏差项() 被初始化为 0。这种方法的目的是维持跨层的激活方差和反向传播梯度方差。如果不初始化,梯度方差(包含大量修正信息)一般会随层间反向传播而很快衰减。
2.5 学习速率
模型最优化的过程中,参数更新的速度可以通过学习速率来控制。比如下面的梯度下降公式中,是学习速率:
看到公式以后你可能会认为越大收敛速度会越快,事实上并不是这样哦。学习速率过大甚至可能会导致损失函数的不收敛,因为有时候因为太激进,参数的迭代步伐太大,一不小心跨过了凸优化的极小值,如图 15 所示。在非凸模型中(我们大多数时候遇到的),大学习速率的结果是不可预测的,但出现损失函数不收敛的可能性是非常高的。所以一定要慎重哦。
以下为页边注
图 15:从上图可以看出,有时候学习率太大,更新的参数反倒跨过了最低点,朝着误差增大的方向挪动了。
以上为页边注
那怎么办呢?一个简单的方案就是,初始化一个比较小的学习速率,谨慎地在参数空间内迭代和调整以避免模型不收敛。同时,我们还可以固定模型中所有参数的学习速率,而不是为模型中所有参数设定不同的学习速率。
深度学习系统训练阶段通常最耗时耗资源,一些研究也试图应用一些新的方法来设置学习速率。例如,Ronan Collobert 通过取神经元输入单元数的平方根的倒数来把权重
(
)的学习速率进行标准化。另一种方法是允许学习速率随着时间而减小,如:
在上述方案中,是一个可调参数,代表起始学习速率。
也是一个可调参数,代表学习速率应该开始降低的时间。实践中,这种方法相当有效。下个部分,我们会讨论另一种方法,即不需要手动调节学习速率的自适应梯度下降法。
2.6 使用 AdaGrad 进行次梯度优化
AdaGrad 是标准随机梯度下降法(SGD)的一种实现,但是有一个关键的区别:每个参数的学习速率是不同的。参数的学习速率取决于该参数梯度更新的历史情况,更新的历史越稀疏,就应该使用更大的学习速率加快更新。换句话说,那些在过去未被更新的参数更有可能在现在获得更高的学习速率。其形式如下:
其中,
对应上述公式我们可以看到,在这种算法中,如果梯度历史的方均根(RMS)非常低,学习速率会比较高。算法的实现如下:
# Assume the gradient dx and parameter vector x cache += dx**2
x += - learning_rate * dx / np.sqrt(cache + 1e-8)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论