【斯坦福cs231n】2-2线性分类

线性分类

在最后一节中,我们介绍了图像分类的问题,这是从一组固定的类别向一个图像分配单个标签的任务。更多的,我们描述了通过将图像与来自训练集的(注释)图像进行比较来标记图像的k-最近邻(kNN)分类器。我们看到,kNN有一些缺点:

  • 分类器必须记住所有的训练数据并将其存储,以便将来与测试数据进行比较。这在空间上是低效的,因为数据集的大小可能很容易达到千兆字节。
  • 分类器执行对单个测试样本进行预测的操作是昂贵的,因为它需要与所有训练图像进行比较。

概述.我们现在要开发更强大的图像分类方法,我们最终将自然地扩展到整个神经网络和卷积神经网络。该方法将有两个主要部分:一个得分函数,这个函数是原始数据到预测分类的得分的映射;以及损失函数,这个函数衡量了预测得分与真实结果之间的差值。然后,我们将图像分类问题作为一个最小化相对于得分函数的损失函数的优化问题。

从图像到标签分数的参数化映射

该方法的第一个组成部分是定义将图像的像素值映射到每个类的置信度得分的分数函数。我们将以具体的例子来介绍这种方法。像以前一样,我们假设一个图像训练数据集$x_i \in R^D$,每个图像都有一个相关的标签$y_i$。在这里,我们定义$i = 1 \dots N$,并且$y_i \in { 1 \dots K }$。也就是说我们拥有$N$个样本(每个样本的维数都是$D$)和$K$个不同的类别。例如,在CIFAR-10中,我们有一个训练集,它有$N=50,000$张图片,其中每张图片都是$D=32 x 32 x 3 = 3072$像素,并且由于一共有10个不同的类别(狗,猫,车等等),所以$K=10$。我们现在将定义将原始图像像素映射到类分数的分数函数:$f: R^D \mapsto R^K$。

线性分类器.在这个模块中,我们可以从最简单的一个线性映射函数开始:

$$
f(x_i, W, b) = W x_i + b
$$

在上述等式中,我们假设图像$x_i$将其所有像素平坦化形状为[D x 1]的列向量。矩阵$W$(大小是[K x D]),向量$b$(大小是[K x 1])是函数的参数。在CIFAR-10中,$x_i$包含第i张图中的所有像素,并且展开成为单列的尺寸为[3072 x 1]的向量,$W$尺寸是[10 x 3072],$b$的尺寸是[10 x 1],所以这个函数每次有3072个参数输入(原始像素值),并且有10个参数输出(10个类别的得分)。参数$W$通常被称为权重(weights),$b$被称为偏差向量(bias vector),因为它会在不与实际输入数据$x_i$发生交互的情况下,影响输出分数值。但是,您经常会听到人们用权重或者参数这样的术语来描述这一概念。

有一些注意事项:

  • 首先,注意$Wx_i$的矩阵乘法部分,正在对10个独立的分类器进行并行的评估,其中$W$的每一行分别是一个类别的分类器。
  • 同时也要注意,给定的输入数据$(x_i, y_i)$是不可变的,但是我们可以控制调节参数$W,b$。我们的目标是通过设置这些参数,使得最终分类器计算出来的得分与整个训练集中的真实标签数据相匹配。我们将详细介绍如何做到这一点,但从直觉上来说,我们希望被正确分类的分数是高于错误分类的分数的。
  • 这种方法的一个优点是训练数据被用于学习参数$W,b$,但一旦学习完成,我们就可以忽略训练数据集,并且只保留学习得到的参数值即可。这是因为一个新的测试图像可以通过调用这个方法来基于已经算出的分数进行分类。
  • 最后,注意测试图像分类涉及到一个单个的矩阵乘法和加法操作,这明显比将测试图像与所有训练图像进行比较更快。

卷积:卷积神经访问将图像像素映射到如上所示的分数,但映射(f)更复杂,并且包含更多的参数。

解释线性分类器

