计算机视觉教程核心版(三)优化上篇

目录

优化介绍

可视化损失函数

优化方法

计算梯度和梯度下降

总结

优化介绍

前面两节主要介绍了图像分类任务的两个关键成分

  • 得分函数:将原生像素映射到类得分
  • 损失函数:基于产生的得分与训练数据集真是标签的匹配程度衡量参数集合的质量。(例如softmax和SVM)。

我们从前面已经看到,预测越精准的参数集同样有着更低的损失。我们将引进第三个也是最后一个关键组成成分:

优化。优化的过程就是寻找一组参数集,使得损失函数最小的过程。

铺垫:一旦理解了这三个主要成分是怎样联系的,我们将回顾第一个成分(参数化的得分函数)

可视化损失函数

举例:

回归算法损失

l2loss

l1loss

分类算法损失

Cross entropy Loss

Sigmoid cross entropy loss

画图source code

下图表示多类SVM损失函数图,左边表示变量仅为一个。中间和右边表示两个变量。蓝色部分表示损失最小,红色表示损失最大。右边碗图表示数据经过标准化后损失函数的样子

标准化后更容易优化,如下图所示,标准化输入之后,参数更容易到达最小值点。

优化方法

损失函数可以让我们量化任何特定权重集W的质量。优化的目标寻找使得损失函数最小的参数集W。

计算梯度和梯度下降

  • 方案1:一个非常不好的寻找参数集的方法shi 随即搜索。我们第一个脑海中想到的方法大概就是,随机测试许多组不同的权总,并记录哪一组权重效果最好。流程可能如下
# assume X_train is the data where each column is an example (e.g. 3073 x 50,000)
# assume Y_train are the labels (e.g. 1D array of 50,000)
# assume the function L evaluates the loss function

bestloss = float("inf") # Python assigns the highest possible float value
for num in xrange(1000):
  W = np.random.randn(10, 3073) * 0.0001 # generate random parameters
  loss = L(X_train, Y_train, W) # get the loss over the entire training set
  if loss < bestloss: # keep track of the best solution
    bestloss = loss
    bestW = W
  print 'in attempt %d the loss was %f, best %f' % (num, loss, bestloss)

# prints:
# in attempt 0 the loss was 9.401632, best 9.401632
# in attempt 1 the loss was 8.959668, best 8.959668
# in attempt 2 the loss was 9.044034, best 8.959668
# in attempt 3 the loss was 9.278948, best 8.959668
# in attempt 4 the loss was 8.857370, best 8.857370
# in attempt 5 the loss was 8.943151, best 8.857370
# in attempt 6 the loss was 8.605604, best 8.605604
# ... (trunctated: continues for 1000 lines)

这种方法大约给出15.5%的准确率。

方案2:随机局部寻找。

  • 核心思想:迭代细化。我们可以通过其他方法使得效果更好。核心的思想是,如果直接寻找一个最佳权重集较为困难,甚至是不可能的,但是如果将问题转化为精炼一个具体的权重集W,问题将变得容易很多。另一种表述是,我们的方法开始于一个随机的权重W,之后迭代细化它,使得每次迭代它都变得更好一些,即获得更低的损失。

对于这种方式你能想到的第一种方式可能是将权重沿着随机的一个方向进行改动,如果损失变小,则权重更新。代码流程大致如下:

W = np.random.randn(10, 3073) * 0.001
bestloss = float('inf')
for i in xrange(1000):
    step_size = 0.0001
    Wtry = W + np.random.randn(10, 3073) * step_size
    loss = L(Xtr_cols, Ytr, wtry)
    if loss < bestloss:
        w = Wtry
        bestloss = loss
    print 'iter %d loss is %f' % (i, bestloss)

使用同样的损失函数评估,这种方法实现的准确率大约是21.4%。这个效果好一些,但是依然浪费时间且计算代价昂贵。

  • 方案3:跟随着梯度。在之前的部分我们尝试在权重空间中寻找一个方向,沿着这个方向更新权重将使得损失变得更小。事实上,我们没有必要去随机寻找一个我们认为好的方向。取而代之的是,我们可以计算更新权重的最佳方向,这方向在数学理论上是最快下降的方向。这个方向与损失函数的梯度有关。这类比于在山坡旅行中,我们感受我们脚下删的坡度,并走下最陡的方向。梯度为沿着每一个维度的偏导数向量。
  •  

用有限差分数值计算梯度

上面给出的公式允许我们以数字方式计算梯度。

def f(x):
    return 2 * x+3
x = np.arange(6).reshape(2,3)

grad = np.zeros(x.shape)
h = 1

def eval_numerical_gradient(f, x):
    '''
    a naive implement of numberical 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

    '''
    # 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
        fx = f(x[ix])
        old_value = x[ix]
        x[ix] = old_value + h
        fxh = f(x[ix])
        x[ix] = old_value
        grad[ix] = (fxh - fx) / h
        it.iternext()
    return grad
    
print(grad)

        

实际考虑

在数学公式中,梯度是在h趋近于零的时候计算,但在实际中,使用一个非常小的值经常已经足够使用了例如1e-5。理想的,你希望用尽可能小的步长,因此这将不会导致数字问题。另外,实际中经常就是用中心差公式计算数字梯度。[f(x+h)-f(x-h)]/2h

我们可以计算任意函数在任一点的梯度。

# to use the generic code above we want a function that takes a single argument
# (the weights in our case) so we close over X_train and Y_train
def CIFAR10_loss_fun(W):
  return L(X_train, Y_train, W)

