返回介绍

2 神经网络:技巧和窍门

发布于 2025-02-18 23:44:03 字数 13170 浏览 0 评论 0 收藏 0

前面的部分讨论了神经网络的技术原理,理论和实践结合起来才能发挥大作用,现在咱们介绍一些神经网络在实际应用中常见的技巧和窍门。

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文