介绍交叉熵和KL散度。

从信息量到信源熵

  1. 信息量是通信专业的名词。一个变量的主要特征就是不确定性,也就是发生的概率。信息量用来衡量不确定性的大小。一个事情发生的概率越小,使人越感到意外,则这件事的信息量越大;反之,概率越大,越不意外,信息量越小。举个例子,有一架波音747飞机失事,发生的概率很小,让人很意外,带给人的信息量很大。
    信息量函数应满足两个特性:1)随着概率的增大而减小,即是概率的减函数;2)信息量函数满足可加性,即两个统计独立的消息提供的信息量等于他们分别提供的信息量之和。同时满足递减性和可加性的函数是对数函数,即
    $$ I[p(x_i)] = log \frac{1}{p(x_i)} = -log p(x_i)$$
  2. 信源熵定义为信源输出的平均信息量,即信息量的数学期望。$$ H(X) = E(I[p(x_i)]) = E(-log p(x_i)) = - \sum_{i=1}^{n}p(x_i)log p(x_i)$$信源实际上是一个概率分布,信源熵可以解释为表示这个概率分布至少需要的信息量。

交叉熵

对于一个随机事件,真实概率分布是$p(x_i)$ 是未知的,从数据中得到概率分布为$q(x_i)$。我们用概率分布$q(x_i)$来近似和逼近真实的概率分布$p(x_i)$ 。交叉熵定义为:$$H(p,q) = \sum_{i=1}^{n}p(x_i) I[q(x_i)] =- \sum_{i=1}^{n}p(x_i)log(x_i) $$交叉熵$H(p,q)$是用概率分布$q(x_i)$来近似真实概率分布$p(x_i)$需要的信息量。上面我们说过,信源熵$H(X)$是表示真实概率分布$p(x_i)$需要的最小信息量。可以得到结论:$$H(p,q) \ge H(p)$$由吉布斯不等式可以证明,当且仅当分布$p(x_i)$与$q(x_i)$完全一致时,等号才成立。这个不等式的意义是:用概率分布$q(x_i)$来近似真实概率分布$p(x_i)$需要的信息量一定大于等于概率分布$p(x_i)$本身的信源熵。交叉熵比信源熵多出来的这部分,就是冗余信息量,我们称为KL散度(相对熵)。
$$KL(p||q)= H(p,q) - H(p) \ge 0$$容易看出交叉熵并不是一个对称量,即$ H(p,q) \not=H(q,p)$。同样的,KL散度也不是一个对称量,即$KL(p||q) \not =KL(q||p) $
给定概率分布$p(x_i)$,信源熵$H(p)$就是固定不变的。在机器学习中,交叉熵常用作分类问题的损失函数。交叉熵刻画了预测概率分布$q(x_i)$与真实概率分布$p(x_i)$之间的距离。通过减小交叉熵$H(p,q)$,我们可以使得预测概率分布$q(x_i)$不断逼近真实概率分布$p(x_i)$

相对熵

真实的概率分布为$p(x_i)$,我们用预测概率分布$q(x_i)$对它进行建模和近似。我们需要的平均附加量,也就是冗余量是:
$$KL(p,q) = H(p,q) - H(q) = -\sum_{i=1}^{n}p(x_i)logq(x_i) - \biggl(-\sum_{i=1}^{n}p(x_i)logp(x_i)\biggr) = -\sum_{i=1}^{n}p(x_i)log{\frac{q(x_i)}{p(x_i)}}$$KL散度有以下几个特性:

  • KL散度不是一个对称量,即$KL(p||q) \not =KL(q||p) $
  • $KL(p||q)\ge 0$,当且仅当分布$p(x_i)$与$q(x_i)$完全一致时,等号才成立。
  • KL散度可以看做两个分布之间不相似程度的度量。KL散度越小,两个分布的不相似程度越小,分布$q(x_i)$越适合来近似$p(x_i)$。

tensorflow用交叉熵做损失函数

