前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >斯坦福CS231n - CNN for Visual Recognition(3)-lecture3(下)最优化

斯坦福CS231n - CNN for Visual Recognition(3)-lecture3(下)最优化

作者头像
李智
发布2018-08-03 17:17:19
2930
发布2018-08-03 17:17:19
举报
文章被收录于专栏:李智的专栏

损失函数可以量化某个具体权重集WW的质量。而最优化的目标就是找到能够最小化损失函数值的WW ,本节主要讲最优化问题。

最优化

  上节我们已经介绍了图像分类的两个关键部分:评分函数损失函数,接下来就是最优化的问题了,即如何寻找使得损失函数值最小的WW。 对于SVM 得分函数:f(xi,W)=Wxif(x_i,W)=Wx_i 损失函数:L=1N∑i∑j≠yi[max(0,f(xi;W)j−f(xi;W)yi+1)]+λR(W)L = \frac{1}{N} \sum\limits_i \sum\limits_{j\neq y_i} \left[ \max(0, f(x_i; W)_j - f(x_i; W)_{y_i} + 1) \right] + \lambda R(W)

1 损失函数可视化

  损失函数一般都是在很高维度的空间里,很难直接可视化,但是我们可以采取一种比较巧妙的方法:在1个维度或者2个维度的方向上对高维空间进行切片,就能得到一些直观感受。例如,随机生成一个权重矩阵WW,该矩阵就与高维空间中的一个点对应。然后沿着某个维度方向前进的同时记录损失函数值的变化。 具体一点说,就是我们找到一个向量W1W_1,然后我们给不同的α\alpha值,计算L(W+αW1)L(W+\alpha W_1),这样,如果α\alpha取得足够密,其实我们就能够在一定程度上描绘出损失函数沿着这个方向的变化了。 同样,如果我们给两个方向W1W_1和W2W_2,那么我们可以确定一个平面,我们再取不同值的aa和bb,计算L(W+aW1+bW2)L(W+aW_1+bW_2)的值,那么我们就可以大致绘出在这个平面上,损失函数的变化情况了。

  根据上面的方法,我们画出了下面3个图。最左边的图是调整a的不同取值,绘出的损失函数变化曲线(越高值越大);中间和右边图是调整a与b的取值,绘出的损失函数变化图(蓝色表示损失小,红色表示损失大),中间是在一个图片样本上计算的损失结果,最右图为100张图片上计算的损失结果的一个平均。显然沿着直线方向得到的曲线底端为最小的损失值点,而曲面呈现的碗状图形碗底为损失函数取值最小处。

数学解释   对于第i个样本,我们知道它的损失函数值为:

Li=∑j≠yi[max(0,wTjxi−wTyixi+1)]

L_i = \sum\limits_{j\neq y_i} \left[ \max(0, w_j^Tx_i - w_{y_i}^Tx_i + 1) \right]

  所有的样本上的损失函数值,是它们损失函数值(max(0,-),因此最小值为0)的平均值。为了更好理解,我们假定训练集里面有3个样本,都是1维的,同时总共有3个类别。所以SVM损失(暂时不考虑正则化项)可以表示为如下的式子:

L0=L1=L2=L=max(0,wT1x0−wT0x0+1)+max(0,wT2x0−wT0x0+1)max(0,wT0x1−wT1x1+1)+max(0,wT2x1−wT1x1+1)max(0,wT0x2−wT2x2+1)+max(0,wT1x2−wT2x2+1)(L0+L1+L2)/3

\begin{align} L_0 = & \max(0, w_1^Tx_0 - w_0^Tx_0 + 1) + \max(0, w_2^Tx_0 - w_0^Tx_0 + 1) \\ L_1 = & \max(0, w_0^Tx_1 - w_1^Tx_1 + 1) + \max(0, w_2^Tx_1 - w_1^Tx_1 + 1) \\ L_2 = & \max(0, w_0^Tx_2 - w_2^Tx_2 + 1) + \max(0, w_1^Tx_2 - w_2^Tx_2 + 1) \\ L = & (L_0 + L_1 + L_2)/3 \end{align}

  因为这个例子里的样本都是1维的,因此其实xix_i和wjw_j都是实数。拿w0w_0举例,损失函数里,大于0的值其实都和w0w_0是线性关系的,而最小值为0。因此,我们可以想象成,三条折线合体得到的最终曲线,如下图所示:

  另外,从之前碗状结构的示意图,我们会发现SVM损失函数是一个凸函数,而对于凸函数的最小值求解方法有很多种。而当我们把损失函数ff扩充到神经网络之后,损失函数将变成一个非凸函数,此时可视化时,我们看到的将不再是一个碗状结构,而是凹凸不平的曲面。

2 最优化策略

  最优化的目标为找到能够最小化损失函数值的WW 。对于基础的童鞋,这节课也许比较简单,因为使用的例子(SVM 损失函数)是一个凸函数问题。但是,最终的目标是不仅仅对凸函数做最优化,而是能够最优化一个神经网络,而对于神经网络是不能简单的使用凸函数的最优化技巧的。

