Loading [MathJax]/jax/output/HTML-CSS/jax.js

机器学习之loss

交叉熵(cross entropy)是深度学习中常用的一个概念,一般用来求目标与预测值之间的差距。以前做一些分类问题的时候,没有过多的注意,直接调用现成的库,用起来也比较方便。最近发现自己对交叉熵的理解有些模糊,不够深入。

信息论

交叉熵是信息论中的一个概念,要想了解交叉熵的本质,需要先从最基本的概念讲起。

1.信息量

如果说概率P是对确定性的度量,那么信息就是对不确定性的度量 举个例子: 事件A:巴西队进入了2018世界杯决赛 事件B:中国对进入了2018世界杯决赛 凭直觉来说,事件B的信息量比事件A的信息量大,究其原因,是因为事件A发生的概率很大,事件B发生的概率很小。所以当越不可能发生的事件发生了,我们获取到的信息量就越大。那么信息量应该和事件发生的概率有关,信息量的大小和事件发生的概率成反比。具体的数学表示如下:

假设X是一个离散型随机变量,其取值集合为χ,概率分布函数p(x)=P(X=x),xϵχ,则定义事件X=x0的信息量为: I(x0)=log(p(x0))

2.熵

考虑另一个问题,对于某个事件,有n种可能性,每一种可能性都有一个概率p(xi),这样就可以计算出某一种可能性的信息量

序号 事件 概率P 信息量
A 电脑正常 0.7 log(p(A))=0.36
B 电脑死机 0.2 log(p(b))=1.61
C 电脑爆炸 0.1 log(p(c))=2.30

现在有了信息量的定义,而熵用来表示所有信息量的期望,即: H(X)=ni=1p(xi)log(p(xi)) 其中n代表所有的n中可能性,所以上面的结果就是: H(X)=[p(A)log(p(A))+p(B)log(p(B))+p(C))log(p(C))] =0.7×0.36+0.2×1.61+0.1×2.30 =0.804

然而一些比较特殊的问题,比如投掷硬币只有两种可能,买彩票只有两种可能,我们称之为0-1分布问题,对于这类问题,熵的计算方法可以简化为如下计算: H(X)=ni=1p(xi)log(p(xi)) =p(x)log(p(x))(1p(x))log(1p(x))

3.相对熵(KL散度)

相对熵又称KL散度,如果我们对于同一个随机变量 x 有两个单独的概率分布 P(x) 和 Q(x),我们可以使用 KL 散度(Kullback-Leibler (KL) divergence)来衡量这两个分布的差异。 如果用P来描述目标问题,而不是用Q来描述目标问题,得到的信息增量,<font color='red'>在机器学习中,P往往用来表示样本的真实分布,比如[1,0,0]表示当前样本属于第一类。Q用来表示模型所预测的分布,比如[0.7,0.2,0.1] </font>。 直观的理解就是如果用P来描述样本,那么就非常完美。而用Q来描述样本,虽然可以大致描述,但是不是那么的完美,信息量不足,需要额外的一些“信息增量”才能达到和P一样完美的描述。如果我们的Q通过反复训练,也能完美的描述样本,那么就不再需要额外的“信息增量”,Q等价于P。KL散度的计算公式: DKL(p||q)=ni=1p(xi)log(p(xi)q(xi)) n为事件的所有可能性,DKL的值越小,表示q分布和p分布越接近

4.交叉熵

对上式KL散度变形可以得到: DKL(p||q)=ni=1p(xi)log(p(xi))ni=1p(xi)log(q(xi)) =H(p(x))+[ni=1p(xi)log(q(xi))]

等式的前一部分正是p的熵,等式的后一部分,就是交叉熵: H(p,q)=ni=1p(xi)log(q(xi))

在机器学习中,我们需要评估label和predict之间的差距,使用KL散度刚刚好,即:DKL(y||ˆy),由于KL散度中的前一部分H(y)不变,故在优化过程中,只需要关注交叉熵就可以了。

机器学习中交叉熵的应用

1.为什么要用交叉熵做loss函数?

在逻辑回归问题中,常常使用MSE(Mean Squared Error)作为loss函数,如: loss=12mmi=1(yi^yi)2

这里的m表示m个样本,loss为m个样本loss的均值。MSE在逻辑回归问题中比较好用,那么在分类问题中还是如此么?

2.交叉熵在单分类问题中的使用

这里的单分类是指,每一张图像样本只能有一个类别,比如只能是狗或者只能是猫。交叉熵在单分类问题上的标配方法是: loss=ni=1yilog(^yi)

这个式子为计算一张样本的loss,n表示有n中类别,每个样本只属于其中一种,举例说明:

*
Label 0 1 0
Pred 0.3 0.6 0.1

