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散度为负数?