Lecture3 损失函数和优化损失函数

本文介绍了损失函数在机器学习中的作用,特别是多分类SVM和Softmax分类器的损失函数。SVM损失函数通过间隔确保正确类别的得分高于其他类别,而Softmax则将得分转换为概率分布。正则化用于避免过拟合,L2正则化控制权重大小。文章还讨论了随机搜索和梯度下降等优化方法,以及它们在训练过程中的应用。最后,介绍了小批量梯度下降作为优化策略的效率提升。
摘要由CSDN通过智能技术生成

1. 损失函数

        通过设置评估函数的参数W ,我们希望分类器预测类别的得分和真实值接近。但是,有时事实不尽人意,比如我们预测猫的图片,但是分类器给出猫的评分可能为一个很小的负数,然后其他种类的得分非常大,这显然不是我们想要的。那么我们需要一个函数来量化我们对分类器的预测结果的不满意度,即损失函数(或代价函数)。直观来讲,当分类器的效果很好时,损失值将会很低;当分类器的效果很差时,损失值将会很高。

1.1 多分类SVM的损失函数

        损失函数的定义方式有很多,先来介绍一种常用的损失函数——多分类SVM的损失函数。

        多分类SVM的损失函数希望每个图片对应的真实样本的预测得分比其他类别的得分高出一个间隔。我们定义第i个样本xi的第j个类别的预测得分为。那么多分类SVM的代价函数如下:

        其中sj表示第 j个类别的得分,syi表示第 i个样本的真实标签yi的预测得分, Δ 表示间隔。举例来说,假设我们有三个类别,并得到某个样本的预测得分为[13,-7,11],假设该样本的真实标签为yi=0,假设∆(这是一个超参数,人为设定),那么我们可以得到损失值:

        对于第一个类别的预测得分13,不会在损失函数中计算,因为这是真实种类对应的预测得分。接着是第二个类别的预测得分-7,由于该种类的预测得分远小于真实种类的预测得分(13),所以通过与 0 取max的方式max(0,-7-13+10)=0,得到其损失值为0。然后是第三个类别的预测得分,虽然也比真实预测得分低,但是它和真实类别的预测得分的差小于间隔 Δ =10,所以得到它的损失值为 8。综上所述,多分类SVM的损失函数希望其他种类的预测得分比真实种类的预测得分相差一个 Δ 的间隔,如果不满足则会累计误差,可以用下图形象化地表现:

        此外还有一点需要说明的,在多分类SVM的损失函数中使用的max(0,-)也称为铰链函数,因为它的图像很像一个铰链。有时SVM也会用到平方铰链函数,即max(0,-)²,这时SVM对那些不满足要求的损失值给予更大的惩罚(即对于在间隔内的点)。

1.2 正则化(Regularization)

        不过对于上述的损失函数,还有一个漏洞。设想一下,我们有一个参数为W的分类器可以正确分类数据集中的所有数据,也就是说对于全部数据Li=0,问题在于,这里的W是不唯一的,也就是说可能存在多个不同的参数W都可以使得Li=0。举一个最简单的例子,如果使用参数W的分类器在某个数据集下的Li=0,那么使用参数λW,λ>1的分类器得到的Li也都为0。因为将参数都乘上λ ,只会使得每个种类的预测得分乘上λ,那么sj-syi也会乘上λ(变量含义见前文),但是损失值已经为0,说明,所以,损失函数值依旧为0。

        换句话说,我们希望有一种方式可以采取一种合适的偏好来选择一种权重集合来消除这种歧义。我们只需在损失函数后增加一个正则项 R(W) 即可实现,最常用的正则项为平方L2正则化,它通过在损失函数中引入权重的平方和来阻碍模型训练时选择大的权重,具体表达式如下:

        从式子中可以看出,正则化只是对权重进行的,与训练数据无关,加上正则项后,多分类SVM的损失函数变为:

         前半部分表示由于预测样本导致的损失值,后半部分表示权重的损失值,其中λ是正则化系数,也属于超参数,可以用过交叉验证来选取。不同的正则项有不同的特性,比如L2正则项在SVM中使用时,会使得模型偏向于选择使间隔最大的一种权重。最重要的是,正则项可以有效地抑制较大权重的产生,以此来提高泛化能力减小过拟合现象,因为较小的权重意味着输入数据的大小对评估函数的结果不会有太大的影响。

