目录
1. Introduction
2. 可视化Loss function
3. 优化
3.1 Strategy#1: 随机搜索
3.2 Strategy#2: 随机局部搜索
3.3 Strategy#3: 沿着梯度方向搜索
4. 梯度计算
4.1 数值法
4.2 分析法
5. 梯度下降
6. 总结
上一节学习了图像分类中的两个重要概念:score function和loss function。
线性函数, SVM损失为:
公式(1.1) |
接下来我们即将介绍最后一个重要概念:优化(optimization)。优化就是寻找使损失函数最小的最优参数的过程。
铺垫:当我们了解这三个重要概念之间的关系之后,我们就可以回顾前面介绍的score function,将其从线性映射扩展到更复杂的映射函数:神经网络,卷积神经网络。
损失函数通常都定义在高维空间,很难进行可视化。但我们可以在高维空间上的切一条线或一个平面来获得一些直观的理解。例如:我们可以随机生成一个权重矩阵,对应于空间中的一个单独的点。
然后,我们随机生成一个方向向量,沿着这个方向计算不同的下的损失函数。该过程可以用下图(2.1)来表示,其中横坐标代表的值,纵坐标代表损失函数的值。
类似的,我们可以生成两个方向,对应的参数分别为,分别对应横坐标放心和纵坐标方向,用颜色来代表损失函数的大小,红色代表高损失,蓝色代表低损失,然后分析的变化,具体如图(2.1-2)和图(2.1-3):
图(2.1-1) | 图(2.1-2) | 图(2.1-3) |
从SVM loss function定义(见公式(1.1))中可以看出,该损失函数是分段函数。SVM loss只对小于部分的差值起作用,对大于的部分,它为0。
虽然SVM loss function是分段函数,但是它仍是凸函数。优化这类问题,可以参考斯坦福的凸优化。
如果把评分函数拓展到神经网络,loss函数就不是凸函数了。
不可微/导函数:由于max算子的存在,SVM loss function存在不可导的点,因此是不可导函数。但是,我们可以用次梯度subgradient,本节接下来将交叉使用次梯度和梯度。
优化的目的是为了找到一个使得损失函数最小。那如何进行寻找呢?接下来我们将由浅入深,介绍随机搜索、随机局部搜素以及沿着梯度方向搜索三种策略。
从随机的生成一组值,选择一个最优的,具体见code:
bestW = None
bestloss = float("inf") #python中float里面的最大值
for num in xrange(1000):
W = np.random.randn(10,3073)*1e-4 #随机生成参数
loss = L(X_train,y_train,W)
if loss < bestloss:
bestloss = loss
bestW = W
print("in attempt %d the loss was %f"%(num,loss,bestloss))
使用随机搜索,得到的准确率为15.5%,比10%高一点。
每次选择一个随机方向,判断向这个方向移动是否会减少损失函数,若能则更新权重。
W = np.random.randn(10,3073)*1e-3 #随机生成参数
bestloss = float("inf") #python中float里面的最大值
for num in xrange(1000):
step_size = 1e-4
Wtry = W + np.random.randn(10,3073)*step_size #随机生成方向
loss = L(Xtr_cols,ytr,Wtr)
if loss < bestloss:
bestloss = loss
W = Wtr
print("in attempt %d the loss was %f"%(num,loss,bestloss))
使用随机局部搜索,得到的准确率为21.4%。比第一个策略好一点,但是还是浪费计算资源。
策略二的随机本地搜索的目的是在梯度空间找到一个方向,使得loss函数值减少。事实上,我们可以计算loss函数下降最快的方向,这个方向就是梯度的负方向。
在一维空间,梯度就是函数在某一点瞬时变化率。在多维空间,输入变量是向量,梯度就是在各个变量方向的瞬时变化率组成的向量(梯度也叫作导数)。一维空间计算导数公式如下:
公式(3.1) |
当函数输入不是数值而是向量时,我们把导数叫做偏导数(partial derivatives),梯度就是各个维度上的偏导组成的向量。
计算梯度有两种算法:
数值法:较慢,求近似值
分析法:速度快,准确但是容易出错,需要使用到微积分。
数值法求极限有两个常用公式:
公式(4.1) | ||
中心差公式(centered difference formula) | 公式(4.2) |
详细内容,可以参考wiki。通常中心差公式的效果更好。
更新权重时:我们要去减少损失函数而不是增加损失函数。
步长/学习率的影响:梯度告诉我们函数增长的最快方向,但是并没有告诉我们需要沿着这个方向走多远。学习率/步长也是一个超参数,若太小,则收敛速度太慢;若太大,则可能会跨过最优值进而得到更糟糕的损失。在训练过程中,如何设置学习率也是一个关键问题。
数值梯度求到的是近似梯度,因为不能等于零。分析法需要使用微积分,它是计算真正的梯度,而且计算速度非常快。但是在分析梯度的过程中容易出错。因此在实践中,需要经常分析梯度法求得的梯度与数值梯度法求得的梯度进行对比,进而来验证分析梯度的正确性。这个过程叫做梯度检验(gradien check)。
以SVM loss (该公式见(1.1))为例,求某个点上的梯度:
公式(4.3) |
|
公式(4.4) |
其中代表指示函数,如果条件成立则为true,否则为zero。
计算好梯度后,我们就可以用梯度来更新权重参数,这个过程叫做梯度下降。
最基本的梯度下降(Batch Gradient Descent)的方法就是用一个循环,在循环中计算梯度,更新权重,具体见代码:
while True:
weights_grad = evaluate_gradient(loss_function,data,weights)
weights += -step_size * weights_grad
Mini-batch gradient descent
当训练集比较大时,Batch Gradient Descent若每更新一次参数都要基于全部样本来计算梯度,这是非常低效的。
常用的方法是:每次使用训练集中的一批次(batch)样本来计算梯来更新参数。这个方法之所以有效是因为训练集样本之间是相关的。一批次(batch)样本可以看做训练集的估计。
while True:
data_batch = sample_training_data(data,256)
weights_grad = evaluate_gradient(loss_function,data_batch,weights)
weights += -step_size * weights_grad
由于每次更新参数都只需要部分数据,所以可以更频繁的进行参数更新,故而使用mini-batch梯度下降通常可以更快的收敛。
随机梯度下降Stochastic Gradient Descent (SGD)
随机梯度下降是mini-batch梯度下降的一个特例,每次使用的mini-batch只包括一个样本的情况就是SGD。但是在实际应用中比较少见随机梯度下降,因为一次计算多个样本梯度会更加高效。
虽然从定义上来讲只有当mini-batch只包括一个样本的情况下才属于SGD,但实际中的SGD通常被用来指代mini-bach。
MGD指Minibatch Gradient Descent
BGD指Batch Gradient descent。
其中,Mini-batch的大小也是一个超参数,但是它不需要使用交叉验证选择;而是通常根据内存大小来制定,常常使用2的幂:32、64、128,这样更容易实现向量化操作,且运算更快。