Как написать функцию потерь с Keras / Tensorflow для отрицательного биномиального распределения? - PullRequest
1 голос
/ 21 апреля 2019

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

Большое спасибо

Мне просто нужно переписать этот код, с кодом, дружественным к тензорным потокам. В соответствии с полученной ошибкой, возможно, tenensflow.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

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

TypeError: Тензорные объекты являются итеративными, только когда стремительное выполнение включен. Чтобы перебрать этот тензор, используйте tf.map_fn.

1 Ответ

1 голос
/ 24 апреля 2019

Вам нужно 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]])]
...