原文地址:http://cs231n.github.io/linear-classify/
标题:Linear classification: Support Vector Machine, Softmax
随手翻译,不当之处请指正
线性分类
上一部分我们介绍过图片分类的问题,也就是从一系列不同的类别中挑出来一个为某张图片贴上标签。还有,我们介绍了K-最近邻分类器,让测试图片与带标签的训练图片逐一对比来为他们贴上标签。我们知道的,kNN有如下缺点:
- 分类器需要记住所有训练数据,并且存储起来,以备以后与测试数据的比较。这样做空间效率非常低,因为数据集容易达到GB级别。
- 对测试图片进行分类也非常困难,因为需要和训练集中所有图片逐一对比。
总览 我们现在要开发一个更加有效的,最终可以很自然的扩充到整个神经网络和卷积神经网络的图像分类方法。这个方法应该包括两个主要的部分:一个评价函数(score function),用以将原始数据映射到类别评分中(maps the raw data to class scores),一个损失函数(loss function),用以量化预测值和真实值之间的符合程度。这时候,就变成了一个最优化问题,调整评价函数的参数以使损失函数降到最小。
从图像到标签评分的参数化映射
这种方法的第一部分是定义一个评价函数,将图像中的像素值映射成每个类别的置信得分。我们使用具体实例来建立这样的评价函数。假设一个由图片组成的测试数据集
xi∈RD
,每张图片都有一个标签
yi
。这里
i=1...N
,
yi=1...K
。这里一共N个样本图片(每个图片D维)以及K个不同的类别。例如,在CIFAR-10数据集中,我们的训练集N=50000张图片,每张图片有D=32x32x3=3072像素,k=10,因为总共有10个类别。现在建立评价函数
f:RD↦RK
将原始图像数据映射到类别评分中。
线性分类:这一个模块中我们从可论证的最简单的方法—-线性映射开始:
在上述等式中,我们假设图片 xi 中所有的像素点都展平到一个[Dx1]的向量中。矩阵 W 大小为[KxD],向量
下面几点应该注意:
- 首先,在 Wxi 矩阵相乘中,其实是并行地评估10个独立的分类器(共10个类别),每个分类器是 W 的一行。
- 我们认为输入数据
(xi,yi) 是固定不变的,但是我们可以控制参数 W 和b 。我们的目标就是调整参数,使得训练集中计算出的类别评分与真实值相符。具体怎样做我们后面会提及,但是我们希望得到的是正确的分类的得分应该高于不正确的分类得分。 - 这个方法的好处就是,我们需要训练集去训练参数
W
和
b ,一旦训练完成,我们就可以完全抛弃训练集,而只保留训练过的参数。因为新的测试图片可以简单地在函数中处理,并且根据最后的类别评分分类。 - 最后,这种方法在测试时只需要做一次矩阵相乘,以及相加,比让测试图片与训练图片逐一比较方便许多。
伏笔:卷积神经网路就是按以上的方法将图片像素映射成评分,但是映射函数更加复杂,也包括更多的参数。
理解线性分类器
线性分类器通过有权值地叠加图像三个通道内所有像素计算类别评分。根据我们为这些权值赋值的不同,该函数可以决定在特定的位置对特定的颜色区别对待(like or dislike certain colors at certain positions in the image)。因此,可以想到,“船”这一类别图像的周围应该有大量的蓝颜色(某种程度上可以代表水)。你可能希望在“船”这一分类中,蓝色通道里有许多正权值,在红色通道以及绿色通道里有许多负权值。
图片映射到类别得分的一个例子。为了可视化,我们假设图片只有四个黑白像素(为了简介,我们不考虑颜色通道的问题)。有三种类别,红色的猫,绿色的狗,蓝色的船(这里的颜色并不代表RGB颜色通道)。我们把图像像素伸展到一维数组中,做矩阵相乘,得到每个类别的得分。注意到
W
中的参数并不理想,输入了猫的图像,但是在猫这一分类的得分却很低。事实上,这一系列权值标明,他们看到的是狗的图片。
将图像比拟为高维空间的点图片被刻画入高维空间后,我们可以认为图像在高维空间中是一个点(例如,CIFAR-10集中每一张图片都是3072维空间中的一个点)。也就是说,整个数据集就是这个空间中的一系列有标签的点。
由于我们把带权值的像素点相加得到每个类别的得分,每个类别的得分在这个空间中都是线性的。我们不可能将3072维的空间可视化,但是如果我们把这3072个维度挤进2个维度中,我们就大概能看到分类这一过程做了什么:
图片空间的卡通化表示,每张图片就是一个点,一共有三个可视化的分类器。红色的线是车分类器,线上的所有点在车辆这一类别中的评分为0,红色的箭头表示评分增长的方向,所以红线右边的所有点在车辆这一分类中都有正得分,而左边所有点都是负得分。
从上面可以看出,
把线性分类比作模板匹配 另外一种描述
W
的说法就是,
我们超前一点:CIFAR-10数据集权重训练结果,船分类模板和我们的预期一样,包括大量的蓝色像素点。这个模板在与一张在海上的船的照片做内积时会相应的给出高分。
另外,马的模板看起来有一个双头马,这是因为数据集中有朝左和朝右两种马的图片。线性分类器将两种朝向的马整合到一个模板中。类似的,车的分类器可能将好几种车的朝向和颜色融合到一个模板中。车的模板中有大量红色内容,则可能是因为CIFAR-10数据集中红色车比其他颜色的车出现的多。线性分类器太弱,以至于不能正确的标识(account for)不同颜色的车辆,但是后面我们会看到的神经网络可以胜任这份工作。神经网络可以在隐藏层中设计中间神经元,他们可以检测特定车的类型,比如绿色的朝左的车,蓝色的朝右的车。下一层的神经元则可以将这些特征输入带权值地输入特定的车辆检测器,获得到更加精确的得分。
偏置的技巧 在继续讲之前我们需要提及一个通用的简化技巧,以将
W,b
两个参数表示成一个。当时将评价函数定义为:
实际操作中,分别监控两组参数(偏置向量 b ,以及权重
在CIFAR-10数据中, xi 现在是[3073x1],而不是[3072x1](额外的维度全部置1),同事 W 是[10x3073]而不是[10x3072]。额外的
偏置技巧。矩阵相乘再与向量相加(左半边)和将矩阵扩展一个维度,全部填充为1,再做乘法(右半边)是相同的。因此,我们预处理我们的数据,扩充他们的维度并填充1,我们就只用学习一个简单的矩阵权重,而不用同时注意权重和偏置值。
图像数据预处理 在上例中我们使用原始的像素点值(从0到255)。在机器学习中,归一化输入特征的步骤是非常常见的(在图片中,每个像素点都是一个特征)。特别重要的是,要把每个特征值与平均值相减,使数据居中。在图像处理中,对应的就是在训练集中计算一个平均图像,再用每一个图像与其相减,获得一个像素值大约在-127到127之间的图片。更普遍的预处理是将每个输入特征标准化到-1到1之间。Of these, zero mean centering is arguably more important but we will have to wait for its justification until we understand the dynamics of gradient descent.(?)
损失函数
在前一节我们定义了一个函数,输入像素值,输出类别评分,权重矩阵
在上例中,示例输入图片是一只猫,得到了猫、狗和船这三个类别的评分,我们看到结果并不好:我们输入了一张猫的图片但是猫这一个类别的评分非常低(-96.8)(相较狗和船两个类别–分别是437.9和61.95)。我们用损失函数来衡量我们对于输出结果的不满意程度(有时候也可以叫做cost function 或者objective)。所以,我们对训练数据的分类很差的时候,函数值会非常高,反之则很低。
多类支持向量机损失(Multiclass Support Vector Machine loss)
定义损失函数的细节有多种方法。我们首先建立一个叫多类支持向量机(SVM)损失(以下简称SVM loss)的损失函数。SVM loss 是SVM“想要”使得每张图片的正确的分类得分比不正确分类得分高固定的值 Δ ,。有的时候将损失函数人格化是很有用的:SVM“想要”一个特定的输出,可以让损失函数值更低(更好)。
更具体地说,回想一下第i个例子,我们有了图片的像素值矩阵
xi
以及图片正确类别的标签
yi
。评价函数输入像素,计算向量
f(xi,W)
以获得类别评分,我们缩写成
s
。例如,第j类得分是第j个元素:
例 我们解剖一下,看看它如何运作。假设我们有三个类别接受了评分向量 s =[
第一个表达式得出的是0,因为[-7-13+10]得到的是负值,所以用max(0,-)的方法阈值化为0。这一对表达式中我们得到的损失为0,是因为正确的类别得分(13)比错误的类别得分(-7)至少大了10。事实上,差距是20,比10大得多,但是SVM只关心差距是否比10大。所有比10大的差距都会被max函数钳制在0。第二个表达式计算了[11-13+10]也就是8。虽然正确的分类得分比不正确的分类得分高(13>11),但是差距没有达到10,只有2,也就是为什么损失函数得到了8(也就是差距还要有多大才能达到预期边界(margin))。总的来说,SVM损失函数想要让正确的分类的得分比不正确的分类得分高至少 Δ 。如果没有达到要求,就要累计损失(accumulate loss)。
在这个特定的模型中我们讨论的是线性评价函数(
f(xi;W)=Wxi
),所以我们也可以将损失函数重写成下面的等式:
其中 wj 是 W 中的第j行,并重置成一个列。当我们考虑形式更加复杂的评分函数
最后我们还要交代的是, max(0,−) 的阈值0常被称作为铰链损失。有的时候可能会使用平方铰链损失支持向量机(L2-SVM),这种方法使用的阈值化方法为 max(0,−)2 ,这使得不满足条件的得分更加不理想(之前是线性,现在是平方次增长)。未平方的方法很基础,但是在一些数据集中,平方化的方法更加好用,这些可以在交叉验证的时候考虑。
损失函数量化了我们对于训练集中预测结果的不满意程度
多类支持向量机(MSVM)“想要”让正确分类的得分比不正确分类的得分至少高
Δ
,任何在红线内的或者更高的点都会导致损失的叠加。否则损失就变成0了。我们的目标就是,找到一组权重值使得训练集中所有样本都满足这样的限制,并且能够使得总损失尽量低。
正则化 上面展示的损失函数有一个非常大的bug。设想我们有一个数据集,以及一组能够对所有样本正确分类的权值
W
(也就是说,所有的得分都如此,所有的边界条件都满足,对所有的
也就是说,我们想要定义一种特性,使得相似的
W
得以被剔除。所以我们用正则化惩罚函数
在上面的表达式中,我们求矩阵 W 所有元素的平方和。注意,正则化函数不是面向数据的而是面向权重的。多类SVM损失包括两个部分:数据损失(所有样本的损失
或者展开成如下形式:
N 是训练集中的图像数量。我们将正则化惩罚函数乘以一个超参数
添加正则化惩罚函数还有一些额外的很好的特性,我们会在后面提到。例如在SVM中添加L2 惩罚会得到max margin(在CS229这门课程中有讲述)
最令人满意的特性就是惩罚大权值可以提高一般化程度,因为这意味着没有哪一个输入维度可以单独对输出结果造成非常大的影响。例如,我们有一个输入向量 x=[1,1,1,1] 和两个权重向量 w1=[1,0,0,0,] 和 w2=[0.25,0.25,0.25,0.25] 。由于 wT1x=wT2x=1 ,所以两个权重向量都有相同的点积结果,但是 w1 的L2惩罚值是1.0而 w2 的L2惩罚值只有0.25。也就是说,在L2惩罚作用下, w2 表现的更好,因为它能提供更小的正则化损失。直观上说,这是因为 w2 中的权值更小,更分散。因为L2惩罚偏好比较小的,比较分散的权重向量,最终的分类器也常常让所有维度上的数字非常小,而不是让某些维度非常大。我们会在后面看到,这个影响可以提升分类器对测试图片表现的一般化程度,从而导致过拟合。
但是偏置值并不会有像权重一样的影响,他们并不控制某个输入维度的影响力。所以我们一般归一化权重
W
但是不会归一化偏置向量
代码 这里是损失函数(没有归一化)的Python实现,有向量化的也有未向量化的:
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损失使用一个特定的方法来衡量基于训练集的预测与真实标签的一致程度。在训练集上的正确预测与最小化损失是一样重要的。
现在我们要做的就是想出一种方法找到使损失函数最小的权值矩阵。
实践中的考量
设置
Δ
我们大略地谈过超参数
Δ
以及它的设置。我们应该设置什么值?我们是否需要交叉验证它?事实上,这个超参数在任何情况下都可以被设置为
Δ=1.0
。
Δ
和
λ
看起来似乎是两个不同的超参数,事实上他们控制了同一个点:在数据损失和正则化损失之间(in fact they both control the same tradeoff: The tradeoff between the data loss and the regularization loss in the objective)。理解这个点的关键是权重
W
的量级直接影响到评分(同时也有他们的不同之处):我们收缩
与二分类支持向量机的关系 可能在来到这堂课之前在二分类支持向量机方面有些经验,第i个样本的损失可以被写成:
其中 C 是一个超参数,
另:原始优化 若有SVM基础,你可能听说过核心,对偶和SMO算法等等。在这节课中(神经网络),在原始的无约束的形式下,我们总是使用最优化的目标(方法?)。这些目标(方法?)中许多严格上来说都是不可微的(例如,max(x,y)不可微是因为在x=y的时候有一个拐点),但是实际上并不会有问题,并且次梯度也非常常用。
另:其他的多类SVM表达 在这一部分中,多类SVM(Multiclass SVM)是多分类SVM(SVM over multiple classes)的一种(公式化)表达方式。另一种常用的形式是“一对全支持向量机”(One-Vs-All SVM),这种方法为每一个分类对所有其他分类都训练一个独立的二值支持向量机。相应的但是不常见的也有“全对全”(All-vs_All)方法。我们的表达式使用的是比OVA更加有效的Weston and Watkins 1999(pdf)版本(在OVA中可以建立多类数据集,在这个版本中可以达到0数据损失,但是OVA不可以,连接中有更多细节)。最后的你会看到结构化支持向量机,他会最大化正确分类得分与错误分类最大得分间的距离。理解这些表达形式并不是这节课的内容。在本讲义中涉及的表达形式在实际应用中表现良好,但是有争议的最简单OVA方法也可以达到很好的效果(Rikin et al. 2004 In Defense of One-Vs-All Classification (pdf))。
Softmax 分类器
SVM是常见的两个分类器之一,另外一个就是有不同的损失函数的Softmax分类器。如果你听说过二元逻辑斯蒂回归分类器,那么Softmax就是它扩展到多分类的情况。与SVM把输出
f(wi,W)
当做每个类别的得分(无标定,并且很难理解)不一样,Softmax分类器给出了更加直观的输出。在Softmax中,用
f(xi;W)=Wxi
做映射这一步没有变化,但是我们将这些得分看做每个分类的未标准化的对数概率,并且用互熵损失(cross-entropy loss)代替了铰链损失(hinge loss),并有了如下表达式:
我们用 fj 代表类别得分 f 中的第j个元素。像以前一样,数据集中的所有损失用
信息论观点 “真”分布
Softmax分类器将估计的分类可能性( q=efyi∑jefj )和“真”分布情况(也就是我们只在正确的类别有一个1,而其他错误的类别处均为0,例如 p=[0,...1,...0] )间的互熵减到最小。而且,由于互熵可以被写成熵和相对熵(Kullback-Leibler divergence)两部分,也就是 H(p,q)=H(p)+DKL(p||q) , Δ 函数 p 的熵是0,所以这也等同于最小化两个分布之间的相对熵(一种衡量距离的方法)。也就是说,互熵想要让预测分布把所有的值放到正确的分类中。
概率解释 看以下表达式:
该表达式可以被理解成给定的图片 xi 贴上正确的(归一化的)可能性标签 yi , W 是参数(the (normalized) probability assigned to the correct label
实践问题:数值稳定性 在写代码实现Softmax时,由于指数的原因,中间值
我们可以随意选择 C 的值,这不会改变结果,但是我们可以用这个值增强计算的数值稳定性。一般将
f = np.array([123, 456, 789]) # example with 3 classes and each having large scores
p = np.exp(f) / np.sum(np.exp(f)) # Bad: Numeric problem, potential blowup
# instead: first shift the values of f so that the highest number is 0:
f -= np.max(f) # f becomes [-666, -333, 0]
p = np.exp(f) / np.sum(np.exp(f)) # safe to do, gives the correct answer
可能混淆的命名习惯 准确地说,SVM分类器使用铰链损失(hinge loss)或者有的时候被称为最大边界损失(max-margin loss)。Softmax分类器使用互熵损失(cross-entropy loss)。Softmax分类器名字由来是softmax函数(用于将类别得分列中的数挤压到[0,1]区间,并且和为0,以便互熵损失可以使用)。事实上,讨论“softmax损失”是无意义的,因为softmax是一个挤压函数,但是用来速记更方便。
SVM与Softmax
这张图可能会帮助理解SVM与Softmax分类器间的不同:
SVM与Softmax间不同点的例子。在两个情况下,我们都计算了相同的得分向量
Softmax分类器为每个分类提供“概率” 与计算无标定而且难以解释的所有类别得分的SVM不同,Softmax允许我们计算所有标签(分类)的可能性。比如,一个给定的图片在SVM分类器中对于分类“猫”,“狗”和“船”有得分
步骤是取幂以及标准化化使得和为1。现在如果正则化系数 λ 比较高,权重矩阵 W 就会获得更大的惩罚,最终导致权重变小。比如权重变成原来的一半(
现在,概率更加分散。在权重由于强大的正则化系数 λ 而逐渐变小的限制下,输出的可能性会接近相同。因此,Softmax分类器计算出来的可能性(像SVM一样)可以被看做置信度,并且评分是可理解的,但是绝对分数(或者他们的异同点)从理论上来说不可理解( Hence, the probabilities computed by the Softmax classifier are better thought of as confidences where, similar to the SVM, the ordering of the scores is interpretable, but the absolute numbers (or their differences) technically are not.)。
实际情况中个,SVM和Softmax不相上下 SVM和Softmax表现的不同非常小,并且每个人对于他们的评价不一样。SVM似乎更加注重眼前(local objective),这可以说是一个bug,也可以说是一种特性。例如一个评分结果[10,-2,3],并且第一个类别是正确的类别。一个SVM分类器(理想边界值为 Δ=1 )会发现正确分类评分已经比其他评分高出 Δ 了,于是计算出的损失为0。SVM并不关心每个得分的细节:如果我们有另外两种得分[10,-100,-100]或者[10,9,9],SVM并不会关心,因为边界值1已经被满足,所以损失为0。但是Softmax 并不会这样发展,它会为[10,9,9]积累更高的损失。也就是说,Softmax分类器不满足于他得到的评分,它认为正确的分类总有可能得到更高的概率,不正确的分类总有可能得到更低的概率,损失总有可能变得更小。但是SVM一旦达到了边界条件就满足,不会去在约束之外微调具体的得分。直观上可以说是一种特性:例如,一个忙于区分汽车和卡车的分类器不应该被一个青蛙样本叨扰,这个样本本来就已经有很低的评分, and which likely cluster around a completely different side of the data cloud。
交互式的网页示例
我们写了一个交互式网页示例,帮助理解线性分类器。示例将损失函数可视化了一个二维三分类问题。样例也展示了优化过程,我们以后会讲到。交互式的网页示例