python - 在TensorFlow中,python KL散度

我有两个张量,形状为[None,1000]的prob_a和prob_b,我想计算从prob_a到prob_b的KL散度。TensorFlow有这个函数吗?我试着使用tf.contrib.distributions.kl(prob_a, prob_b)但它给出了:

NotImplementedError: No KL(dist_a || dist_b) registered for dist_a type Tensor and dist_b type Tensor

如果没有相关的函数,有什么好的解决方法?

时间:

KL散度定义为:

KL(prob_a, prob_b) = Sum(prob_a * log(prob_a/prob_b))

另一方面,交叉熵H被定义为:

H(prob_a, prob_b) = -Sum(prob_a * log(prob_b))

如果创建变量y =prob_a/prob_b,可以通过调用负H(proba_a, y)获得KL散度,在Tensorflow表示法中,类似于:

KL = tf.reduce_mean(-tf.nn.softmax_cross_entropy_with_logits(prob_a, y))

因为有softmax_cross_entropy_with_logits,所以不需要在KL上优化。


KL(prob_a, prob_b) 


 = Sum(prob_a * log(prob_a/prob_b)) 


 = Sum(prob_a * log(prob_a) - prob_a * log(prob_b)) 


 = - Sum(prob_a * log(prob_b)) + Sum(prob_a * log(prob_a)) 


 = - Sum(prob_a * log(prob_b)) + const 


 = H(prob_a, prob_b) + const 



假设输入张量prob_aprob_b是沿第一个轴和1的概率张量,你可以这样做:


def kl(x, y):


 X = tf.distributions.Categorical(probs=x)


 Y = tf.distributions.Categorical(probs=y)


 return tf.distributions.kl_divergence(X, Y)



result = kl(prob_a, prob_b)



一个简单的示例:


import numpy as np


import tensorflow as tf


a = np.array([[0.25, 0.1, 0.65], [0.8, 0.15, 0.05]])


b = np.array([[0.7, 0.2, 0.1], [0.15, 0.8, 0.05]])


sess = tf.Session()


print(kl(a, b).eval(session=sess)) # [0.88995184 1.08808468]



你会得到同样的结果


np.sum(a * np.log(a / b), axis=1) 



但是,此实现有个bug(在Tensorflow 1.8.0中进行了检查)。

如果在a中有零概率,例如如果你尝试[0.8, 0.2, 0.0]而不是0 * log(0 / b)应该提供零。

为了缓解这一问题,应该加一些小的数值常数,也谨慎使用tf.distributions.kl_divergence(X, Y, allow_nan_stats=False)在此类情况下导致运行时错误。

另外,如果在b中存在一些零,那么你会得到inf值,这些值不会被allow_nan_stats=False选项捕获。

例如:


 ds = tf.contrib.distributions


 p = ds.Normal(loc=0., scale=1.)


 q = ds.Normal(loc=1., scale=2.)


 kl = ds.kl_divergence(p, q)


 # ==> 0.44314718




prob_a = tf.nn.softmax(a)


cr_aa = tf.nn.softmax_cross_entropy_with_logits(prob_a, a)


cr_ab = tf.nn.softmax_cross_entropy_with_logits(prob_a, b)


kl_ab = tf.reduce_sum(cr_ab - cr_aa)



...