我正在尝试将分布
p
拟合到具有 KL 散度的分布
q
。
import torch
p = torch.Tensor([0.1, 0.2, 0.7])
q = torch.Tensor([0.333, 0.334, 0.333])
所以我自己计算kl散度:
def kl_div(P, Q):
return (P * (P / Q).log()).sum()
kl_div(p, q)
结果是
tensor(0.2972)
torch.nn.functional.kl_div
功能。
我认为
input
应该是网络的输出,target
是一个常数。p
视为 input
,将 q
视为 target
。
但是
的结果torch.nn.functional.kl_div(p.log(), q, reduction='sum') # tensor(0.3245)
和我不一样。
这个得到的结果和我一样。
torch.nn.functional.kl_div(q.log(), p, reduction='sum') # tensor(0.2972)
所以我真的很想知道出了什么问题?
我对kl散度的理解有问题吗?
或者我填错了
torch.nn.functional.kl_div
的参数
还有一个问题:
如果我的分布为零怎么办?
比如
p = torch.Tensor([0., 0.3, 0.7])
q = torch.Tensor([0.333, 0.334, 0.333])
这种情况我还是需要计算kl散度。
KL 散度不对称。
KL(P, Q) != KL(Q, P)
。
逐点 KL 散度定义为
y_{true} * log(y_{true}/y_{pred})
。
接下来,你的功能:
def kl_div(P, Q):
return (P * (P / Q).log()).sum()
将
P
视为真实分布。从你的问题来看,听起来你将 P
视为预测分布,将 Q
视为真实分布,因此你的功能相对于你的函数发生了翻转。
您可能会感到困惑,因为数学符号
KL(P||Q)
将 P
定义为观测分布,将 Q
定义为“模型”分布,而 ML 上下文使用 P
表示您正在训练的模型的输出和 Q
表示数据集中的真实观测结果。
对于第二个问题,当其中一个值为零时,KL 散度未定义。这是该指标的定义。如果 P(i) = 0 且 Q(i) > 0,这意味着
P
表示事件 i
是不可能的,而 Q
表示它是可能的 - 对于这种差异没有任何衡量标准。
您可以通过在您的值中添加一个小 eps 来捏造它,即
torch.nn.functional.kl_div((p+1e-8).log(), q, reduction='sum')
。但是,如果您的用例经常出现 0 值,您应该考虑不同的指标。