最后需要说明的是,不同于权重,偏置项不会影响输入数据对评估函数输出的影响,所以一般不会对偏置项进行正则化,不过其实在实际应用中,是否对偏置项正则化影响不大。另外,引入正则项后,损失函数永远不可能为0,除非将权值全设为0,但这是很不理智的一件事。

1.3 Δ 的设置

        从上面的损失函数可以知道,除了权重以外,还有两个超参数 Δ 和λ需要我们手动设置,虽然可以采用交叉验证的方式,但是其实我们对于任何情况,都可以将 Δ 设置为1。Δ 和λ看似是两个不同的超参数,但其实他们有同样的效果:用于平衡数据损失值和正则化损失值。正则项影响的是权重W的量级,权重的量级又影响这评估函数的结果:权重量级越大,那么不同种类之间的评估得分相差也就越大,反之相差也就越小,所以正则项控制着评估得分之间的差异,而正则化的强弱是由λ控制的,通过改变λ可以任意放大或者缩小这种差异。于是乎, Δ 数值的大小也就变得没有那么重要了,因为我们可以通过λ随意控制评估得分差异的大小。因此,我们需要考虑的只有我们允许权值的大小是多少(通过控制正则项系数λ)。

2. Softmax 分类器

        除了多分类SVM分类器以外,还有一种常见的 Softmax 分类器,Softmax分类器其实就是二元逻辑回归分类器在多分类问题上的延伸。不同于 SVM 将f(xi,W)直接作为每个类别的评估得分,Softmax 分类器使用了一种更为直观的输出,并且有它的概率意义。Softmax 的损失函数中首先也是计算出 f(xi,W)=Wxi ,然后使用除法归一化,最后套上log作为损失函数,其中这里的损失函数也称交叉熵损失,公式如下:

        上式中的fj表示f(xi,W)的计算结果中的第j个元素,fyi 表示第i个数据的真实样本对应的值,上式左边和右边是等价的表达方式。可以看出,Softmax的损失函数首先经过归一化,经过这样的处理后,每个类别的评估值都在[0,1]之间,并且全部类别的评估值之和为1,变成一个概率分布。转变为一个概率分布后,然后套上一个-log()就是最终的损失函数了,实际上就是一个交叉熵损失。

3. SVM和Softmax对比

         上图展示了SVM和Softmax的区别,对于一个输入的数据,两个分类器首先都是计算相同的得分f,但是两者对于得分f 的解释不同。对于SVM分类器,它将f就当做类别的得分,所以它的损失函数时希望正确类别的得分比其他类别高出一个间隔的差距。而Softmax分类器则将f处理成对数概率,其损失函数希望归一化后的真实类别的概率尽可能地大(可以从交叉熵和极大似然估计的角度分别理解)。从上图中可以看出SVM的损失值为1.58,Softmax的交叉熵损失为1.04,但这两者是没有可比性的,对于一个损失函数的值只能和相同的损失函数进行比较。

        SVM和Softmax的性能差距很小,不过还是有一些差异的。相比于Softmax,SVM更加,这可以看做是SVM的缺陷也可以看做是SVM的特点,比如说某个样本的预测得分为[10,-2,3],假设第一个类别是其真实类别,并且SVM 的 Δ=1 ,那么很显然这个预测得分的损失为0。SVM是不关注某个种类的具体得分的,比如说如果预测得分分别为 [10,-100,-100] [10,9,9] ,在Δ=1的情况下,损失值都为0。所以,只要满足真实种类的评估得分比其余都大一个规定的间隔,那么SVM会无视具体的差异的,但是对于Softmax则不同,在Softmax分类器中,[10,-100,-100]的损失值肯定比[10,9,9]小,换句话说,Softmax希望的是真实种类的概率尽量大,而其他的尽量小。但是在某种情况下,这也不一定就是SVM的缺陷,比如说某个汽车分类器专注于对卡车和汽车的分类,那么模型的预测得分不应该受到其他种类比如青蛙的影响,只要青蛙的得分保证一定程度地低就够了。