在机器学习中交叉熵常常用作分类问题的损失函数。这里有个问题,交叉熵用于概率分布,但神经网络的输出并不一定是一个概率分布。
概率分布应满足2个条件:
1) $0 \le p(X =x) \le 1$
2) $\sum_{x}{} p(X=x) = 1$
如何把神经网络的输出变成概率分布呢?这里就要用到softmax回归。假设输出层的输出为$y_0,y_1,y_2 \dots y_n$,则softmax函数的形式为:$$softmax(y_i) = \frac{exp(y_i)}{\sum_{j}exp(y_j)}$$由于交叉熵一般会与softmax回归一起使用,TensorFlow对这两个功能进行了统一,可以直接用函数tf.nn.softmax_cross_entropy_with_logits来计算softmax后的交叉熵函数。对于只有一个正确答案的分类问题,可以用函数tf.nn.sparse_nn.softmax_cross_entropy_with_logits来加速计算过程。

pytorch中交叉熵损失函数的实现

在多分类问题中,实际概率分布是 $y = [y_0,y_1,…,y_{C-1}]$,其中C为类别数;y是样本标签的one-hot表示,当样本属于第$i$类时$y_i=1$,否则$y_i=0$。预测概率分布为$p = [p_0,p_1,p_2,…,p_{C-1}]$。$c$是样本标签。此时,交叉熵损失函数为$$loss = -\sum_{i=0}^{C-1}y_i log(p_i) = - y_c \cdot log(p_c) = - log(p_c)$$
接下来介绍pytorch中具体实现这个数学式子的函数。

torch.nn.functional.log_softmax()与class torch.nn.NLLLoss()

1
torch.nn.functional.log_softmax()
  • 作用:先做softmax运算,再做log运算。在数学上等价于$log(softmax(x))$
1
class torch.nn.NLLLoss(weight = None)
  • 作用:这是neg log likelihood loss(NLLLoss),即负对数似然函数。
  • 参数:
    • weight(tensor,optional): 一维tensor,里面的值对应类别的权重。当训练集样本分布不均匀时,使用这个参数非常重要。手动指定类别的权重,长度应为类别个数C。
  • 输入:
    • input(N,C): C是类别个数。为log_probabilities形式,即概率分布再取log。可以在最后一层加log_softmax,这就要用到函数torch.nn.functional.log_softmax()
    • targets(N): 是类别的索引,而不是类别的one-hot表示。比如,5个类别中的第3类,target应为2,而不是[0,0,1,0,0]

loss可以表示为:$$loss(x,class) = -x[class]$$如果指定了weight,可以表示为:$$loss(x,class) = - weight[class]*x[class]$$
举个例子:

1
2
3
4
5
6
7
import torch 
log_m = torch.nn.functional.log_softmax()
loss_function = torch.nn.NLLLoss()
inputs = torch.randn(3,5) #batch_size * num_classes = 3 * 5
target = torch.LongTensor([1,0,4])
loss = loss_function(log_m(inputs),target) # inputs要先做log_softmax,再送入loss_function
loss.backward()

class torch.nn.CrossEntropyLoss(weight = None)

  • 作用:将函数log_softmaxNLLLoss集成到一起。在多分类问题中非常有用。
  • 参数:
    • weight(tensor,optional): 一维tensor,里面的值对应类别的权重。当训练集样本分布不均匀时,使用这个参数非常重要。手动指定类别的权重,长度应为类别个数C。
  • 输入:
    • input(N,C): C是类别个数。每个类别的分数,不用过softmax层。
    • targets(N): 是类别的索引,而不是类别的one-hot表示。比如,5个类别中的第3类,target应为2,而不是[0,0,1,0,0]

loss可以表示为:$$loss(x,class) = - \text{log}\frac{e^{x[class]}}{ \sum_{j=0}^{C-1}e^{x[j]}} = -x[class] + \text{log}(\sum_{j=0}^{C-1}e^{x[j]}) $$当指定了weight时,loss计算公式为: $$ loss(x, class) = weights[class] \cdot (-x[class] + \text{log}(\sum_{j=0}^{C-1}e^{x[j]})) $$
参见: