摘要:需要注意的是正则化损失并不是加在每个数据的损失上,而是加在所有一组训练集个数据损失的平均值上,这样我们得到最终的损失函数,是正则化强度。这不会影响损失函数的输出,自然也不会影响损失,但这一下解决了溢出问题。
【DL-CV】线性分类器<前篇---后篇>【DL-CV】反向传播,(随机)梯度下降
现在有一个模型,能对输入的图像各种可能的类别进行评分。我们会引入损失函数Loss Function(或叫代价函数 Cost Function)定量的衡量该模型(也就是权重W)的好坏,其原理是——输出结果与真实结果之间差异越大,损失函数输出越大,模型越糟糕(需要训练让损失变小)。
根据差异定义的不同,损失函数有不同的计算公式,这里介绍在图像识别中最常用的两个损失——多类别SVM损失(或折叶损失hinge loss)和交叉熵损失,分别对应多类别SVM分类器和Softmax分类器
而且为了方便介绍,我们继续以图片评分的例子为例
第i个数据中包含图像xi 的像素和代表正确类别的标签yi(一个代表类别的数字),xi经过模型后输出sj (j对应某个类别的数字,sj对应该类别的分数),了解这些后我们先抛出每个数据损失计算公式:
模型越好,正确类别的得分应该要比其他错误类别的得分高,至于高多少,这个阈值(Δ)由我们来定,如果高出阈值,我们认为正确类别和某个类别的区分很好,我们给一个0损失给这两个类别的区分。相反如果某个错误类别比正确类别的得分高,说明该模型对这两类别的区分很糟,我们把高多少这个值加上阈值作为其损失。求正确类别和其他错误类别两两的损失值的和作为该数据的总损失。
一个具体例子如下图
这种损失也叫折叶损失,因其使用max(0,-)而得名,是标准常用的用法。有时候也会使用平方折叶损失SVM(L2-SVM),它使用的是max(x,-)2,这会放大损失(对坏的方面更加敏感),有些数据集使用L2-SVM会有更好的效果,可以通过交叉验证来决定到底使用哪个。
正则化!!这里插入讲下正则化的问题
上面损失函数有一个问题。假设有一个数据集和一个权重集W能够正确地分类每个数据(对于所有的i都有Li=0)时,这个W并不唯一,比如当λ>1时,任何数乘λW都能使得损失值为0,因为这个变化将所有分值的大小都均等地扩大了;又比如可能存在W的某一部分很大,另外一部分几乎为0。我们并不想要一堆扩大了N倍的W或者极不均匀的W,这时候就要向损失函数增加一个正则化惩罚(regularization penalty)R(W)来抑制大数值权重了。
需要注意的是正则化损失R(W)并不是加在每个数据的损失上,而是加在所有一组训练集(N个数据)损失的平均值上,这样我们得到最终的损失函数L,λ是正则化强度。
最常用的正则化惩罚是L2范式,L2范式通过对所有参数进行逐元素的平方惩罚来抑制大数值的权重:
正则化很重要,正则化的作用也不止于此,更多作用后面会在介绍,在此你只要知道正则化的作用是提升分类器的泛化能力,每个损失函数都应该引入 λR(W)
关于阈值ΔΔ是一个超参数,该超参数在绝大多数情况下设为Δ=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
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/19784.html
摘要:需要注意的是正则化损失并不是加在每个数据的损失上,而是加在所有一组训练集个数据损失的平均值上,这样我们得到最终的损失函数,是正则化强度。这不会影响损失函数的输出,自然也不会影响损失,但这一下解决了溢出问题。 【DL-CV】线性分类器【DL-CV】反向传播,(随机)梯度下降 现在有一个模型,能对输入的图像各种可能的类别进行评分。我们会引入损失函数Loss Function(或叫代价函数 ...
摘要:每传递一层,就相当于执行一次线性分类器和激活函数,其输出可作为下一个线性分类器的输入继续往下传递。像这样多个线性分类器叠加起来,那种网络结构就成型了。 【DL-CV】计算机视觉前置了解【DL-CV】损失函数,SVM损失与交叉熵损失 神经网络的变种很多,各种模型满天飞,但是相信大家见得最多的是这种showImg(https://segmentfault.com/img/bVbeWOO?w...
阅读 2228·2019-08-30 10:51
阅读 784·2019-08-30 10:50
阅读 1462·2019-08-30 10:49
阅读 3129·2019-08-26 13:55
阅读 1590·2019-08-26 11:39
阅读 3412·2019-08-26 11:34
阅读 1936·2019-08-23 18:30
阅读 3380·2019-08-23 18:22