请注意,线性分类器将其分类计算为其所有3个颜色通道中的所有像素值的加权和。分类结果取决于我们为这些权重设置什么具体值,该函数具有在图像中某些位置的某些颜色的喜欢或者不喜欢的能力(取决于每个权重的符号)。例如,你可以想象,如果图像的边缘部分有很多的蓝色(这部分可能是水),那么这张图片是“船”的类别的可能性更大。你可能希望“船”分类器在其蓝色通道有着正权重(蓝色增加“船”类别的分值),而在红色/绿色通道中有着负权重(红色/绿色的存在降低“船”类别的分值)。


一个将图像到分类得分的映射例子。为了方便可视化,我们假设图像只有4个像素(4个单色像素,这里我们不考虑彩色通道),我们有三个类别(红色(猫),绿色(狗),蓝色(船))。(说明:这里的颜色简单的表示了3个类别,但与RGB通道无关。)我们将图像像素拉伸成一列,并执行矩阵乘法以得到每个类的分数。请注意,这里给定的权重W不是很好:我们传入一张猫的图片,通过这个权重计算得到的对应的猫的得分很低。实际上,这套权重对应得到的得分似乎在说明它看到的是一条狗。


将图像比作高维点.由于图像被拉伸成高维列向量,我们可以将每个图像解释为该空间中的单个点(例如,CIFAR-10中的每个图像是32×32×3像素的3072维空间中的点)。类似地,整个数据集是一个(被标记的)的点集合。

由于我们将每一个类的分数定义为所有图像像素的加权和,所以每个类对应的分数在这个空间上是一个线性函数。我们无法想象3072维空间的样子,但如果我们想象将所有的维度都挤压到两个维度时,那么我们就可以试着去可视化分类器正在做的事情:

图像空间中,每个图像都是单个点,并且有三个分类器被可视化。使用汽车分类器(红色)的示例,红色线显示空间中为汽车分类得分为0的所有点。红色箭头表示的是分数增加方向,所以红线右侧的所有点都是正(线性增加)得分,并且左边的所有点都是负(线性递减)得分。


正如我们上面所见到的,权重矩阵$W$的每一行都对应一个类别的分类器。这些数字的几何解释是:当我们更改$W$的其中一行时,像素控件中的相应行将沿不同方向旋转。另一方面,偏置量$b$允许我们的分类器转换行。特别要注意的是,没有偏置量时,插入$x_i=0$时,不管权值为何值,最终得到的分数总是0,所以所有分类器的线条都被迫穿过原点。

将线性分类器解释为模板匹配.对于权重$W$的另外一种解释是每一行都对应一个类的模板(有时也称为原型)。然后,通过使用内积(或点积)逐个比较每个模板与图像,以获得最适合的图像,从而获得每个类的分数。当我们执行内积操作的时候,线性分类器正在进行模板匹配,其中模板是通过学习得到的。另一种看待这个问题的方法是我们依然在执行最邻近操作,但是,相比与成千上万的训练图像进行比较,我们这里只是使用单个图像进行比较(尽管我们会学习这张图,但它不一定是训练集中的图像),并且我们使用(负)内积作为距离而不是L1或L2距离。

CIFAR-10学习结束时得到的权重示例。注意,例如,船模板包含大量蓝色像素。因此,一旦与其内部在海洋上的船舶图像相匹配,该模板就会得到高分。


此外,请注意,马模板似乎包含一个双头马,这是由于数据集中有左右两匹马造成的。线性分类器将这两种模式的马在数据中合并成一个模板。类似地,汽车分类器似乎已经将多种模式合并成了单个模板,其必须从各方面以及所有颜色识别汽车。特别地,这个模板最终表现是红色的,这暗示着CIFAR-10数据集中红色的车占汽车类别的大部分。线性分类器太弱,无法正确识别不同颜色的汽车,但正如我们将看到的,神经网络将允许我们执行此任务。神经网络能够通过其隐藏层来开发出可以检测特定汽车类型的中间神经元(例如面向左方的绿色汽车,面向前方的蓝色汽车等),并且下一层的神经元可以通过各个车辆检测器的加权和来将它们组合成更准确的车辆分数。

