【DL-CV】线性分类器<前篇---后篇>【DL-CV】反向传播,(随机)梯度降低python
如今有一个模型,能对输入的图像各类可能的类别进行评分。咱们会引入损失函数Loss Function(或叫代价函数 Cost Function)定量的衡量该模型(也就是权重W)的好坏,其原理是——输出结果与真实结果之间差别越大,损失函数输出越大,模型越糟糕(须要训练让损失变小)。segmentfault
根据差别定义的不一样,损失函数有不一样的计算公式,这里介绍在图像识别中最经常使用的两个损失——多类别SVM损失(或折叶损失hinge loss)和交叉熵损失,分别对应多类别SVM分类器和Softmax分类器
并且为了方便介绍,咱们继续以图片评分的例子为例数组
第i个数据中包含图像xi 的像素和表明正确类别的标签yi(一个表明类别的数字),xi通过模型后输出sj (j对应某个类别的数字,sj对应该类别的分数),了解这些后咱们先抛出每一个数据损失计算公式:网络
模型越好,正确类别的得分应该要比其余错误类别的得分高,至于高多少,这个阈值(Δ)由咱们来定,若是高出阈值,咱们认为正确类别和某个类别的区分很好,咱们给一个0损失给这两个类别的区分。相反若是某个错误类别比正确类别的得分高,说明该模型对这两类别的区分很糟,咱们把高多少这个值加上阈值做为其损失。求正确类别和其余错误类别两两的损失值的和做为该数据的总损失。
一个具体例子以下图函数
这种损失也叫折叶损失,因其使用max(0,-)而得名,是标准经常使用的用法。有时候也会使用平方折叶损失SVM(L2-SVM),它使用的是max(x,-)2,这会放大损失(对坏的方面更加敏感),有些数据集使用L2-SVM会有更好的效果,能够经过交叉验证来决定到底使用哪一个。spa
这里插入讲下正则化的问题code
上面损失函数有一个问题。假设有一个数据集和一个权重集W可以正确地分类每一个数据(对于全部的i都有Li=0)时,这个W并不惟一,好比当λ>1时,任何数乘λW都能使得损失值为0,由于这个变化将全部分值的大小都均等地扩大了;又好比可能存在W的某一部分很大,另一部分几乎为0。咱们并不想要一堆扩大了N倍的W或者极不均匀的W,这时候就要向损失函数增长一个正则化惩罚(regularization penalty)R(W)来抑制大数值权重了。
须要注意的是正则化损失R(W)并非加在每一个数据的损失上,而是加在全部一组训练集(N个数据)损失的平均值上,这样咱们获得最终的损失函数L,λ是正则化强度。blog
最经常使用的正则化惩罚是L2范式,L2范式经过对全部参数进行逐元素的平方惩罚来抑制大数值的权重:图片
正则化很重要,正则化的做用也不止于此,更多做用后面会在介绍,在此你只要知道正则化的做用是提高分类器的泛化能力,每一个损失函数都应该引入 λR(W)get
Δ是一个超参数,该超参数在绝大多数状况下设为Δ=1.0就好了。
权重W的大小对于类别分值有直接影响(固然对他们的差别也有直接影响):当咱们将W中值缩小,类别分值之间的差别也变小,反之亦然。所以,不一样类别分值之间的边界的具体值(好比Δ=1或Δ=100)从某些角度来看是没意义的,由于权重本身就能够控制差别变大和缩小。也就是说,真正的权衡是咱们容许权重可以变大到何种程度(经过正则化强度λ来控制)。
先来了解Softmax函数,这是一种压缩函数,实现归一化的
$$P(s)={e^{s_k}\over \sum_je^{s_j}}$$
Softmax函数接收一组评分输出s(有正有负),对每一个评分sk进行指数化确保正数,分母是全部评分指数化的和,分子是某个评分指数化后的值,这样就起到了归一化的做用。某个类的分值越大,指数化后越大,函数输出越接近于1,能够把输出看作该类别的几率值,全部类别的几率值和为一。若是正确类别的几率值越接近0,则该模型越糟,应用这个特性,咱们经过对正确类别的几率值取-log来做为损失(正确类别的几率越小,损失越大),因而咱们获得
$$L_i=-log({e^{s_{y_i}}\over \sum_je^{s_j}})$$
这就是交叉熵损失计算公式(有时也叫非正式名Softmax损失),具体例子以下图,使用上不要忘记加正则化λR(W)哦
由于交叉熵损失涉及指数函数,若是赶上很大或很小的分值,计算时会溢出。s很大es会上溢出;s是负数且|s|很大,es会四舍五入为0致使下溢出,分母为0就很差了。为解决这个潜在的问题,咱们要在计算前对得分数据处理一下。取全部得分的最大值M = max(sk), k=1,2,3...,令全部得分都减去这个M。这不会影响损失Softmax函数的输出,天然也不会影响损失,但这一下解决了溢出问题。要证实也很简单:es-M = es / eM , 而分子分母会约掉 eM
但仍然存在一个问题,若是分子发生下溢出致使Softmax函数输出0,取对数时就会获得−∞,这是错误的。为解决这个问题,其实咱们把上面的变换代进去继续算就会发现本身解决了
求和项里必定会有一个e0=1,最终对大于1和取对数不会发生溢出了,最后损失公式变成这样:
$$L_i=-\log({e^{s_{y_i}}\over \sum_je^{s_j}})=-\log({e^{({s_{y_i}}-M)}\over \sum_je^{({s_j}-M)}})=\log(\sum_j{e^{(s_j-M)}})-(s_{y_i}-M)$$
咱们用一组数据来探究它们的区别(假设SVM损失中的Δ=1),有三组输出分数[10,-2,3], [10,9,9],[10,-100,-100],正确类别的得分都是10,易得三组数据的SVM损失都是0,但它们的交叉熵损失明显是有高低之分的。对于SVM损失,它关心的是边界区分,正确类别的得分其余得分高出Δ就完事了,损失为0了。但对于交叉熵损失,因为正确类别的几率与分数间的差别是有关的,损失不可能等于0,正确类别的得分无穷大,其余得分无穷小,损失才趋于0。换句话说,交叉熵损失永远有缩小的空间,它但愿评分模型完美;而SVM损失只须要评分模型好到必定程度就好了。
但实际使用上,它们常常是类似的,一般说来,两种损失函数的表现差异很小,大可没必要纠结使用哪一个
注:如下代码基于单层网络而且不考虑激活函数(图像数据与权重相乘获得分数)进行损失统计,目的是为了集中介绍损失函数的numpy实现。x是二维数组,是N个样本的数据,每行是该样本的像素数据(已展开),所以这里采用x*W。y是一维数组,包含每一个样本的真实类别(一个数字)。
import numpy as np # SVM损失函数实现 def svm_loss_naive(W, x, y, reg): """ 循环实现 """ train_num = x.shape[0] # 样本数量 classes_num = W.shape[1] # 类别数量 loss = 0.0 for i in range(train_num): # 计算某个样本的损失值 scores = x[i].dot(W) correct_class_score = scores[y[i]] # 提取该样本的真实类别分数 for j in range(classes_num): # 正确类别得分与其余得分比较 if j == y[i]: continue margin = scores[j] - correct_class_score + 1 # 这里设阈值为1 if margin > 0: # 形成损失,将其计入 loss += margin loss /= train_num loss += reg * np.sum(W * W) # 加上正则化损失 return loss def svm_loss_vectorized(W, x, y, reg): """ 最高效向量化运算,维持x的二维结构运算 """ train_num = x.shape[0] classes_num = W.shape[1] scores = x.dot(W) # 二维结构,每行是该样本各个类别的得分 correct_class_scores = scores[np.arange(train_num), y] # 提取每一个样本的真实类别分数 correct_class_scores = np.repeat(correct_class_scores,classes_num).reshape(train_num,classes_num) # 扩展至二维结构(与scores同形状),每一行都是该样本真实类别的得分 margins = scores - correct_class_scores + 1.0 margins[range(train_num), y] = 0 # 令正确类别与自身相比的loss为0 抵消 +1.0 loss = (np.sum(margins[margins > 0])) / train_num # 把正数(loss)全加起来除以样本数得最终损失 loss += reg * np.sum(W*W) # 加上正则化损失 return loss
import numpy as np # Softmax 损失函数实现 def softmax_loss_naive(W, x, y, reg): """ 循环实现 """ loss = 0.0 classes_num = W.shape[1] # 类别数量 train_num = x.shape[0] # 样本数量 for i in range(train_num): # 计算某个样本的损失值 score = x[i].dot(W) score -= np.max(score) # 减去最大值防指数运算溢出 correct_class_score = score[y[i]] # 提取该样本的真实类别分数 exp_sum = np.sum(np.exp(score)) loss += np.log(exp_sum) - correct_class_score # 每一个样本的损失叠加 loss /= train_num loss += 0.5 * reg * np.sum(W*W) # 加上正则化损失 return loss def softmax_loss_vectorized(W, x, y, reg): """ 最高效向量化运算,维持x的二维结构运算 """ classes_num = W.shape[1] train_num = x.shape[1] scores = x.dot(W) # 二维结构,每行是该样本各个类别的得分 scores -= np.repeat(np.max(scores, axis=1), classes_num).reshape(scores.shape) # 减去最大值防指数运算溢出 exp_scores = np.exp(scores) # 指数化 correct_class_scores = scores[range(train_num), y] # 提取每一个样本的真实类别分数 sum_exp_scores = np.sum(exp_scores, axis=1) # 每一个样本的指数和 loss = np.sum(np.log(sum_exp_scores) - correct_class_scores) # 全部样本总损失 loss /= train_num loss += reg * np.sum(W*W) expand_sum_exp_scores = np.repeat(sum_exp_scores, classes_num).reshape(scores.shape) # 对每一个样本的指数和进行扩展,与scores进行除法运算 return loss