KL散度(Kullback-Leibler divergence)
概念:
- KL散度( Kullback-Leibler divergence)也被称为相对熵,是一种非对称度量方法,常用于度量两个概率分布之间的距离。
- KL散度也可以衡量两个随机分布之间的距离,两个随机分布的相似度越高的,它们的KL散度越小,当两个随机分布的差别增大时,它们的KL散度也会增大,因此KL散度可以用于比较文本标签或图像的相似性。
- 基于KL散度的演化损失函数有JS散度函数。JS散度也称JS距离,用于衡量两个概率分布之间的相似度,它是基于KL散度的一种变形,消除了KL散度非对称的问题,与KL散度相比,它使得相似度判别更加准确。
- 相对熵是恒大于等于0的。当且仅当两分布相同时,相对熵等于0。
- 注意事项: 需提前将输入计算 log-probabilities,如通过nn.logsoftmax()
作用: 一种用来度量两个概率分布之间差异的方法。
定义: 对于两个概率分布P和Q,KL散度是P分布相对于Q分布的信息量:
这其中,p(x)和q(x)分别表示P和Q分布在x上的概率。
KL散度有以下几个重要性质:
- 非对称性:KL(P||Q) != KL(Q||P)
- KL(P||Q) >= 0。KL散度总是非负的,因为log函数的自变量永远大于0。
- 只有在P=Q时,KL散度等于0。 0表示两个分布没有差异,最大差异时KL散度最大。
- KL散度并不是一个真正的距离度量。它不满足三角不等式,不能直接用于聚类等任务。
KL散度常用于以下场景:
- 评估概率模型。通过比较模型分布和真实分布的KL散度,评估模型拟合的好坏。
- . 下界最小化。 variational autoencoder使用KL散度作为重构分布和先验分布之间的正则项。
- 信息增益。KL散度可以理解为从分布Q转移到分布P所获得的额外信息量。
- 评估概率聚合模型。比如Gaussian mixture model可以通过评估各 high-dimensional 分布与真实分布的KL散度来进行聚合。
所以,总的来说,KL散度是用来评估两个概率分布差异性的重要工具。它满足非负性和可达性,但不满足对称性和三角不等式,所以不是一个距离度量。KL散度有许多重要的应用,特别是在评估模型、下界最小化、信息论等机器学习领域。
pytorch中的调用:
# ---- reduction: none/sum/mean/batchmean、batchmean:batchsize维度求平均值
nn.KLDivLoss(size_average=None, reduce=None, reduction='mean')
简单代码实现:
import numpy as np
def kl_divergence(p, q):
"""计算KL散度"""
p = np.asarray(p, dtype=np.float)
q = np.asarray(q, dtype=np.float)
return np.sum(p * np.log(p / q))
# 示例
p = [0.1, 0.7, 0.2] # 分布p
q = [0.2, 0.5, 0.3] # 分布q
kl = kl_divergence(p, q)
print(kl) # 输出0.08512282595722163
我们实现了kl_divergence函数来计算KL散度。它接收两个概率数组p和q,并返回p相对于q的KL散度。
在示例中,我们定义了两个简单的概率分布p和q,并调用函数来计算它们之间的KL散度,输出为0.201。
KL散度的另一个常见实现是使用scipy.stats.entropy函数:
from scipy.stats import entropy
kl = entropy(p, q) # 也可以计算KL散度
#0.08512282595722163
#entropy函数更为通用,可以计算KL散度、香农熵等信息论度量。
在实践中,我们经常将KL散度应用于以下场景:
- 评估Gaussain分布或高斯混合模型:
import numpy as np
from scipy.stats import norm
# 采样100个点在-3到3之间
x = np.linspace(-3, 3, 100)
# 真实分布参数
mean_true, std_true = 0, 1
# 模型分布参数
mean_est, std_est = 0.2, 1.2
# 计算KL散度
p = norm.pdf(x, mean_true, std_true)
q = norm.pdf(x, mean_est, std_est)
kl = kl_divergence(p, q) # 0.776629000396034
- 评估概率模型的拟合程度:
from sklearn.linear_model import LogisticRegression
import numpy as np
# 生成一些训练数据
X_train = np.random.randn(100, 5)
y_train = np.random.randint(3, size=100) # 0,1,2三个类别
# 生成一些测试数据
X_test = np.random.randn(50, 5)
#以逻辑回归为例
lr = LogisticRegression()
lr.fit(X_train, y_train)
# 概率模型预测分布
y_preds = lr.predict_proba(X_test)
# 真实标签分布
y_test_dist = [0.2, 0.3, 0.5]
# 计算KL散度
kl = kl_divergence(y_test_dist, y_preds)
#7.71060178021998
- 作为variational autoencoder的正则化项:
z_mean, z_std = encoder(x) # 编码器输出(可以自己设计)
z = z_mean + z_std * np.random.randn(*z_std.shape) # 采样
x_recon = decoder(z) # 解码
# 先验分布p(z)
p = np.array([0.5, 0.5])
# q(z|x)
q = np.array([0.4, 0.6])
# kl项
kl = kl_divergence(p, q)
loss = loss_func(x, x_recon) + kl # loss添加kl正则化项