偏置技巧.在继续下面的内容之前,我们将介绍一个通用的简化技巧,将两个参数$W,b$表示为一个参数。回想一下,我们将得分函数定义为:

$$
f(x_i, W, b) = W x_i + b
$$

对两个参数分别跟踪考虑(偏置$b$和重量$W$)是有点麻烦的。一个常用的技巧是将两组参数组合成一个单一的矩阵,我们通过将向量$x_i$扩展一个额外的维度,其值为常数1–一个默认的偏置维度。使用额外的维度,新的分数函数将简化为单个矩阵乘法:

$$
f(x_i, W) = W x_i
$$

在我们的CIFAR-10例子中,$x_i$现在的维度由[3072 x 1]变为了[3073 x 1]-(额外的维度的值为常数1),$W$现在由[10 x 3072]变为了[10 x 3073]。现在$W$矩阵额外增加的那一列对应了偏置量$b$。下面是一个帮助我们理解的例子:

上图介绍了调整偏置量的小技巧。执行一次矩阵乘法,然后将结果与偏置量(左侧)相加,等效于对所有输入向量添加常数为1的偏置维度,并将权重矩阵向右扩展一列偏置列(右侧)。因此,如果我们通过将其附加到所有向量来预处理我们的数据,我们只需要学习一个权重矩阵,而不是保存权重和偏差两个矩阵。


图像数据预处理.请注意,在上面的例子中,我们使用了原始像素值(范围从[0 … 255])。在机器学习中,对输入数据进行归一化操作是非常常见的做法(在图像识别的例子中,每一个像素被认为是一个特征)。特别地,通过减去每个特征的平均值来确定数据的中心值是很重要的。在图像识别的例子中,这对应于计算训练图像上的平均图像,并从每个图像中减去它,以获得像素范围大约在[-127 … 127]的图像。进一步的常用预处理是缩放每个输入特征,使其值范围落在[-1,1]的区间内。其中,均值为0是更为重要的步骤,但我们理解动态梯度下降之后我们才可以解释这一原因。

损失函数

在上一节中,我们定义了由一组权重$W$参数化的像素到得分的映射函数。此外,我们并没有控制数据$(x_i,y_i)$(这些数据是外部给到并不可改变的),但我们控制了权重,并且我们希望通过设置这些权值来预测分类得分,从而达到与真实标签相符合的效果。

例如,回到我们前面的那个将猫的图片分类到“猫”,“狗”和“船”的例子中,我们可以看到那个例子中的特定定权值并不是非常好:我们传入了一张画有猫的图片,但对于猫的分类得分,相比对于其他类别(狗的得分是437.9,船的得分是61.95)却非常低(-96.8)。我们可以通过损失函数(有时也被称为代价函数目标)来衡量我们的分类器有多差。直观的说,如果我们在训练集上分类的效果不好,那么损失值将会很高,反之,则会很低。

多类SVM损失函数

有几种方法来定义损失函数的细节。作为第一个例子,我们将首先开发一种称为多类别支持向量机(SVM)损失函数的常用损失函数。SVM损失函数被设置为使得SVM“想要”每个图像的正确类别具有比不正确类别高得多的固定的余量$\Delta$。请注意,如上所述,有时有助于拟合损失函数:SVM“想要”某种结果,意味着结果将产生较低的损失(这是好的)。

我们来看一下更准确的描述。回想一下我们给出的第i个样本的图像$x_i$以及其标识对应类别的标签$y_i$。得分函数$f(x_i, W)$接受像素值并且计算出得分向量,我们简称为$s$(分数的简写)。例如,第j个类别的得分是函数结果的第j个元素:$s_j = f(x_i, W)_j$。然后将第i个例子的多类SVM损失函数表示如下形式:

$$
L_i = \sum_{j\neq y_i} \max(0, s_j - s_{y_i} + \Delta)
$$

