机器学习之loss

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

信息论

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

1.信息量

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

假设$X$是一个离散型随机变量,其取值集合为$\chi$,概率分布函数$ p(x)=P(X=x),x\epsilon\chi$,则定义事件$X=x_{0}$的信息量为: $$I(x_0)=-log(p(x_0))$$

2.熵

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

序号 事件 概率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)=-\sum_{i=1}^n p(x_i)log(p(x_i))$$ 其中$n$代表所有的n中可能性,所以上面的结果就是: $$\begin{eqnarray} H(X)&=&-[p(A)log(p(A))+p(B)log(p(B))+p(C))log(p(C))]\ &=&0.7\times 0.36+0.2\times 1.61+0.1\times 2.30\ &=&0.804 \end{eqnarray}$$

然而一些比较特殊的问题,比如投掷硬币只有两种可能,买彩票只有两种可能,我们称之为0-1分布问题,对于这类问题,熵的计算方法可以简化为如下计算: $$\begin{eqnarray} H(X)&=&-\sum_{i=1}^n p(x_i)log(p(x_i))\ &=&-p(x)log(p(x))-(1-p(x))log(1-p(x)) \end{eqnarray}$$

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散度的计算公式: $$D_{KL}(p||q)=\sum_{i=1}^np(x_i)log(\frac{p(x_i)}{q(x_i)})$$ n为事件的所有可能性,$D_{KL}$的值越小,表示q分布和p分布越接近

4.交叉熵

对上式KL散度变形可以得到: $$\begin{eqnarray} D_{KL}(p||q) &=& \sum_{i=1}^np(x_i)log(p(x_i))-\sum_{i=1}^np(x_i)log(q(x_i))\ &=& -H(p(x))+[-\sum_{i=1}^np(x_i)log(q(x_i))] \end{eqnarray}$$

等式的前一部分正是p的熵,等式的后一部分,就是交叉熵: $$H(p,q)=-\sum_{i=1}^np(x_i)log(q(x_i))$$

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

机器学习中交叉熵的应用

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

在逻辑回归问题中,常常使用MSE(Mean Squared Error)作为loss函数,如: $$loss = \frac{1}{2m}\sum_{i=1}^m(y_i-\hat{y_i})^2$$

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

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

这里的单分类是指,每一张图像样本只能有一个类别,比如只能是狗或者只能是猫。交叉熵在单分类问题上的标配方法是: $$loss=-\sum_{i=1}^{n}y_ilog(\hat{y_i})$$

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

*
Label 0 1 0
Pred 0.3 0.6 0.1

那么loss为: $$\begin{eqnarray} loss&=&-(0\times log(0.3)+1\times log(0.6)+0\times log(0.1)\ &=&-log(0.6) \end{eqnarray}$$

对应一个batch的loss就是: $$loss=-\frac{1}{m}\sum_{j=1}^m\sum_{i=1}^{n}y_{ji}log(\hat{y_{ji}})$$

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(\hat{y})-(1-y)log(1-\hat{y})$$ 注意,上面式子只是针对一个节点的计算公式,这一点要和单分类的loss区分开来,例子中可以计算为: $$ \begin{eqnarray} loss_猫 &=&-1\times log(0.8)-(1-1)log(1-0.8)=-log(0.8)\ loss_狗 &=&-1\times log(0.7)-(1-1)log(1-0.7)=-log(0.7)\ loss_兔 &=&-0\times log(0.1)-(1-0)log(1-0.1)=-log(0.9) \end{eqnarray} $$ 单张样本的loss即为$loss = loss_猫+loss_狗+loss_兔$ 每一个batch的loss就是: $$loss =\sum_{j=1}^{m}\sum_{i=1}^{n}-y_{ji}log(\hat{y_{ji}})-(1-y_{ji})log(1-\hat{y_{ji}})$$ 式中m为当前batch的样本数量,n为类别数。

补充(sigmoid与softmax)

1. 作用

sigmoid是一个激活函数,输入数据进行完线性变换后,都要进行一个非线性变换,原理就是计算sigmoid函数值,常用的激活函数还有tanh,relu等,sigmoid函数公式为:$f(x) =\frac{1}{1+e^{-x}}$,作用时将数值归一化到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))-(1-p(x))log(1-p(x))$$

其中$p(x)$为$f(x) =\frac{1}{1+e^{-\theta^{\mathrm{T}}x}}$。 在softmax回归中,我们解决的事多分类问题,它的本质就是将一个K维的任意实数向量压缩(映射)成另一个K维的实数向量,其实向量中的每个元素取值都介于(0,1)之间,softmax函数形式如下: $$S_{i}=\frac{e^{V_{i}}}{\sum_{j}e^{V_{j}}}$$

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

  • 计算与标注样本的差距

在神经网络的计算当中,我们经常需要计算按照神经网络的正向传播计算的分数S1,和按照正确标注计算的分数S2,之间的差距,计算Loss,才能应用反向传播。Loss定义为交叉熵: $$loss=-\sum_{i=1}^{n}y_{ji}log(\hat{y_{ji}})$$

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

  • 计算上非常的方便

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

我们的目标是,求loss对于$z_{i}$的梯度:$\frac{\partial C}{\partial z_i}$,根据链式法则: $$\frac{\partial C}{\partial z_i} = \frac{\partial C}{\partial a_j} \frac{\partial a_j}{\partial z_i}$$

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

第二个稍微复杂一点:(除法的导数公式${\frac{u}{v}=\frac{u^{'}v-uv^{'}}{v^{2}} }$)

  • 如果 $i=j$:

$$\frac{\partial a_i}{\partial z_i} = \frac{\partial ( \frac{e^{z_i}}{\sum_k{e^{z_k}}})} {\partial z_i}= \frac{\sum_k{e^{z_k}e^{z_i} - (e^{z_i})^2}}{(\sum_k{e^{z_k} })^2 } = ( \frac{e^{z_i}}{\sum_k{e^{z_k}}})(1 - \frac{e^{z_i}}{\sum_k{e^{z_k}}}) = a_i(1-a_i)$$

  • 如果 $i \neq j$:

$$\frac{\partial a_j}{\partial z_i} = \frac{\partial ( \frac{e^{z_j}}{\sum_k{e^{z_k}}})} {\partial z_i} = -e^{z_j}(\frac{1}{\sum_k{e^{z_k}}})^2e^{z_i} = -a_ia_j$$

ok,所有我们把上面的组合连起来: $$\frac{\partial C}{\partial z_i} = (-\sum_j{y_j \frac{1}{a_j} })\frac{\partial a_j}{\partial z_i} = - \frac{y_i}{a_i}a_i(1-a_i) + \sum_{j\neq i} \frac{y_j}{a_j}a_ia_j = -y_i + y_ia_i + \sum_{j\neq i}y_ja_i = -y_i + a_i\sum_jy_j$$ 最后的结果看起来简单了很多,最后针对分类问题,我们给定的$y_{i}$只会是0或者1,因此,对于分类问题,这个梯度等于: $$\frac{\partial C}{\partial z_i} = a_i - y_i$$

举个例子,通过若干层的计算,最后得到的某个训练样本的向量的分数是*[ 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