2.1 随机搜索

  很显然这不是一个好的搜索方法,或者说,比较差劲。思想很简单,就是随机尝试很多不同的权重,然后看其中哪个最好。之后,我们可以把这次随机搜索中找到的最好的权重WW取出,然后去跑测试集。

核心思路:迭代优化。虽然找到最优的权重W非常困难,甚至是不可能的(尤其当W中存的是整个神经网络的权重的时候),但如果问题转化为:对一个权重矩阵集WW取优,使其损失值稍微减少。那么问题的难度就大大降低了。换句话说,我们的方法从一个随机的WW开始,然后对其迭代取优,每次都让它的损失值变得更小一点。这就引出了下面的第二个最优化策略:随机局部搜索。

2.2 随机局部搜索

  这种搜索方式不每次都随机产生一个参数矩阵WW,而是在现有的参数WW基础上,搜寻一下周边临近的参数,有没有比现在参数更好的WW,然后我们只用更好的WW替换现在的WW,接着在周围继续小范围搜寻。我们可以这么想,在一座山上,现在要下山,然后我们每次都伸脚探一探周边,找一个比现在的位置下降一些的位置,然后迈一步,接着在新的位置上做同样的操作,一步步直至下山。

2.3 跟随梯度

  前两个策略中,我们随机尝试在权重空间中找到一个方向,沿着该方向能降低损失函数的损失值。其实可以直接计算出最好的方向,这就是从数学上计算出最陡峭的方向。这个方向就是损失函数的梯度(gradient)。这就好比我们在山顶,要以最快的方式下山,会环顾四周,然后找到最陡的方向,迈一小步,然后再找当前位置最陡的下山方向,再迈一小步…

  在一维函数中,斜率是函数在某一点的瞬时变化率。梯度是函数的斜率的一般化表达,它不是一个值,而是一个向量。在输入空间中,梯度是各个维度的斜率组成的向量(或者称为导数derivatives)。对一维函数的求导公式如下:

df(x)dx=limh →0f(x+h)−f(x)h

\frac{df(x)}{dx} = \lim_{h\ \to 0} \frac{f(x + h) - f(x)}{h}

  当函数有多个参数的时候,我们称导数为偏导数。而梯度就是在每个维度上偏导数所形成的向量。

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
3 梯度计算

有两种梯度计算方法: 数值梯度(numerical gradient):计算缓慢,但是简单一些 解析梯度(analytic gradient):计算迅速,结果精确,但是实现时容易出错,且需要使用微分

3.1 数值梯度

数值梯度,就是利用求导公式

df(x)dx=limh →0f(x+h)−f(x)h

\frac{df(x)}{dx} = \lim_{h\ \to 0} \frac{f(x + h) - f(x)}{h}

  对每个维度,加上有限的值hh,通过损失值变化,计算函数在该维度上的偏导数。

实际应用   我们仔细上面导数求解的公式,会发现数学定义上hh是要趋于0的,但实际我们计算的时候我们只要取一个足够小的数(比如e−5e^{-5})作为h就行了,所以我们要精准计算偏导的话,要尽量取到不会带来数值计算问题,同时又能很小的hh。

  另外,实际计算中,另外一个公式用得更多,即中心差值公式

[f(x+h)−f(x−h)]/2h

[f(x+h) - f(x-h)] / 2 h

  计算到的梯度,准确地说,梯度的方向是函数增大方向,负梯度才是下降方向,因为我们希望损失函数降低,所以要沿着梯度负方向进行更新。

步长的影响:梯度指出了函数变化率最大的方向,但没说明在这个方向上应该走多远。选择步长(也叫作学习率)将会是神经网络训练中最重要(也是最头痛)的超参数设定之一。如果步长很小,那么梯度下降地会很慢,很慢;而如果步长过大,可能越过最优点,从而导致更高的损失值。

效率问题:这个计算方法的复杂度,基本是和我们的参数个数成线性关系的。在我们的CIFAR-10例子中,我们总共有30730个参数,因此我们单次迭代总共就需要计算30731次损失函数。这个问题在之后会提到的神经网络中更为严重,很可能两层神经元之间就有百万级别的参数权重。

3.2 解析梯度

  从上面数值梯度公式我们就很容易看出,计算的梯度实际上是一个近似(hh无法极限逼近0),同时该算法效率低,耗时。   而解析法可以让我们直接得到梯度的一个公式,但是这种方法更容易出现错误。 因此,我们可以通过数值梯度进行梯度检查,具体来说,先分别计算解析梯度和数值梯度,然后比对结果,以此来保证解析梯度的正确性

  我们用一个样本点的SVM损失函数举例说明:

Li=∑j≠yi[max(0,wTjxi−wTyixi+Δ)]

