J'ai deux tenseurs, prob_a
et prob_b
avec une forme [None, 1000]
, et je veux calculer la divergence KL de prob_a
à prob_b
. Existe-t-il une fonction intégrée pour cela dans TensorFlow? J'ai essayé d'utiliser tf.contrib.distributions.kl(prob_a, prob_b)
mais cela donne:
NotImplementedError: No KL(dist_a || dist_b) registered for dist_a type Tensor and dist_b type Tensor
S'il n'y a pas de fonction intégrée, quelle solution de contournement serait la bonne?
En supposant que vos tenseurs en entrée prob_a
et prob_b
sont des tenseurs de probabilité dont la somme est égale à 1 le long du premier axe, procédez comme suit:
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)
Un exemple simple:
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]
Vous obtiendriez le même résultat avec
np.sum(a * np.log(a / b), axis=1)
Cependant, cette implémentation est un peu boguée (vérifiée dans Tensorflow 1.8.0).
Si vous avez une probabilité nulle dans a
, par exemple. Si vous essayez [0.8, 0.2, 0.0]
au lieu de [0.8, 0.15, 0.05]
, vous obtiendrez nan
même si, selon la définition de Kullback-Leibler, 0 * log(0 / b)
devrait contribuer à zéro.
Pour atténuer cela, il convient d’ajouter une petite constante numérique. Il est également prudent d'utiliser tf.distributions.kl_divergence(X, Y, allow_nan_stats=False)
pour provoquer une erreur d'exécution dans de telles situations.
De même, s'il existe des zéros dans b
, vous obtiendrez des valeurs inf
qui ne seront pas interceptées par l'option allow_nan_stats=False
, de sorte que celles-ci doivent également être gérées.
Pour softmax_cross_entropy_with_logits, il n'est pas nécessaire d'optimiser 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
tf.contrib.distributions.kl
prend des instances d'un tf.distribution
et non d'une Tensor
.
Exemple:
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
En supposant que vous ayez accès aux logits a et b:
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)
Je pense que cela pourrait fonctionner:
tf.reduce_sum(p * tf.log(p/q))
où p est ma distribution de probabilité réelle et q est ma distribution de probabilité approximative.