KL 散度又叫 相对熵,是衡量 两个概率分布 匹配程度的指标,KL 散度越大,分布差异越大,匹配度越低 

计算公式如下

 

或者

 

 

其中 p是 目标分布,或者叫被匹配的分布,或者叫模板分布,q 是去匹配的分布;

试想,p 是真实值,q 是预测值,岂不是 个 loss function

 

性质

如果 两个分布 完全匹配,Dkl=0;

KL 散度是非对称的,即 D(p||q) 不一定等于 D(q||p);

KL 散度 取值范围 大于等于 0;        【参考资料2有证明,有个 杰森不等式】

 

直观解释

KL 散度左边是个 概率,右边是个 log(p/q);

先看下右边的 log,如果 p > q,log 为正,如果 p < q,log 为负,如果 p = q,log 为 0;

为了使 KL 是个期望值,加入了权重 p,也就是说,p 使得 高概率的匹配区域 比 低概率的匹配区域 更加重要

p(xi) 大,说明 xi 对应的 占比大,占比大的 是不是该 重视一下呢,而 乘以 p(xi) 刚好可以使得其 权重增大,代表 重视了

直观而言,优先正确匹配近似分布中真正高可能性的事件是有实际价值的。
从数学上讲,这能让你自动忽略落在真实分布的支集(支集(support)是指分布使用的 X 轴的全长度)之外的分布区域。
另外,这还能避免计算 log(0) 的情况——如果你试图计算落在真实分布的支集之外的任意区域的这个对数项,就可能出现这种情况。

 

Python 实现

import numpy as np
import scipy.stats      # 不能只 import scipy

# 随机生成两个离散型分布
x = [np.random.randint(1, 11) for i in range(10)]
px = x / np.sum(x)
print(px)
y = [np.random.randint(1, 11) for i in range(10)]
py = y / np.sum(y)
print(py)


##### scipy API进行计算
# scipy计算函数可以处理非归一化情况,因此这里使用
# scipy.stats.entropy(x, y)或scipy.stats.entropy(px, py)均可
KL = scipy.stats.entropy(x, y)
print(KL)           # 0.14931583282835742


##### 手动实现
KL = 0.0
for i in range(10):
    KL += px[i] * np.log(px[i] / py[i])
print(KL)           # 0.14931583282835742

 

Pytorch 中 KL 计算出现负值

需加个 log

import torch

KL_criterion = torch.nn.KLDivLoss(size_average=False)
a = torch.tensor([0.2, 0.1, 0.3, 0.4])
b = torch.tensor([0.1, 0.2, 0.3, 0.4])

loss1 = KL_criterion(a.log(), b)
print(loss1)        # tensor(0.0693)    加 log 正确
loss2 = KL_criterion(a, b)
print(loss2)        # tensor(-1.5699)   不加 log 为 负数,错误

注意:分布的结果相加需要为1

 

 

 

 

参考资料:

https://www.jianshu.com/p/7b7c0777f74d  直观解读KL散度的数学概念    讲得比较好,主要参考,里面还有其他内容,可以看看

https://zhuanlan.zhihu.com/p/39682125  KL散度理解

https://www.cnblogs.com/hxsyl/p/4910218.html  浅谈KL散度

https://www.zhihu.com/question/384982085/answer/1856014994  pytorch KL散度为负数?