那么loss为: loss=(0×log(0.3)+1×log(0.6)+0×log(0.1) =log(0.6)

对应一个batch的loss就是: loss=1mmj=1ni=1yjilog(^yji)

m为当前batch的样本数

3.交叉熵在多分类问题中的使用

这里的多类别是指,每一张图像样本可以由多个类别,比如同时包含一只猫和一只狗,和单分类问题的标签不同,多分类的标签是n-hot的。比如一张图里面既有猫,又有狗,那么具体看例子:

*
Label 1 1 0
Pred 0.8 0.7 0.1

值得注意的是,这里的Pred不再是通过softmax计算的了,这里采用的是sigmoid激活函数,将每一个节点的输出归一化到0-1之间。所有Pred值的和也不再是1,换句话说,就是每一个Label都是独立分布的,相互之间没有影响。所以交叉熵在这里是单独对每一个节点进行计算,每一个节点只有两种可能值,所以是一个二项分布。对于这种只有两个结果0,1的分布,熵的计算可以进行简化,同样的,交叉熵的计算也可以简化,即: loss=ylog(ˆy)(1y)log(1ˆy) 注意,上面式子只是针对一个节点的计算公式,这一点要和单分类的loss区分开来,例子中可以计算为: loss=1×log(0.8)(11)log(10.8)=log(0.8) loss=1×log(0.7)(11)log(10.7)=log(0.7) loss=0×log(0.1)(10)log(10.1)=log(0.9) 单张样本的loss即为loss=loss+loss+loss 每一个batch的loss就是: loss=mj=1ni=1yjilog(^yji)(1yji)log(1^yji) 式中m为当前batch的样本数量,n为类别数。

补充(sigmoid与softmax)

1. 作用

sigmoid是一个激活函数,输入数据进行完线性变换后,都要进行一个非线性变换,原理就是计算sigmoid函数值,常用的激活函数还有tanh,relu等,sigmoid函数公式为:f(x)=11+ex,作用时将数值归一化到0-1之间。

tensorflow中的接口示例如下:

1
2
3
4
5
6
7
import tensorflow as tf
import numpy as np
sess = tf.InteractiveSession()
y = np.array([1.,2.,3.,4.])
sigmoid = tf.nn.sigmoid(y).eval()
print(sigmoid)
# [0.73105858 0.88079708 0.95257413 0.98201379]

softmax是网络结构输出层的一层,计算输出层的值。主要用于神经网络最后一层,作为输出层进行多分类,是Logistic二分类的推广。在Logistic回归中,loss函数是sigmoid交叉熵,所以loss公式为: H(X)=p(x)log(p(x))(1p(x))log(1p(x))

其中p(x)f(x)=11+eθTx。 在softmax回归中,我们解决的事多分类问题,它的本质就是将一个K维的任意实数向量压缩(映射)成另一个K维的实数向量,其实向量中的每个元素取值都介于(0,1)之间,softmax函数形式如下: Si=eVijeVj

也就是说,是该元素的指数与所有元数指数的比值,这个定义非常直观,还有很多优点。

  • 计算与标注样本的差距

在神经网络的计算当中,我们经常需要计算按照神经网络的正向传播计算的分数S1,和按照正确标注计算的分数S2,之间的差距,计算Loss,才能应用反向传播。Loss定义为交叉熵: loss=ni=1yjilog(^yji)

其中ˆy就是softmax计算结果,这样的话,它占的比重越大,这个样本的Loss也就越小,这种定义符合我们的要求

  • 计算上非常的方便

我们对分类的Loss进行改进的时候,我们要通过梯度下降,每次优化一个step大小的梯度,我们应用链式法则对loss进行求导,最后的形式非常简单,过程如下,我们重新定义loss,a为拟合值,是关于参数输出zi的函数,y为真实标签,则: C=iyilogai

我们的目标是,求loss对于zi的梯度:Czi,根据链式法则: Czi=Cajajzi

有个人可能有疑问了,这里为什么是aj而不是ai,这里要看一下softmax的公式了,因为softmax公式的特性,它的分母包含了所有神经元的输出,所以,对于不等于i的其他输出里面,也包含着zi,所有的a都要纳入到计算范围中,并且后面的计算可以看到需要分为i=j和i≠j两种情况求导。 下面我们一个一个推: Caj=(jyjlogaj)aj=jyj1aj

第二个稍微复杂一点:(除法的导数公式uv=uvuvv2)

  • 如果 i=j

aizi=(ezikezk)zi=kezkezi(ezi)2(kezk)2=(ezikezk)(1ezikezk)=ai(1ai)

  • 如果 ij

ajzi=(ezjkezk)zi=ezj(1kezk)2ezi=aiaj

ok,所有我们把上面的组合连起来: Czi=(jyj1aj)ajzi=yiaiai(1ai)+jiyjajaiaj=yi+yiai+jiyjai=yi+aijyj 最后的结果看起来简单了很多,最后针对分类问题,我们给定的yi只会是0或者1,因此,对于分类问题,这个梯度等于: Czi=aiyi

举个例子,通过若干层的计算,最后得到的某个训练样本的向量的分数是*[ 1, 5, 3 ],那么概率分别就是[ 0.015, 0.866, 0.117],如果这个样本正确的分类是第二个的话,那么计算出来的偏导就是[0.015,0.866−1,0.117]=[0.015,−0.134,0.117]*,!然后再根据这个进行back propagation就可以了。

tensorflow中关于softmax的使用:

1
2
3
4
5
6
7
8
9
10
11
import tensorflow as tf
import numpy as np
sess = tf.InteractiveSession()
y = np.array([1.,2.,3.,4.])
np.exp(y) / np.sum(np.exp(y))
# array([0.0320586 , 0.08714432, 0.23688282, 0.64391426])
softmax = tf.nn.softmax(y).eval()
print(softmax)
# [0.0320586 0.08714432 0.23688282 0.64391426]
tf.argmax(softmax, axis=0).eval()
# 3