Расхождение KL в TensorFlow
У меня два тензора: prob_a
и prob_b
с формой [None, 1000]
, и я хочу вычислить расхождение KL от prob_a
до prob_b
. Есть ли встроенная функция для этого в 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
Если нет встроенной функции, что было бы хорошим способом?
Ответы
Ответ 1
Предполагая, что ваши входные тензоры prob_a
и prob_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)
Однако эта реализация немного ошибочна (проверено в Tensorflow 1.8.0).
Если у вас есть нулевые вероятности в a
, например если вы попробуете [0.8, 0.2, 0.0]
вместо [0.8, 0.15, 0.05]
, вы получите nan
, даже если по определению Кульбака-Лейблера 0 * log(0 / b)
следует внести ноль.
Чтобы смягчить это, нужно добавить небольшую числовую константу. Также целесообразно использовать tf.distributions.kl_divergence(X, Y, allow_nan_stats=False)
, чтобы вызвать ошибку во время выполнения в таких ситуациях.
Кроме того, если в b
есть несколько нулей, вы получите значения inf
, которые не будут перехвачены опцией allow_nan_stats=False
, поэтому они также должны обрабатываться.
Ответ 2
Ибо есть 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
Ответ 3
Я не уверен, почему это не реализовано, но, возможно, есть обходной путь. Расхождение 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
, вы можете получить расхождение KL, вызвав отрицательный H(proba_a, y)
. В нотации Tensorflow что-то вроде:
KL = tf.reduce_mean(-tf.nn.softmax_cross_entropy_with_logits(prob_a, y))
Ответ 4
tf.contrib.distributions.kl
принимает примеры tf.distribution
не Tensor
.
Пример:
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
Ответ 5
Предполагая, что у вас есть доступ к логитам a и 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)
Ответ 6
Я думаю, что это может сработать:
tf.reduce_sum(p * tf.log(p/q))
где p - мое фактическое распределение вероятностей, а q - мое приблизительное распределение вероятностей.
Ответ 7
Я использовал функцию из этого кода (из этого среднего поста), чтобы вычислить KL-расхождение любого данного тензора из нормального гауссовского распределения, где sd
- стандартное отклонение и mn
является тензором.
latent_loss = -0.5 * tf.reduce_sum(1.0 + 2.0 * sd - tf.square(mn) - tf.exp(2.0 * sd), 1)