Как я должен написать функцию потерь с Keras (и TensorFlow) для отрицательного биномиального распределения?

#python #tensorflow #keras #deep-learning #log-likelihood

#python #tensorflow #keras #глубокое обучение #логарифмическая вероятность

Вопрос:

Я моделирую переменные с отрицательным биномиальным распределением. Вместо ожидаемого среднего значения прогноза я скорее хотел бы смоделировать два параметра распределения. Итак, выходной уровень моей нейронной сети состоит из двух нейронов. Для этого мне нужно написать пользовательскую функцию потерь. Но приведенный ниже код не работает — похоже, проблема с итерацией по тензорам.

Как я должен написать функцию потерь с Keras (и TensorFlow) для отрицательного биномиального распределения?

Мне просто нужно переписать этот код, используя код, дружественный к тензорам TensorFlow. Согласно полученной мной ошибке, возможно tensorflow.map_fn , это может привести к решению, но мне не повезло с этим.

В целом это работает хорошо, но не с Keras / Tensorflow

 from scipy.stats import nbinom
from keras import backend as K
import tensorflow as tf

def loss_neg_bin(y_pred, y_true):

    result = 0.0
    for p, t in zip(y_pred, y_true):
        result  = -nbinom.pmf(t, p[0], min(0.99, p[1]))

    return result
  

Ошибка, которую я получил:

Ошибка типа: объекты тензора могут повторяться только при активном выполнении. Включено. Для перебора этого тензора используйте tf.map_fn.

Комментарии:

1. вы пытались использовать это ?

2. спасибо, это, кажется, только другое определение распределения, без четкого способа, как использовать его для функции потерь.. также устарела ссылка на библиотеку tensorflow-probability, которая кажется многообещающей, но я хотел бы использовать этот другой способ…

Ответ №1:

Вам нужно tf.map_fn выполнить цикл и tf.py_func завершить nbinom.pmf . Например:

 from scipy.stats import nbinom
import tensorflow as tf

def loss_neg_bin(y_pred, y_true):
    result = 0.0
    for p, t in zip(y_pred, y_true):
        result  = -nbinom.pmf(t, p[0], min(0.99, p[1]))
    return result

y_pred= [[0.4, 0.4],[0.5, 0.5]]
y_true= [[1, 2],[1, 2]]
print('your version:n',loss_neg_bin(y_pred, y_true))

def loss_neg_bin_tf(y_pred, y_true):
    result = tf.map_fn(lambda x:tf.py_func(lambda p,t:-nbinom.pmf(t, p[0], min(0.99,p[1]))
                                           ,x
                                           ,tf.float64)
                       ,(y_pred,y_true)
                       ,dtype=tf.float64)
    result = tf.reduce_sum(result,axis=0)
    return result

y_pred_tf = tf.placeholder(shape=(None,2),dtype=tf.float64)
y_true_tf = tf.placeholder(shape=(None,2),dtype=tf.float64)
loss = loss_neg_bin_tf(y_pred_tf, y_true_tf)

with tf.Session() as sess:
    print('tensorflow version:n',sess.run(loss,feed_dict={y_pred_tf:y_pred,y_true_tf:y_true}))

# print
your version:
 [-0.34313146 -0.13616026]
tensorflow version:
 [-0.34313146 -0.13616026]
  

Кроме того, если вы используете tf.py_func для вычисления функции массы вероятности для отрицательного биномиала в качестве модели обратной связи с потерями, вам необходимо самостоятельно определить функцию градиента.

Обновление — добавить дифференцируемую отрицательную биномиальную потерю

Функция массы вероятности для nbinom равна:

 nbinom.pmf(k) = choose(k n-1, n-1) * p**n * (1-p)**k
  

для k >= 0 согласно scipy.stats.nbinom.

Поэтому я добавляю дифференцируемую версию с отрицательными биномиальными потерями.

 import tensorflow as tf

def nbinom_pmf_tf(x,n,p):
    coeff = tf.lgamma(n   x) - tf.lgamma(x   1) - tf.lgamma(n)
    return tf.cast(tf.exp(coeff   n * tf.log(p)   x * tf.log(1 - p)),dtype=tf.float64)

def loss_neg_bin_tf_differentiable(y_pred, y_true):
    result = tf.map_fn(lambda x: -nbinom_pmf_tf(x[1]
                                                , x[0][0]
                                                , tf.minimum(tf.constant(0.99,dtype=tf.float64),x[0][1]))
                       ,(y_pred,y_true)
                       ,dtype=tf.float64)
    result = tf.reduce_sum(result,axis=0)
    return result

y_pred_tf = tf.placeholder(shape=(None,2),dtype=tf.float64)
y_true_tf = tf.placeholder(shape=(None,2),dtype=tf.float64)
loss = loss_neg_bin_tf_differentiable(y_pred_tf, y_true_tf)
grads = tf.gradients(loss,y_pred_tf)

y_pred= [[0.4, 0.4],[0.5, 0.5]]
y_true= [[1, 2],[1, 2]]
with tf.Session() as sess:
    print('tensorflow differentiable version:')
    loss_val,grads_val = sess.run([loss,grads],feed_dict={y_pred_tf:y_pred,y_true_tf:y_true})
    print(loss_val)
    print(grads_val)

# print
tensorflow differentiable version:
[-0.34313146 -0.13616026]
[array([[-0.42401619,  0.27393084],
       [-0.36184822,  0.37565048]])]