例如.让我们通过一个例子来了解他是如何工作的。假设我们有三个类,可以得到$s = [13, -7, 11]$,第一个类别对应的是true(即$y_i = 0$)。并且假设$\Delta$(一个我们稍后会介绍到的超参数)的值是10。根据上面的针对错误分类得分的求和表达式($j \neq y_i$),我们可以得到下面的结果:

$$
L_i = \max(0, -7 - 13 + 10) + \max(0, 11 - 13 + 10)
$$

你可以看到第一部分的结果是0,因为[-7 - 13 + 10]得出的是一个负数,然后通过函数$max(0,-)$将其阈值化为0。我们得到的损失值为0,因为正确分类的得分(13)比不正确分类的得分(-7)相距大于10。事实上,他们的距离是20,这个值是远大于10的,但SVM只关心差值最多在10以内;任何高于此间距的额外差值被取最大值操作控制在了0的位置上。第二部分关于[11 - 13 + 10]的计算结果是8。也就是说,即使正确的分类比不正确的分类(13>11)有更高的分数,但并没有超过10的间隔值。其差值仅仅为2,所以其损失值为8(即,差额要高出多少才能超过间隔值)。总而言之,SVM损失函数希望被正确分类的到$y_i$的得分要比不正确分类的得分至少要大$\Delta$。如果不是这样,我们就会累计损失值。

请注意,在这个特定的模块中我们正在使用线性评分函数($f(x_i; W) = W x_i$),所以我们也可以以等效的方式重写损失函数:

$$
L_i = \sum_{j\neq y_i} \max(0, w_j^T x_i - w_{y_i}^T x_i + \Delta)
$$

这里的$w_j$是权重矩阵$W$的第j行转化为列的形式。然而,在更复杂的评分函数$f$的形式中,不一定是这样的。

在我们完成本节之前,我们将提到的最后一个术语是值域为0的$max(0,-)$函数,我们通常称为转折点损失(hinge loss)。有时候您会听到有关人们使用形式为$max(0,-)^2$的对违规惩罚更强烈(二次而不是线性)的平方转折点损失SVM(或L2-SVM)。无平方计算的是更为标准的版本,但在某些数据集中,平方转折点损失可以工作的更好。这一点可以在交叉验证期间确定。

损失函数量化了我们对训练集的预测结果的不满意程度。


多类别支持向量机“想要”正确分类的分数比其他分数至少多出delta的大小。如果任何类别在红色区域内(或者更高)都有分数,那么就会有累计的损失。否则损失值为0。


正则化.上面提到的损失函数有一个bug。假设我们有一个数据集,并且有一组用于正确分类每个样本的参数$W$(例如,所有的分数都是满足所有的边距值,并且对于所有的i都有$L_i = 0$)。问题是这套$W$不一定是唯一的:可能有许多与$W$类似的参数可以正确分类示例。一种看待这个问题的简单的方式是如果$W$的一些参数正确分类了所有样本(对于每个样本来说损失值为0),那么任意倍数的这些参数$\lambda W$(其中$\lambda > 1$)也可以得出损失值为0的结果,因为这种变换均匀地拉伸了所有的得分幅度,以及它们的绝对差异。但是,如果正确分类的和最近的不正确分类的分数差异为15,然后将$W$的所有元素乘以2会使得新的差异值为30。

换句话说,我们希望对某些权重$W$进行一些偏好编码,以消除这种歧义。我们可以通过用正则化乘法$R(W)$来扩展损失函数,做到这一点。最常见的正则化惩罚是L2范数,通过对所有参数进行二次方的惩罚来阻止大权重出现:

$$
R(W) = \sum_k\sum_l W_{k,l}^2
$$

在上面的表达式中,我们将$W$中所有元素的平方求和。请注意,正则化函数不是基于数据的函数,它只是基于权重。包括了正则化惩罚在内的完整的多类支持向量机的损失函数由两部分组成:数据损失(这是全部样本中平均损失值$L_i$)和正则化损失。也就是说,完整的多类SVM损失函数变为以下形式:

或者将其扩展为完整形式:

$$
L = \frac{1}{N} \sum_i \sum_{j\neq y_i}
\left[ \max(0, f(x_i; W)_j - f(x_i; W)_{y_i} + \Delta) \right] + \lambda \sum_k\sum_l W_{k,l}^2
$$

