cs231n 2023春季课程理解——lecture_2

引言

  这节内容主要讲的是图像分类。图像分类简单来说,就是从一组固定的标签(或者可以说是固定的类别)之中,根据所输入的图像,为该图像分配一个标签的过程。它是计算机视觉中一个非常基础,同时也非常重要的任务。许多计算机视觉的任务,诸如图像分割,目标检测等,都可以在一定程度上简化地看成是图像分类。本节的主要内容为:用线性分类器进行图像分类,其中包括数据处理、邻近算法、K邻近算法、SVM、softmax损失和超参数的选取等。

图像分类的挑战

  首先,我们先介绍一下图像分类。对于我们人类而言,对一张图像进行分类,可以很直观的读取出图像中的语义信息,从而进行分类,这是因为对于我们来说,图像是包含丰富的语义信息的多媒体呈现。但是,对于计算机来说,它却是将其当做是一张巨大的数字网格,因此,它对于一张图像进行分类可能会比较困难。例如,在下图中,在我们眼中这张图像里面有着一直可爱的猫,但是在计算机里面,它确实一些数字。假设这张图像的大小为600*800,那么计算机读取它时,则读取到的实际上是800×600×3=1440000个数字,且每个数字的值均在0-255之间。(×3是因为一张图像一般由红,绿,蓝三种颜色通道组成,即RGB图像,且数字0-255代表着该通道该像素点的颜色深浅,0代表着纯黑,255代表着纯白。)语义鸿沟
  同时,正因为计算机将所有图像全部看成是数字,因此使用计算机来进行图像分类仍旧会遇到许多问题,如:

  1. 拍摄视角的变化:对于同一个物体,可以用摄像机通过不同的角度来进行拍摄。此时,这些不同的角度的照片在计算机中,它们的数字的组成是不同的。
  2. 物体大小的变化:图像中的物体可能大小会不一样(这一点不仅在图像中会出现,现实世界中,观看物体的远近不同,它的大小也会不同,或者物体本身就有些有大小不一样的)。
  3. 形变:许多物体的形状可能不会一样,同种物体之间的形状可能不一样。
  4. 遮挡:在拍摄物体时,可能会被一些遮挡物给遮挡住物体的一部分,这就导致该物体展现不全,甚至可能只能展现出一小部分出来。
  5. 光照条件的变化:对于同一个物体,在同一个地点拍摄,光照不同,最终的结果也会不同。
  6. 背景:有些物体的颜色本身与背景非常类似,导致计算机难以区分(比如说变色龙,可以根据周围环境改变自身颜色)。
  7. 类内差异:同一个类别的物体,它们之间的差异可能也比较大。比如说椅子,它有不同种外形结构。

  下图是一些相关挑战的图像展现:挑战1
挑战2

挑战3
挑战4
挑战5
挑战6

图像分类方法

直接编写函数接口来进行图像分类(hard-code)

  那么,如何使用计算机来对图像进行分类呢,一种最简单,最容易想到的方法就是直接编写一个函数接口(API)来对图像进行分类,如下图。它实际上是很难实现的,因为没有什么很明显的方法可以做到这一点。比如说你正在上算法课,你现在的任务是去对一些数据进行排序,或者去计算一个凸包等,为了实现它们,你可以写下算法并列举所有需要执行的步骤。但当我们去识别物体的时候,是没有什么明显的算法来让你很直观的识别出这些物体。接口编写
  当然了,虽然没有什么明显的方法,但是仍然有人去尝试着写一个算法来识别这些物体,就是通过边缘检测来实现,比如说下图中,我们知道猫有眼睛,嘴巴,耳朵和鼻子,我们也知道了它的边缘。在上一节中我们知道了边缘对于图像识别的重要性,因此我们可以通过寻找不同角度的边缘来识别物体,通过这种方法,可以写下识别物体的算法,但是结果证明,它的效果不是很好,因为你可能之后需要识别的是狗,卡车,鱼等,那么你有需要重新开始,所以这不是一种使用非常广泛的方法。边缘检测

机器学习

  为了寻找一个更好的方法,可以让它区分出不同的类别。这里所寻找到的方法是:找到一个数据集(该数据集包含各种类别的图像以及标签);将它输送给图像之中,让计算机使用机器学习方法来训练这些数据;最后通过训练出来的模型来识别新的图像,以确定这个图像是什么类别。机器学习

最近邻算法(Nearest Neighbor)

  在这里,首先介绍一下最近邻算法,它正是基于此前的想法来实现的。它在整个训练过程中,并不会做任何事情,而只是简单的记住了所有图像的数据以及对应的标签,同时在测试的时候将输入的图像与训练的时候所记住的图像进行比对来找到与之最相似的图像,从而预测出所输入的图像的类别。这个分类算法和卷积神经网络没有任何的关系,并且在实际中也很少应用,但通过它,可以对图像分类有个最基本的认识。最近邻算法
  以上是最近邻的一个基本思想,它首先通过一个训练,来记忆所有的数据和标签。之后,再通过一个新的预测函数(或者可称之为接口)来预测与训练集最相似的标签(虽然说它的效果并不是很好)。它的基本理论是,计算输入图片与训练时的图片之间的L1距离(曼哈顿距离)来确定预测的图片与训练时的哪张图片最接近。

L1距离(曼哈顿距离)

  在之前,讲到,最近邻算法主要是通过L1距离来比较两张图片之间的相似度。那么什么是L1距离(曼哈顿距离)呢,它指的是两点在南北方向的距离加上两点在东西方向上的距离。通俗的讲,就是两点之间,只能以水平方向或者竖直方向走。对于二维坐标系中的两个坐标点而言,其基本计算公式为:
d ( i , j ) = ∣ x i − x j ∣ + ∣ y i − y j ∣ d(i,j)=|x_i -x_j|+|y_i-y_j| d(i,j)=xixj+yiyj
  那么对于图像来说呢,它的计算方式则是通过计算两个矩阵(其实也就是两张图像,矩阵上的每个数字都对应着图像上的每个像素点)对应元素的差值的绝对值,然后将得到的矩阵逐元素相加,最终确定的值即为L1距离。如此,则可以将其公式表示为:
d 1 ( I 1 , I 2 ) = ∑ P ∣ I 1 P − I 2 P ∣ d_1(I_1,I_2)=\sum\limits_P|I_1^P-I_2^P| d1(I1,I2)=PI1PI2P
L1距离
  下面是关于L1距离的最近邻算法的具体编程实现:

import numpy as np

class NearestNeighbor(object): # 此处定义一个最近邻算法类
  def __init__(self): # 对类进行初始化
    pass

  def train(self, X, y):  # 训练数据集所需要的函数
    """X是一个NxD维的数组,其中N代表着样本数量,D代表着数据的维度,代表着输入的数据。Y是一个大小为N的1维数组,即其长度为N,代表着输入数据对应的标签 """
    # 最近邻分类器只是简单的记住了所有的训练数据
    self.Xtr = X 
    self.ytr = y

  def predict(self, X):
    """ X是一个NxD维的数组,其中N代表着样本数量,这里的样本都是我们希望预测的数据 """
    num_test = X.shape[0]
    # 确保输出的类型能够匹配输入的类型
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

    # 循环所有的样本
    for i in range(num_test):
      # 使用L1距离来为第i张测试图片找到在训练图片中与之最接近的图片
      distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
      # self.Xtr - X[i,:]是求测试数据中第i张图片与训练数据中所有图片的差值
      min_index = np.argmin(distances) # get the index with smallest distance
      # 从第i张测试图片与所有训练图片的L1距离中找到最小的那个位置的索引
      Ypred[i] = self.ytr[min_index] # 根据索引来预测那张图片的标签

    return Ypred

  从上面的代码中,我们可以看到,事实上,利用最近邻算法来分类很简单,这是因为所有的矢量化操作(即矩阵的运算)均使用numpy来进行了。此外,在上述代码中,我们在训练数据的时候不用做任何的操作,因为此时只是将数据记录到了内存中,而在预测时,则会通过将每一张训练的图片与预测图片进行计算比较,以此来得出最终的分类结果 。因此,这个算法,在Train函数中,它的时间复杂度为O(1), 而在predict函数中,它的时间复杂度为O(n)。
  这其实不太符合我们的需求。因为我们总是希望测试的时候能够更快地进行,而训练的时候可以稍微慢一些

L2距离(欧式距离)

  在上面,我们讲解了L1距离,并通过python实现了基于L1距离的最近邻算法。但其实,在最近邻算法中,还可以使用L2距离来实现。L2距离指的是两点之间的直线距离,其公式为:
d 2 = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 d_2 = \sqrt{(x_1-x_2)^2+(y_1-y_2)^2} d2=(x1x2)2+(y1y2)2
类似的,在对图像进行处理时,它的公式为:
d 2 ( I 1 , I 2 ) = ∑ P ( I 1 P − I 2 P ) 2 d_2(I_1,I_2) = \sqrt{\sum_{P}(I_1^P-I_2^P)^2} d2(I1,I2)=P(I1PI2P)2
  此时,上述的代码只需要改动一句:

distances = np.sqrt(np.sum(np.square(self.Xtr - X[i,:]), axis = 1))
L1与L2的比较

  上面讲述了L1距离以及L2距离,那么它们之间有些什么不同呢?
  简单来说,L1 距离更依赖于坐标轴的选定,坐标轴选择不同 ,L1 距离也会跟着变化,判定的数据归类的边界会更趋向于贴近坐标系的轴来分割所属区域。而 L2 的话相对来说与坐标系的关联度没那么大,它会形成一个圆,而不是跟随坐标轴变化 。它的不同如图所示:
L1与L2的差异
  所以最近邻分类结果到底是什么样子的呢?它其实可以简单的理解为对数据进行划分空间(如下图),然后根据附近的点来对周围的空间进行分类(染色),但通过图片,我们可以看到它的结果好像不是很好,如图片中间其实主要是绿色部分,虽然很少,同时 正中间还有一个黄色的点,但因为此算法只看最近的区域,因此此时出现了一个单独的类别孤岛,与真正的黄色区域隔离了。最近邻分类结果

K-最近邻算法(K-Nearest Neighbors)

  上面我们提到,最近邻算法可能不会很好的对类别进行划分。所以为了解决这个不足,在一定程度上将最近邻算法进行了改进,即K-最近邻算法。该算法不在只寻找一个最近的邻居(Neighbors),而是寻找了K个。它的思想为:如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。即在训练图像中找最相似的 k个图片的标签,k中数量最多的标签作为对测试图片的预测。(显然,当k=1时,它就是最近邻算法)
KNN
  KNN的分类结果如上图所示。此外,你也可以点击这里去自己测试使用不同的K值,不同的计算方式(L1距离或者L2距离)等来实际得到分类结果,以查看它们之间的差别。

超参数的选择

  在实际应用中,我们需要选择不同的K值,选择使用哪种距离来进行计算,这些都被称之为超参数。 因为他们不是从训练数据的过程中学到的,而是需要提前设置的。那么问题来了,该如何去设置这些参数呢?
也许你第一个想到的就是选择在整个数据集上面表现的最好的参数来作为超参数,这样可以为你的训练数据提供最准确或者最好的性能。但这其实是一个非常糟糕的想法,因为当这个算法实际部署之后,它所表现出来的性能会远远低于预期。这种情况我们称之为过拟合。你可以将其理解为,如果在整个数据集上选择超参数,那么相当于你将测试集也放在了训练集里面进行训练,这种情况下,你再去测试测试集,效果肯定会很好,但是一旦你将其他数据拿来测试,那么你得到的结果肯定不会很好。因此这是一个不好的想法,别去用于实践。
  那么,另一个更好的想法则可以是,将获取到的完整的数据集进行拆分,将其拆分为训练集以及测试集。然后选择在训练集中表现最好的参数来作为超参数,然后将选择的超参数用于测试集中。这看起来是一个更加合理的策略,但它也不是一个很好的想法,因为如果你这么做了,你可能只会在这一个数据集上得到比较好的结果,但实际上在新的数据集上面却不会很好。
  因此,一个合理的方法是,将数据集划分为训练集(占大部分),验证集以及测试集。 然后将算法在训练集上进行训练,然后使用验证集来进行评估选择超参数,最后将选择好的放入测试集中进行测试。