4. 损失函数可视化

        损失函数通常会被定义在一个很高纬度的空间中(比如CIFAR-10数据集下的线性分类器,参数矩阵的大小为[10*3073] ,共30730个参数),这就导致损失函数很难可视化。不过,我们可以通过切片的方式,从高位空间中获取1维或者2维的可视化效果。比如说,我们可以随机生成一个参数矩阵W(表示高维空间中的一个点),然后沿着一条射线在高维空间中行进,记录沿途的损失值。也就是说,我们可以生成一个随机的方向向量 W1,然后计算这个方向上的损失函数,通过改变函数L(W+aW1)中的a 。于是,我们就可以画出一个图像,其中x轴表示a的值,y轴表示损失函数值。同理,我们可以通过同样的方法,选择两个方向向量W1和W2,然后计算L(W+aW1+bW2)的值,画出一个图像,其x轴表示a,y轴表示b,图像的颜色表示损失值的高低。

        如下图所示是SVM损失函数的可视化结果,左图表示沿一个方向的可视化结果,y轴表示损失值的高低。中图和右图表示选择两个方向的可视化效果,颜色表示损失值的高低(蓝色表示损失值低,红色表示损失值高)。其中,中间的图表示对于一个样本的损失值可视化结果,右图表示对于100个样本取平均的可视化结果。从图中我们可以看出,损失函数是一个分段线性的结构,而右图之所以是圆滑的碗状是因为这是很多个分段线性结构取平均后的结果。

         我们可以分析一下为什么损失函数是一个分段线性的结构,对于某个样本数据xi ,其损失函数如下:

         即,损失值是多项之和,每一项的值都独立于特定的权重参数,或者是其线性函数(负数时函数值为0),如下图所示,就是对于单个权重的损失函数图像,x轴表示权值,y轴表示损失值,求和的效果就是一个分段的线性结构。而对于一个完整的SVM数据损失值(在CIFAR-10数据集上),就是一个30730维度的版本。

         从损失函数的碗状图像可以猜到,SVM的损失图像是一个凸函数,凸函数的优化方法有很多种。但是当我们将评价函数拓展到神经网络中后,其可视化图像就变成一个复杂,凹凸不平的形状了。

5. 优化器

        再次说明,损失函数使得我们能够量化任意一组权值的质量。优化器的目标是找到一组权重,使得最小化损失函数值。虽然我们后面介绍优化器时使用的例子是一个凸优化问题(SVM损失函数),但需要注意的是,我们的最终目标是找到一个优化方法可以用于神经网络(神经网络中很难使用凸优化的技巧)。

5.1 随机搜索

        由于验证某个参数是否足够优秀是很方便的一件事情,所以第一种很容易想到的策略就是我们随机生成一些权重,然后量化其好坏,保留最优的。代码如下:

        # 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 range(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)   

        从代码的输出结果可以看到,有一些随机的权重W比其他的好,于是将其保留下来,我们将最好的权重在测试集中进行验证:

        # Assume X_test is [3073 x 10000], Y_test [10000 x 1]
        scores = Wbest.dot(Xte_cols) # 10 x 10000, the class scores for all test examples
        # find the index with max score in each column (the predicted class)
        Yte_predict = np.argmax(scores, axis = 0)
        # and calculate accuracy (fraction of predictions that are correct)
        np.mean(Yte_predict == Yte)
        # returns 0.1555

        得到其准确率为15.5%,随机猜测图片属于哪种类别的准确率为10%,可以发现这种无脑随机的搜索方法比胡乱猜测是要好的。

        虽然这是一种很愚蠢的优化策略,但其中也有一个重要的思想:迭代寻优

        直接寻找一个最优解是基本不可能的一件事情,但是如果我们首先随机选取一个初始值,然后逐步地去优化,使得权重每次都稍微进步一点,这样最终就会逼近最优解。随机搜索就像是一个人蒙住双眼在一个山地中,企图走到山谷最低处。