其中$N$是训练样本的数量。正如你所见的,我们将正则化惩罚附加到损失目标上,由超参数$\lambda$加权。这个超参数通常是由交叉验证来设置,除此之外没有别的简单的方法。

事实上关于引入正则化惩罚除了上述的动机之外,正则化惩罚还给我们带来了许多理想的性质,其中有许多内容我们将在后面的部分讲到。例如,包括L2惩罚在内的SVM中的最大间隔属性(如果你感兴趣的话,见CS229)。

正则化最具吸引力的功能是可以惩罚大量的权重值使其倾向于增强泛化性能,因此这意味着没有输入数据的情况下也可以通过其自身大幅度的影响评分分值。例如,假设我们有一些输入向量$x = [1,1,1,1]$,以及两个权重向量$w_1 = [1,0,0,0]$,$w_2 = [0.25,0.25,0.25,0.25]$。然后可以得到$w_1^Tx = w_2^Tx = 1$,因此可见两个权重向量得到了相同的结果,但$w_1$的L2惩罚是1.0而$w_2$的L2惩罚仅仅是0.25。因此,根据L2惩罚的结果来看,权重向量$w2$应该被优先选择,因为它实现了较低的正则化损失。从直觉上来看,这是因为$w_2$的权重值更小并且更扩散。由于L2惩罚倾向于更小且更弥散的权重向量,所以最终的分类器更倾向于是将所有的维度都考虑到,而不是一小部分输入维度影响很大。正如我们在稍后的课程中看到的,这种效果可以提高分类器在测试图像上的泛化性能,并防止产生过拟合

注意,偏置量具有与权重不同的效果,它不能控制输入数据的影响力。因此,我们通常只对权重$W$进行正则化操作,而不是偏置量$b$。但是,在实践中,这一点通常会忽略不计(即我们统一使用权重和偏置量组合后的矩阵来计算偏差)。最后,请注意,由于正则化惩罚,所以我们绝对不能在所有的样本中的损失值完全为0,除非你错误的把权重矩阵设置为了$W=0$。

代码.这是在Python中实现的损失函数(无正则化),这是无向量化以及半向量化的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def L_i(x, y, W):
"""
unvectorized version. Compute the multiclass svm loss for a single example (x,y)
- x is a column vector representing an image (e.g. 3073 x 1 in CIFAR-10)
with an appended bias dimension in the 3073-rd position (i.e. bias trick)
- y is an integer giving index of correct class (e.g. between 0 and 9 in CIFAR-10)
- W is the weight matrix (e.g. 10 x 3073 in CIFAR-10)
"""
delta = 1.0 # see notes about delta later in this section
scores = W.dot(x) # scores becomes of size 10 x 1, the scores for each class
correct_class_score = scores[y]
D = W.shape[0] # number of classes, e.g. 10
loss_i = 0.0
for j in xrange(D): # iterate over all wrong classes
if j == y:
# skip for the true class to only loop over incorrect classes
continue
# accumulate loss for the i-th example
loss_i += max(0, scores[j] - correct_class_score + delta)
return loss_i
def L_i_vectorized(x, y, W):
"""
A faster half-vectorized implementation. half-vectorized
refers to the fact that for a single example the implementation contains
no for loops, but there is still one loop over the examples (outside this function)
"""
delta = 1.0
scores = W.dot(x)
# compute the margins for all classes in one vector operation
margins = np.maximum(0, scores - scores[y] + delta)
# on y-th position scores[y] - scores[y] canceled and gave delta. We want
# to ignore the y-th position and only consider margin on max wrong class
margins[y] = 0
loss_i = np.sum(margins)
return loss_i
def L(X, y, W):
"""
fully-vectorized implementation :
- X holds all the training examples as columns (e.g. 3073 x 50,000 in CIFAR-10)
- y is array of integers specifying correct class (e.g. 50,000-D array)
- W are weights (e.g. 10 x 3073)
"""
# evaluate loss over all examples in X without using any for loops
# left as exercise to reader in the assignment

这一部分主要介绍了:SVM损失函数采用一种特定的方法来衡量训练数据的预测与实际标签的一致性。此外,对训练集进行良好预测也等同于最大限度地减少损失值。

我们现在要做的是找到一种可以减少损失值的权重的方法。

实际操作中的注意事项

设置Delta值.请注意,我们已经学习过超参数$\Delta$以及其设置。那么应该选择设么样的值才是合适的呢?我们是否必须通过交叉验证才能得到呢?事实证明,这个超参数可以在任何情况下被安全的设置为$\Delta = 1.0$。超参数$\Delta$和$\lambda$看起来似乎是两个不同的超参数,但事实上他们控制着相同的操作:都是数据损失和正则化损失直接的权衡。理解这一点的关键是,权重$W$的大小对最终评分有直接影响(这也是他们之间的差异):当我们缩小$W$的所有数值时,评分的差异将变得更小,反之当我们放大权值$W$时,评分结果的差异也将变大。因此,评分之间的准确的间隔值(例如$\Delta = 1$或者$\Delta = 100$)在某种意义上是无意义的。因为权值可以被任意的缩小或放大。因此,唯一真正的权衡指标是我们允许权值增长的成都(通过正则化强度$\lambda$来控制)。

与二分类支持向量机的关系.你可能在参加这个课程之前了解过二分类支持向量机,所以,其中第i个样本的损失可以写成:

$$
L_i = C \max(0, 1 - y_i w^Tx_i) + R(W)
$$

这里的$C$是一个超参数,并且$y_i \in { -1,1 }$。你可以尝试自己证明,我们本节所讨论的多类别SVM实际上是包含了二分类SVM这一特例的。也就是说,如果我们只有两个类,那么损失值就降低到上面的二分类SVM损失函数的表达形式。此外,这个表达式中的$C$与之前的$\lambda$对结果起着相同的控制作用,他们之间的关系是:$C \propto \frac{1}{\lambda}$。

题外话:原始优化.如果你在此课程之前就已经知道了SVM,那么你可能还听说过SVM的内核,对偶,以及SMO算法等等。在这节课中(与神经网络的情况一样)我们将在无约束的原始形式的情况下优化目标。许多目标在技术上是不可区分的(例如,函数max(x,y)在x=y时无法区分哪一个是大的),但实际上这不是问题,并且常见的是使用子梯形图。

题外话:其他SVM表达式.值得注意的是,本节介绍的多类别SVM是在几个多类别SVM中的其中一个。另外一个常用的形式是一对全部One-Vs-All(OVA)SVM,其中每个类别分别对应一个独立的该类别到全部其他类别的二分类SVM。与之相关的一个在实践中不常用到的是全部对全部All-vs-All(AVA)策略。我们的策略是遵循Weston和Watkins 1999(pdf)的版本,这是一个比OVA更强大的版本(在这个版本的SVM中,您可以构建多类别的数据集,并且实现数据损失为0,但OVA无法做到这一点。如果对此感兴趣,请参阅论文中的细节)。最后一个你可能会见到的表达式是结构化SVM,这最大限度地提高了正确分类的得分与不正确得分中第二高的得分的间隔值的大小。对于这些SVM表达形式的差异的讨论超出了本课程的范畴。这些版本的SVM在实践过程中是可以安全的使用的,但是即使是最简单的OVA策略的形式也可以有效的分类(关于这些讨论,Rikin等人在2004年的In Defense of One-Vs-All Classification (pdf)也有提到)。

Softmax分类器

结果表明,SVM是两种常用的分类器之一。另外一个流行的选择是Softmax分类器,它拥有与SVM不同的损失函数。如果你之前有听过二分类逻辑回归分类器,那么对于Softmax分类器来说,实际上就是它在多类别上的泛化形式。不同于SVM那样通过函数$f(x_i,W)$为每个类别输出评分(未经校准的评分可能很难定义),Softmax分类器给出了一个稍微更直观的输出(归一化的分类概率),并且还有一个对于概率的解释,我们将在稍后介绍。在Softmax分类器中,映射函数$f(x_i; W) = W x_i$保持不变,但是我们现在将这些分数解释为每个类的归一化对数概率,并用交叉熵损失代替合页损失(hinge loss)