此外,还有另外一种选择超参数的方式,那就是交叉验证。这是一种更为常见的方式(在小数据集中),它主要是把数据集划分为训练集以及测试集,然后再将训练集平均划分为k部分(K折交叉验证),依次取其中的一部分作为验证集。最后取它们的平均结果作为最终的结果。超参数选择
交叉验证

KNN的优点
  1. 实现和理解非常简单
  2. 不需要再训练的时候花费太多的时间(毕竟只是将它们记录在内存中)
KNN的缺点
  1. 测试的时候速度非常慢(毕竟需要花费大量的时间来与训练集中的图片做一一对比)
  2. 近使用像素之间的差异(L2距离)是不够的,因为在有些图片中,L2距离会一样
  3. 维度灾难
    L2距离失效
    维度灾难

  如上图,在简单地遮住一些地方,或者降低了几个像素,或者对图像整体色度进行更改,它们的L2距离都与原图一样。而KNN 有点像训练数据把样本空间分成几块,我们需要训练数据密集的分布在样本空间里,否则测试图片的最邻近点可能实际距离会非常远,导致和最接近的训练集样本实际上完全不同。但是如果使训练数据密集分布,需要的训练集数量指数倍增加,是数据维度的平方。这就会导致维度灾难。

如何去更好地使用KNN算法

  如果想要使用KNN算法来进行图像分类,那么可以通过以下的建议来让KNN的实际应用更好:

  1. 对数据集进行预处理:对数据中的特征(如某个像素值)进行归一化处理,使其具有零均值(zero mean)和单位方差(unit variance)。
  2. 如果数据的维度非常高,可以考虑使用一些降维的方法,如PCA、NCA和随机投影法( Random Projections)。
  3. 将数据集随机进行划分,划为训练集,验证集以及测试集。
  4. 在验证集上面进行调优:例如尝试不同的k值,比较L1以及L2对结果的差别。
  5. 如果KNN算法在测试的时候太过于久,那么可以尝试使用ANN库(例如FLANN)加速(可能会损失一些精度)
  6. 对最优的参数做记录:记录好之后,直接去对测试集进行测试,并以这个结果作为最终结果。

线性分类

  上述的KNN虽然能对图像进行分类,但实际上使用的却很少。因此,接下来将讲述线性分类。线性分类也是一个非常简单的学习算法,但是它非常重要,并且能够帮助我们建立整个神经网络以及卷积神经网络。我们可以简单的将神经网络类比成乐高积木,卷积层等就像乐高中的不同组件,通过这些组件,我们可以搭建一个非常大的神经网络模型。线性分类器是神经网络中最常见的一种基本组件。乐高积木类比
神经网络中的线性层

参数模型

  在讲线性分类之前,先简单介绍一下参数模型。与KNN在训练过程中没有产生任何参数相反,参数模型指的是在训练完成后会产生一组参数,并可以只使用这组参数来对预测进行分类,而不需要使用训练数据。而在参数模型中,线性分类就是最简单的一种。
  参数模型由两部分组成,分别为输入数据以及参数/权重。其中,一般将输入作为x,而W则代表着参数或者权重。以下图中的图像为例,这张输入图像尺寸大小为32323,即有3072个数字。再经过一定的参数模型(图中的函数f)之后,会输出带有一定长度(类别数量的多少)的一维矩阵,矩阵中每个数值代表着该类别的可能性。在实际中,我们通常会为参数模型添加一个偏置量(b)(如下图所示),它能到模型更好地拟合数据。
参数模型

线性分类器

  线性分类目前主要有三种不同的观点(但其本质好像都是一样的),每种的介绍如下:

  1. 将权重W当做是所有分类器的组合,直接进行分类,得出结果。下面将使用一个简单的例子来说明。
    例子
      假设上图中的输入图像有4个像素(为了理解,此处假设图片是黑白的,即只有一个通道),因此线性分类器的工作原理就是:首先将这四个像素拉伸成一个列向量,列向量的长度即为4,而权重,则为3×4的矩阵(3代表着已有的类别,因此可以将W中的每行代表每个类别的分类器。4表示的是总像素个数(毕竟要与图片拉伸成的列向量做矩阵相乘)),之后再加上一个偏置项(可以将其理解为为每个类别提供一定的权重),最终获得一个列向量(包含该图片是每个类的得分),取得分最高的,即为该图像的类别。(显然,上图给的这个权重效果不是很好,输入的明明是猫,但输出的类别却变成了狗)
    实际上,线性分类器会计算图像中 3 个颜色通道中所有像素的值与权重矩阵的乘积,进而得到每个类别分值。再根据对权重设置的值,对于图像中的某些位置的某些颜色,函数表现出喜好或者厌恶(这个位置的权重是正或者是负)。比如说,我们可以想象 “船” 这个类别就是被大量的蓝色所包围(因为船在水中)。那么 “船”对应的分类器在蓝色通道上的权重就有很多的正权重(这样才能提高 “船”这个类别的分值),而在绿色和红色通道上的权重为负的就比较多(才能降低 “船”的分值)。现在再结合上面的小猫例子,猫分类器对第二个位置的像素比较嫌弃的 ,但巧合的是输入的小猫图像第二个位置像素值很大,最终计算得到一个很低的分数(当然,这个分类器显然是错误的)。

  2. 当然,除了将图片拉伸成为一个列向量之外,我们也可以将其理解为:W中的每一行都是一个分类器的模型(上面好像说到过,它的每一行都对应一个类别的分类),因此可以将W中的每行变成与图像相同形状的矩阵,然后通过使用内积(也叫点积)来比较图像和模板,然后找到和哪个模型最相似。(这也可以说是可视化)
    这种理解的角度下,线性分类器其实是在利用所学习到的模型,和输入图像做模型匹配。这可以把它看作一种高效的KNN,不同的是它不再使用所有的训练集的图像来比较,而是每个类别只用了一张图片来表征(这张图片是我们学习到的模型,而不存在训练集中),而且这种情况下我们更换度量标准,使用**(负)内积**来计算向量间的距离,而不是使用 L1 或者 L2 距离。具体可以看下图。
    可视化

  3. 将图像看做是高维空间中的点。那么线性分类器将试图在它们之间找到边界,来区分不同的类别。将这些空间降到二维(当然,我也没办法展示出高维的结果),我们可以看到每条直线都是一个分类器,试图将这个类别与其他类别给区分开来,这样,当分类器多了时,每个类别都会区分出来。
    高维分类

线性分类器失效的情况

  线性分类的分类能力其实是有限的,在某些情况下,它不能找到合适的分类器来将类别分开,比如说多模型等,具体见下图。
线性分类器失效情况

参数的选择

  在上面我们讲述了线性分类,但却没有讲述如何去选择一个合适的权重W,从上文(下图也有例子可以看到)我们可以得知,如果这个权重W不合适,那么最终的分类效果会很差(比如猫被分类成了狗)。现在我们来讲讲如何选择合适的权重W,如何用训练集来确定最合适的权重W。
权重的选择

损失函数

  在实际上,我们更希望程序能够根据训练的结果来自动的知道哪些W(权重)是好的,哪些是不好的,并自动来选择合适的W。为此,我们需要一些函数(具体实现)来量化,这种能量化W的函数,就称为损失函数(Loss Function)。
  在讲解损失函数时,我们一般会用一些训练样本来举例。假设此时有N个训练样本,同时也具有N个对应的训练标签,设为 { ( x i , y i ) } i = 1 N \left\{(x_i, y_i)\right\}_{i=1}^N {(xi,yi)}i=1N,其中 x i x_i xi表示输入的图像, y i y_i yi表示对应的标签。那么这个数据集的平均损失函数(取平均的目的是为了找一个合适的W使得L最小)可以定义为:
L = 1 N ∑ i L i ( f ( x i , W ) , y i ) L=\frac{1}{N}\sum_iL_i(f(x_i, W), y_i) L=N1iLi(f(xi,W),yi)
  接下来,将在几种线性分类器中讲述不同的损失函数(视频中是一起讲的,那就一起写)。

多类别支持向量机分类(Multiclass Support Vector Machine)

  多类别SVM其实可以看做是SVM的一个扩展,当多类别SVM中的类别数为2时,其就是SVM。SVM损失主要思想是想让SVM在正确分类上的得分比在不正确分类上的得分高一定的值(这个值我们称之为 Δ \Delta Δ)。现在假设每个类别的得分均为s,那么第j个类别的得分为 s j = f ( x i , W ) j s_j = f(x_i, W)_j sj=f(xi,W)j。因此,多类SVM的计算公式为:
L i = ∑ j ≠ y i m a x ( 0 , s j − s y i + Δ ) L_i = \sum_{j\neq y_i}max\left ( 0, s_j-s_{y_i}+\Delta \right ) Li=j=yimax(0,sjsyi+Δ)
损失函数举例
  以上图为例(在这个例子中,我们设置 Δ \Delta Δ为1),图中有三个样本,对应着三个类别的分类。我们以样本中的第一张图片为例,它的得分为s=[3.2, 5.1, -1.7],那么它的真实分数 s y i = s 0 = 3.2 s_{y_i}=s_0=3.2 syi=s0=3.2,所以针对猫的损失函数为:
L 1 = max ⁡ ( 0 , 5.1 − 3.2 + 1 ) + max ⁡ ( 0 , − 1.7 − 3.2 + 1 ) = max ⁡ ( 0 , 2.9 ) + max ⁡ ( 0 , − 3.9 ) = 2.9 + 0 = 2.9 L1=\max(0, 5.1-3.2+1)+\max(0, -1.7-3.2+1)=\max(0, 2.9) + \max(0, -3.9)=2.9+0=2.9 L1=max(0,5.13.2+1)+max(0,1.73.2+1)=max(0,2.9)+max(0,3.9)=2.9+0=2.9
类似地,我们可以求出L2 = 0, L3 = 12.9。因此,整个数据集中的损失函数为L=(2.9+0+12.9)/3=5.27

  注: 添加 Δ \Delta Δ的原因是为了让正确类别的分数比其他类别的分数至少高一个 Δ \Delta Δ,这样就可以让预测分数在一定范围内才开始计算损失值,否则的话直接赋值为0。
SVM损失函数的Python代码实现(从非向量化到向量化)如下:

def L_i(x, y, W):
  """
  非向量化的版本。 计算单个例子(x,y)的多类SVM损失
  - x 是表示图像的列向量(例如,CIFAR-10中的3073 x 1(第3073行时一个附加的偏置维度,即b))
  - y 是一个给出正确类别索引的整数(例如,CIFAR-10中的0到9之间)
  - W 是一个权重矩阵 (例如,CIFAR-10中的10 x 3073)
  """
  delta = 1.0 
  scores = W.dot(x) # w与x点乘,获得一个对每个类别的得分的向量
  correct_class_score = scores[y]
  D = W.shape[0] # 类别的总量
  loss_i = 0.0
  for j in range(D): # 迭代所有错误的类别
    if j == y:
      # 如果分类正确,则跳过
      continue
    # 累加第i个样本的损失值
    loss_i += max(0, scores[j] - correct_class_score + delta)
  return loss_i

