Расхождение 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)