$$
L_i = -\log\left(\frac{e^{f_{y_i}}}{ \sum_j e^{f_j} }\right) \hspace{0.5in} \text{or equivalently} \hspace{0.5in} L_i = -f_{y_i} + \log\sum_j e^{f_j}
$$

这里我们使用了符号$f_j$来表示类别分数向量$f$的第j个元素。如上所述,数据集的完全损失是所有训练样本中的$L_i$均值与正则化项$R(W)$所组成。函数$f_j(z) = \frac{e^{z_j}}{\sum_k e^{z_k}}$称为softmax函数:它接受一个包含任意真实值的分数向量($z$),并将其向量值压缩到总和为1的0到1之间的值。如果你是第一次看到涉及softmax功能的完整的交叉熵损失函数,可能会觉得看起来很恐怖,但对于它的功能的理解是相对简单的。

信息论的观点.真实分布$p$与假设分布$q$之间的交叉熵定义为:

$$
H(p,q) = - \sum_x p(x) \log q(x)
$$

Softmax分类器最小化了预测分类的概率分布(正如上面所见到的$q = e^{f_{y_i}} / \sum_j e^{f_j}$)与真实分类的的概率分布的交叉熵,在这里我们指的是所有在正确类别上的概率分布(例如$p = [0, \ldots 1, \ldots, 0]$只包含在$yi$处的一个1)。此外,由于交叉熵可以写作熵和Kullback-Leibler分歧的和的形式:$H(p,q) = H(p) + D{KL}(p||q)$,并且delta函数$p$的熵是0,这也相当于最小化两个分布之间的KL分歧(距离的度量)。换句话说,交叉熵的目标是希望对于正确类别的预测结果的分布与真实分布达到一致。

概率的解释.看看下面的表达式:

$$
P(y_i \mid x_i; W) = \frac{e^{f_{y_i}}}{\sum_j e^{f_j} }
$$

这个式子可以解释为对于给定图片$x_i$,并由$W$参数化的分配给正确标签$y_i$的(归一化)概率。为了理解这一点,请回忆一下Softmax分类器将输出向量$f$中的评分值解释为没有归一化的对数概率。那么以这些数值做指数函数的幂就得到了没有归一化的概率,而除法操作则对数据进行了归一化处理,使得这些概率的和为1。从概率论的角度来理解,我们就是在最小化正确分类的负对数概率,这可以看做是在进行最大似然估计(MLE)。该解释的另一个好处是,损失函数中的正则化部分$R(W)$可以被看做是权重矩阵$W$的高斯先验,这里进行的是最大后验估计(MAP)而不是最大似然估计。提及这些解释只是为了让读者形成直观的印象,具体细节就超过本课程范围了。

实操事项:数值稳定。编程实现softmax函数计算的时候,中间项$e^{f_{y_i}}$和$\sum_j e^{f_j}$因为存在指数函数,所以数值可能非常大。除以大数值可能导致数值计算的不稳定,所以学会使用归一化技巧非常重要。如果在分式的分子和分母都乘以一个常数C,并把它变换到求和之中,就能得到一个从数学上等价的公式:

$$
\frac{e^{f_{y_i}}}{\sum_j e^{f_j}}
= \frac{Ce^{f_{y_i}}}{C\sum_j e^{f_j}}
= \frac{e^{f_{y_i} + \log C}}{\sum_j e^{f_j + \log C}}
$$

$C$的值可自由选择,不会影响计算结果,通过使用这个技巧可以提高计算中的数值稳定性。通常将$C$设为$logC=-max_jf_j$。该技巧简单地说,就是应该将向量$f$中的数值进行平移,使得最大值为0。代码实现如下:

SVM vs Softmax

线性分类的可交互网页演示

概要

坚持原创技术分享,您的支持将鼓励我继续创作!