def L_i_vectorized(x, y, W):
  """
  一个更快的半向量化版本的实现。
  半向量化指的是对于单个样本,实现不包含for循环,但是在样本外仍然有一个循环(在此函数之外)
  """
  delta = 1.0
  scores = W.dot(x)
  # 用一个向量来计算所有类之间的损失
  margins = np.maximum(0, scores - scores[y] + delta)
  # 正确类的损失应该为0
  margins[y] = 0
  loss_i = np.sum(margins) # 求和
  return loss_i

  本节的重点是,SVM损失采用了一种特定的方法来测量训练数据上的预测与真实标签的一致性。此外,对训练集进行良好的预测相当于将损失最小化。因此,我们也需要考虑如何让损失值最小(详情请见下章)。

正则化损失(regularization loss)

  上面提到的损失函数其实有一个问题。假设现在我们有一个数据集和一组权重参数W可以正确地对对每个数据进行分类。即所有的 L i L_i Li都为0。那么这个W是否唯一呢?其实这个答案是否定的。只要是任意的 λ > 1 \lambda >1 λ>1 λ W \lambda W λW都可以满足 L i = 0 L_i=0 Li=0,因为乘以 λ \lambda λ后会均匀地扩大了所有的值大小,因此结果不变。下图是一个举例:
举例
  对此,我们希望对W进行特定地处理,以消除上述情况的发生。一种常见的方法则是使用正则化惩罚 (regularization penalty) R(W)。此时完整的多类SVM损失函数就变成了 L = 1 N ∑ i L i + λ R ( W ) L=\frac{1}{N}\sum_{i}L_i+\lambda R\left (W\right ) L=N1iLi+λR(W)。其中 1 N ∑ i L i \frac{1}{N}\sum_{i}L_i N1iLi是让模型能够匹配训练的数据,而 λ R ( W ) \lambda R\left (W\right ) λR(W)则是阻止模型在训练集上效果太好(以防止过拟合等)。

常见的正则化损失
  1. 最常用的正则化损失是L2正则(Ridge)。它是将W中每个元素平方后加起来作为惩罚项,从而限制大的权重,让整体权重值变得尽可能的小。其公式为: R ( W ) = ∑ k ∑ l W k , l 2 R(W)=\sum_k\sum_lW_{k, l}^2 R(W)=klWk,l2
  2. 此外,还有L1范式,它的思想为将W中每个元素的加起来作为惩罚项,如此可以让W更加稀疏。其公式为: R ( W ) = ∑ k ∑ l ∣ W k , l ∣ R(W)=\sum_k\sum_l|W_{k, l}| R(W)=klWk,l
  3. 当然,你也可以将它们结合使用。当结合他们时,公式为: R ( W ) = ∑ k ∑ l β W k , l 2 + W k , l R(W)=\sum_k\sum_l \beta W_{k, l}^2 +W_{k, l} R(W)=klβWk,l2+Wk,l
  4. 上述三种主要是一些简单的正则化方法,还有一些复杂些的如Dropout、Batch normalization、Stochastic depth、fractional pooling等等
为什么要正则化

  这里有三个原因:

  1. 表达对权重的偏好
  2. 让模型更加简单,这样它就可以更好地在测试集上使用(其实就是让模型不那么大,测试时的计算消耗就相对而言会很小)
  3. 通过增加曲率来改进优化
softmax分类

  上面讲的SVM是两种常用的分类器之一,另外一个则是softmax(它有不同的损失函数)。如果你在此之前听说过了二类逻辑回归分类,那么softmax可以看做是多类上的扩展,因此它也叫多类别逻辑回归(Multinomial Logistic Regression)。softmax分类器不像SVM那样将输出当做是每个类别的分数(也许这么说不是很合理,但好像挺通俗的,而且视频里面说的就是分数),它将每个输出都当做了一个概率(即对于输入是这个类的概率)。其具体公式如下图(就是懒得写了):