5.2 随机局部搜索

        此外,我们可以在局部采用随机搜索的方式,具体来说就是我们从一个随机的W开始,然后随机生成一个扰动δW,然后如果W+δW的值更小,那么我们就更新。这就好像在下山的过程中,随机向某个方向迈出一脚,如果有向下的趋势,那么就往下走,否则就不动。代码如下:

        W = np.random.randn(10, 3073) * 0.001 # generate random starting W
        bestloss = float("inf")
        for i in range(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%,这有所改进,但是对于性能的浪费是巨大的。

5.3 跟随梯度

        上一种方法中,我们使用随机搜索的方法去寻找可能的优化方向,但是其实我们有很好的方法去寻找最优的前进方向,我们可以直接计算出当前最优的方向。即计算出一个向量,保证在数学上朝着这个方向是当前下降最快的。这一向量称作损失函数的梯度。类比下山,跟随梯度下降的方法就类似我们通过感受我们脚下地势的斜率,然后朝着下降最快的方向前进一样。对于一个一维的函数,梯度就是其斜率,而对于一个多维的函数,梯度是一个向量,每一维度就是对应属性在多维函数中的偏导数。

6. 计算梯度

有两种计算梯度的方法:

  • 数值梯度:比较慢,且是一个近似值,但是比较简单
  • 解析梯度:快速精确但是由于需要推导分析,所以容易出错

6.1 使用有限差分计算梯度

        对于一个一维的函数,梯度计算公式为:

         使用这个公式,我们就可以用有限差分的方法计算梯度了,具体的方法就是选取一个微小的h,然后计算 ,作为梯度,对于一个多维函数,对每一维度都进行这样的操作即可。函数代码如下:

         其中 f 表示需要计算梯度的函数, x 表示输入的向量。

        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) # evalute 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

        在代码中通过微小的改变 h ,我们可以逐一计算出每一维度的梯度,最终返回梯度向量 grad 。

· 实际应用中需要注意的问题:

        在梯度的数学公式中,h是无限趋近于0的,而在实际使用中,我们通常使用一个足够小的数字来代替(通常为1e-5),这样不会导致数值上的问题。此外,在实际应用中,使用中心差分公式的效果更好,公式为

        使用上文的梯度计算公式,我们可以计算任意一点在任意一个函数中的梯度,于是我们可以使用该函数来计算在CIFAR-10中某些随机点关于损失函数的梯度。代码如下:

        # 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)
 
        # prints:
        # original loss: 2.200718
        # for step size 1.000000e-10 new loss: 2.200652
        # for step size 1.000000e-09 new loss: 2.200057
        # for step size 1.000000e-08 new loss: 2.194116
        # for step size 1.000000e-07 new loss: 2.135493
        # for step size 1.000000e-06 new loss: 1.647802
        # for step size 1.000000e-05 new loss: 2.844355
        # for step size 1.000000e-04 new loss: 25.558142
        # for step size 1.000000e-03 new loss: 254.086573
        # for step size 1.000000e-02 new loss: 2539.370888
        # for step size 1.000000e-01 new loss: 25392.214036

        梯度告诉了我们下降最快的方向,但是没有告诉我们需要朝这个方向前进多少,上文代码中 step_size 表示的就是一次更新所前进的步长,也称之为学习率。如果 step_size 太小,那么每次进步的幅度就太小,而如果太大,从代码的输出结果也可以看出,损失值不降反增,这就是前进过头了。

· 效率问题:

         使用有限差分的数值梯度有一个问题就是对于每个参数每次迭代都需要计算一次梯度,如果参数非常多那么训练效率就会非常低,很显然我们需要一种更好的策略。