L_i = \sum_{j\neq y_i} \left[ \max(0, w_j^Tx_i - w_{y_i}^Tx_i + \Delta) \right]

  我们可以求它对每个权重的偏导数,比如说,我们求它对wyiw_{y_i}的偏导,我们得到:

∇wyiLi=−⎛⎝∑j≠yi1(wTjxi−wTyixi+Δ>0)⎞⎠xi

\nabla_{w_{y_i}} L_i = - \left( \sum_{j\neq y_i} 1(w_j^Tx_i - w_{y_i}^Tx_i + \Delta > 0) \right) x_i

  其中11是一个bool值,在括号内的条件为真的时候取值为1,否则为0。看起来似乎很吓人,但实际上要写代码完成的话,你只需要计算不满足指定SVM最小距离的类(对损失函数有贡献的类)的个数,然后用这个值会对数据向量xix_i做缩放即可得到梯度。但是要注意只是WW中对应正确的类别的列的梯度。对于其他的j≠yij≠y_i的情况,梯度为:

∇wjLi=1(wTjxi−wTyixi+Δ>0)xi

\nabla_{w_j} L_i = 1(w_j^Tx_i - w_{y_i}^Tx_i + \Delta > 0) x_i

  一旦得到梯度的表达式,那计算梯度和调整权重就变得非常直接和简单。熟练掌握如何在损失函数(loss expression)下计算梯度是非常重要的一个技巧,贯穿整个神经网络的训练实现过程。

4 梯度下降

  在计算得到梯度之后,使用梯度去更新已有权重参数的过程叫做梯度下降,伪代码如下:

代码语言:javascript
复制
while True:
  weights_grad = evaluate_gradient(loss_fun, data, weights)
  weights += - step_size * weights_grad # 梯度下降更新参数

  这个简单的循环存在于所有的神经网络核心库中。虽然也有其他实现最优化的方法(比如LBFGSLBFGS),但是到目前为止,梯度下降是对神经网络的损失函数最优化中最常用的方法。其实所有方式核心思想不变,那就是我们一直跟着梯度走,直到结果不再变化为止。

4.1 小批量数据梯度下降Mini-batch gradient descent

  在大型的应用当中(比如ILSVRC),训练数据可能是百万千万级别的。因此,对整个训练数据集的样本都算一遍损失函数,以完成参数迭代是一件非常耗时的事情。   我们通常会用到的替代方法是,采样出一个子集在其上计算梯度。现在比较前沿的神经网络结构基本都是这么做的,例如ConvNets是每256张作为一个batch去完成参数的更新。参数更新的代码如下:

代码语言:javascript
复制
while True:
  data_batch = sample_training_data(data, 256) # 抽样256个样本作为一个batch
  weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
  weights += - step_size * weights_grad # 参数更新

  之所以可以这么做,是因为训练数据之间其实是关联的。可以想象一个极端情况:在ILSVRC中的120万个图像是1000张不同图片的复制(每个类别1张图片,每张图片有1200张复制)。那么显然计算这1200张复制图像的梯度就应该是一样的。对比120万张图片的数据损失的均值与只计算1000张的子集的数据损失均值时,结果应该是一样的。实际情况中,数据集肯定不会包含重复图像,那么小批量数据的梯度就是对整个数据集梯度的一个近似。因此,在实践中通过计算小批量数据的梯度可以实现更快速地收敛,并以此来进行更频繁的参数更新。

  上述算法的一个极端的情况是,如果我们的一个mini-batch里面只有一张图片。那这个过程就变成随机梯度下降/Stochastic Gradient Descent (SGD),说起来,这个其实在实际应用中倒也没那么常见,原因是向量化之后,一次计算100张图片,其实比计算一张图片100次,要快得多。即使SGD在技术上是指每次使用1个数据来计算梯度,你还是会听到人们使用SGD来指代小批量数据梯度下降(或用MGD来指代小批量数据梯度下降,而BGD来指代则相对少见)。

  小批量数据的大小是一个超参数,但是一般并不需要通过交叉验证来调参。它一般由存储器的限制来决定的,或者干脆设置为同样大小,比如32,64,128等。之所以使用2的指数,是因为在实际中许多向量化操作实现的时候,如果输入数据量是2的倍数,那么运算更快。


参考资料

链接:http://cs231n.github.io/optimization-1/ 链接:https://zhuanlan.zhihu.com/p/21360434?refer=intelligentunit https://zhuanlan.zhihu.com/p/21387326?refer=intelligentunit 链接:http://blog.csdn.net/han_xiaoyang/article/details/50178505

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年11月25日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 最优化
    • 1 损失函数可视化
      • 2 最优化策略
        • 2.1 随机搜索
        • 2.2 随机局部搜索
        • 2.3 跟随梯度
      • 3 梯度计算
        • 3.1 数值梯度
        • 3.2 解析梯度
      • 4 梯度下降
        • 4.1 小批量数据梯度下降Mini-batch gradient descent
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档