W = np.random.rand(10, 3073) * 0.001 # random weight vector
df = eval_numerical_gradient(CIFAR10_loss_fun, W) # get the gradient

梯度显示了损失函数沿着每一个维度的坡度,我们可以利用这个坡度更新:

loss_original = CIFAR10_loss_fun(W) # the original loss
print 'original loss: %f' % (loss_original, )

# lets see the effect of multiple step sizes
for step_size_log in [-10, -9, -8, -7, -6, -5,-4,-3,-2,-1]:
  step_size = 10 ** step_size_log
  W_new = W - step_size * df # new position in the weight space
  loss_new = CIFAR10_loss_fun(W_new)
  print 'for step size %f new loss: %f' % (step_size, loss_new)

在梯度的负方向更新

在上例代码中,注意到我们在计算w_new时候,我们在梯度的负方向进行更新而不是正方向,因为我们想要损失函数减小而不是增加。

步数大小的影响

梯度告诉我们损失函数最陡峭的方向,但它并没有告诉我们沿着这个方向我们应该走多远。选择合适的步长是深度学习中训练一个神经网络最重要的事之一。

效率问题

在上例中,评估数值梯度的参数数量具有线性复杂性。假设由30730个参数,则需要执行30731次评估,进而执行一个参数的更新。问题只会变得更糟,因为神经网络的很容易参数很容易达到百万千万级别。显然这种方法不可拓展,我们需要更好的。

×××××××××××××××××××××××××

用微积分分析计算梯度

数字梯度使用有线差分近似来进行简单地计算。但是,缺点是它是近似的。(因为我们必须选取一个较小的h值,然而真实的梯度定义中,h是趋近于零),并且这也非常耗费计算量。第二种计算题都所用的方法是,使用微积分分析,它能使我们直接得到计算梯度的公式,并且这种方法也非常快速。但是不想数字梯度那样,这种方法更容易执行时候出错,这就是为什么在实际中经常计算完微分梯度之后,将它与数字梯度进行比较来确定执行的正确性。这称为梯度检验。

梯度下降

既然我们使用损失函数计算题都,重复评估执行参数更新的过程叫做梯度下降。

它的香草模型:

# Vanilla Gradient Descent

while True:
  weights_grad = evaluate_gradient(loss_fun, data, weights)
  weights += - step_size * weights_grad # perform parameter update

还有其他方式执行优化,例如LBFGS,但是梯度下降是目前为止最普遍的方式

在整个优化中,我们将加入一些技巧,使得优化表现更加。

Mini-batch gradient descent

在大型的训练数据集下,直接计算所有数据的损失函数来进行一次参数更新,这看起来非常浪费资源。一个较为常用的办法是在训练数据不同的批次上计算梯度,然后执行参数更新。例如ConvNets,从1200000抽取256个样本作为一个批次。这个批次之后用于一次参数更新。香草最小批次更新代码

# Vanilla Minibatch Gradient Descent

while True:
  data_batch = sample_training_data(data, 256) # sample 256 examples
  weights_grad = evaluate_gradient(loss_fun, data_batch, weights)
  weights += - step_size * weights_grad # perform parameter update

这种方法效果良好,因为训练集数据都是相关的。

最小批次法的极限情况是,随机梯度下降法(SGD)。这种方法更少用到,在实际中计算100个样本的梯度比计算100个梯度更加有效。虽然SGD技术是一次使用一个样本求值梯度,但是你会听到SGD指代最小批次梯度下降法。梯度下降法最小批次的大小是一个超参数,但它并不常用交叉验证。它常受内存限制,或者设置为例如32,、64或者128.我们一般使用2的n(1,2,3...)次方,经验来看这样快一些。

总结

流程总结。假设固定(x, y)对数据集。得分函数权重随机初始化并且可以改变。在前向传播的过程中计算类别得分,并存储在向量f中。损失函数包含两个成分:数据损失计算得分f和标签y的匹配性。正则化损失只是一个权重的函数。在梯度下降过程中,我们在权重的基础上计算梯度并使用它们执行参数更新。

本节主要内容:

  • 我们通过迭代精炼来实现损失函数优化的想法,即我们开始随机初始化权重,之后一步一步精炼参数直到损失函数最小。
  • 我们看到了一个函数的梯度是函数上升最快的方向。我们讨论了一个简单但是低效的方法来计算梯度,即有限差分估计。(有限差分指的是用于计算数值梯度的h的值)
  • 我们看到了,参数更新要求棘手的step size(learning late)设置。step size必须设置正好。如果太小,则进展虽稳健但是缓慢。如果太大,进展可能很快但是冒险。这涉及权衡问题,稍后待续。
  • 我们讨论了数值梯度和分析梯度之间的权衡问题。数值梯度简单但是仅仅近似,而且耗费大量计算量。分析梯度准确、快速,单丝更加容易出错,因为它要求数学推导出梯度。因此,实际中我们常在较小的样本中,先使用分析梯度再执行一下梯度检测,即分析梯度的结果和数值梯度比较,以此进行梯度检验。
  • 我们介绍了梯度下降算法,它迭代地计算梯度且每循环以此,执行一次参数更新。

下节预告:

本节核心内容是,根据损失函数权重计算它的梯度。其需要我们设计、训练并且理解神经网络。在下一节,我们将进一步探讨使用连式法则进行分析梯度的计算,或者也成为反向传播。这将允许我们有效地优化相关的任意损失函数(这些损失函数可以使表达任意的神经网络,包含卷及神经网络。)

猜你喜欢

转载自blog.csdn.net/tianzhiya121/article/details/89762169