6.2 用微积分来计算梯度

        数值梯度使用有限差近似非常易于计算,但缺点是它是近似值(因为我们必须选择较小的h值,而真实梯度则将其定义为无限趋近于0),而且这种方式非常浪费计算性能。第二种计算梯度的方法是使用微积分进行分析,这使我们能够为梯度得出一个直接的公式,该公式也非常快地计算。但是,与数值梯度不同,它由于需要复杂的推导,所以更容易出现错误,这就是为什么在实践中计算分析梯度并将其与数值梯度进行比较以检查实现的正确性是非常普遍的。这称为梯度检查。

        使用单个数据点的SVM损失函数为例:

         我们可以给出wyi的梯度:        

        即所有超出安全间隔的种类的个数乘上输入的  ,同时给出其他权值的梯度:

        观察可以发现真实标签对应的权重和其他权重的梯度是不同的,其本质就是求偏导, 在损失函数的求和中每一项都包含wyi,求偏导是将wyi看做自变量,所以需要进行一下求和。而对于其他的权重,在求和中值出现在其中的某一项,所以偏导数不需要求和。

7. 梯度下降法

        现在,我们可以计算损失函数的梯度,重复评估梯度然后执行参数更新的过程称为梯度下降。原始版本的梯度下降如下所示:

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

        这个简单的循环是所有神经网络的核心,虽然还有很多其他优化的方法,但是梯度下降是目前最常用的方法。后面的课程中,随着神经网络的介绍,会在上述的循环中增加一些东西,但是跟随梯度变化的想法一直保持。

· 小批量梯度下降(mini-batch gradient descent):

        当梯度下降应用在大尺度的任务中时,训练集可能有上百万个样本,因此计算损失函数同时计算出梯度并更新的操作需要针对整个训练集,这是十分浪费并且有时候是无法实现的(如果内存太小就会存不下所有样本)。一个常用的方法是将训练集随机划分为多个小批次,每次只对这一个批次的样本进行计算损失值以及梯度,然后用这个梯度进行更新参数,代码如下:

        # 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

        这样的做法之所以可行是因为,训练集中的样本之间是有一定的关联性的。通过评估小批度样本的梯度以执行更频繁的参数更新,可以在实践中实现更快的收敛速度。有一种极端情况就是,一个小批度只包含一个样本,这种方法称为随机梯度下降法Stochastic Gradient Descent (SGD),或者有时候也称为在线梯度下降法。值得注意的是,很多情况下虽然人们使用SGD这个词,但实际他们指代的是小批量梯度下降(mini-batch gradient descent)。每个批度的大小也是个超参数,但一般不会去进行交叉验证,这通常取决于内存的大小,或者将其设置成 2 的幂,这是由于使用2的幂会加快计算机矢量处理的速度。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SVM(支持向量机)的损失函数优化实现是通过最大化边际来实现的。SVM的损失函数是基于Hinge Loss的,它衡量了正确分类和错误分类之间的边际。具体来说,对于每个样本,SVM会计算其真实分类的分数和其他可能分类的分数之间的差距。如果这个差距小于一个预先设定的边际(通常为1),则认为这个样本被正确分类。如果差距大于边际,则认为这个样本被错误分类。 SVM的优化目标是找到一个能够最大化边际的超平面,使得正确分类的样本尽可能远离超平面,而错误分类的样本尽可能接近或超过边际。这可以通过求解一个凸优化问题来实现,其中目标是最小化损失函数和正则化项的和。正则化项用于控制模型的复杂度,以防止过拟合。 在优化过程中,可以使用梯度下降等优化算法来更新模型的参数,使得损失函数逐渐减小。通过迭代更新参数,最终可以得到一个能够最大化边际的超平面,从而实现对样本的分类。 总结起来,SVM的损失函数是基于Hinge Loss的,通过最大化边际来实现对样本的分类。优化过程中使用梯度下降等算法来更新模型参数,使得损失函数逐渐减小,最终得到一个能够最大化边际的超平面。\[1\]\[2\] #### 引用[.reference_title] - *1* *2* *3* [Lecture2:损失函数优化](https://blog.csdn.net/qq_41694024/article/details/128208185)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值