softmax
  现在直接对上图的公式来做解释。首先 s = f ( x i ; W ) s=f(x_i;W) s=f(xi;W)依然是存放分值的向量(一个一维数组),这与之前讲的SVM一致。但此时这个分数被称为每个类别的非归一化log概率。其次,公式 f k ( s ) = e s k ∑ j e s j f_k(s) = \frac{e^{s_k}}{\sum_je^{s_j}} fk(s)=jesjesk则为softmax函数,其输入值是一个向量s,显然,s的值为分值。在经过函数的压缩之后,最终的输出则是一个值的范围为0-1的向量,且该向量中所有元素之和为1。那么我们现在可以将这个压缩后的向量看做一个概率分布,分类标签为k的概率分布,即 P ( y = k ∣ X = x i ) = e s k ∑ j e s j P(y = k|X = x_i) = \frac{e^{s_k}}{\sum_je^{s_j}} P(y=kX=xi)=jesjesk。此时,这个概率我们可以称之为归一化概率。因此, P ( y = k ∣ X = x i ) = e s k ∑ j e s j P(y = k|X = x_i) = \frac{e^{s_k}}{\sum_je^{s_j}} P(y=kX=xi)=jesjesk的概率为1时是最好的(毕竟代表着百分之百正确)。所以,我们希望它的对数似然能最大化,也就是负对数似然最小。由于 P ∈ [ 0 , 1 ] P\in[0,1] P[0,1],所以 − l o g ( P ) ∈ [ 0 , + ∞ ) -log(P)\in \left [0, +\infty\right) log(P)[0,+),故我们可以将这个值作为对 x i x_i xi的损失函数。从而得到softmax损失的公式为:
L i = − l o g P ( Y = y i ∣ X = x i ) = − l o g ( e s y i ∑ j e s j ) L_i = -logP(Y = y_i|X = x_i) = -log(\frac{e^{s_{y_i}}}{\sum_je^{s_j}}) Li=logP(Y=yiX=xi)=log(jesjesyi)
  结合我们之前所讲的损失函数,则有在整个数据集上,softmax损失函数的表达式为:
L = 1 N ∑ [ − l o g ( e s y i ∑ j e s j ) ] + λ R ( W ) L =\frac{1}{N}\sum\left[-log(\frac{e^{s_{y_i}}}{\sum_je^{s_j}})\right] + \lambda R(W) L=N1[log(jesjesyi)]+λR(W)
  这个损失函数又称为交叉熵损失(cross-entropy loss),而SVM中的损失函数又称为最大边界损失(max-margin loss)合页损失(hinge loss)

Softmax与SVM的比较

  它们的对比如下图所示:
两种损失对比
  它们之间的区别为:

  1. 计算上的差异:SVM 和 Softmax 分类器对于数据有不同的处理方式。两个分类器都计算了同样的分值向量 s(这里是通过矩阵乘来实现)。但他们之间的解释是不同的。SVM将它们看作是类别的评分,它的损失函数函数鼓励正确类别的分值比其他类别的分值至少高出一个 Δ \Delta Δ。而softmax则将它们看作每个类别没有归一化的对数概率,鼓励正确类别的归一化对数概率变高,其他变低。(当然,上图中给的计算结果,它们两个之间是没有可比性的。只有在数据集相同,分类器相同的前提条件之下,比较两种损失函数才有意义。)
  2. 损失函数的绝对数值不可以直接解释:SVM的计算是没有标定的,而且很难针对所有的分类的评分给出直接的解释,而softmax则允许计算所有类别的概率。但这个概率分布的集中或者离散程度是由正则化参数 λ \lambda λ决定的,随着 λ \lambda λ的不断增强,权重数值会越来越小,最终概率也会趋近于均匀分布。也就是说,softmax算出来的概率在一定程度上可以看做是一种对分类正确性的自信,但其差值(绝对值)则难以直观解释。
  3. 它们在实际应用中是相似的:在实际应用中,这两种分类器表现出来的差异很小。

总结

  在这篇文章中,我们简单地介绍了一下图像分类的挑战,以及如何去进行图像分类。在图像分类算法中,介绍了一下最近邻算法、K-最近邻算法、SVM以及Softmax分类器。同时,讲解了一下应该如何去选择参数和什么是损失函数和正则化等。

文中所有图片来自于cs231n公开课的网站之中。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值