Keras custom RMSLE metric

Как реализовать эту метрику в Keras? Мой код ниже дает неправильный результат! Обратите внимание, что я отменяю предыдущее преобразование log (x + 1) через exp (x) - 1, а отрицательные предсказания обрезаются до 0:

def rmsle_cust(y_true, y_pred):
    first_log = K.clip(K.exp(y_pred) - 1.0, 0, None)
    second_log = K.clip(K.exp(y_true) - 1.0, 0, None)
    return K.sqrt(K.mean(K.square(K.log(first_log + 1.) - K.log(second_log + 1.)), axis=-1)

Для сравнения здесь стандартная реализация numpy:

def rmsle_cust_py(y, y_pred, **kwargs):
    # undo 1 + log
    y = np.exp(y) - 1
    y_pred = np.exp(y_pred) - 1

    y_pred[y_pred < 0] = 0.0
    to_sum = [(math.log(y_pred[i] + 1) - math.log(y[i] + 1)) ** 2.0 for i,pred in enumerate(y_pred)]
    return (sum(to_sum) * (1.0/len(y))) ** 0.5

Что я делаю неправильно? Спасибо!

EDIT: настройка axis=0 кажется очень близкой к правильной, но я не уверен, поскольку весь код, который я, по-видимому, использует axis=-1.

Ответ 1

Я столкнулся с той же проблемой и искал ее, вот что я нашел

https://www.kaggle.com/jpopham91/rmlse-vectorized

После изменения бит это работает для меня, rmsle_K метод реализован с Keras и TensorFlow.

import numpy as np
import math
from keras import backend as K
import tensorflow as tf

def rmsle(y, y0):
    assert len(y) == len(y0)
    return np.sqrt(np.mean(np.power(np.log1p(y)-np.log1p(y0), 2)))

def rmsle_loop(y, y0):
    assert len(y) == len(y0)
    terms_to_sum = [(math.log(y0[i] + 1) - math.log(y[i] + 1)) ** 2.0 for i,pred in enumerate(y0)]
    return (sum(terms_to_sum) * (1.0/len(y))) ** 0.5

def rmsle_K(y, y0):
    return K.sqrt(K.mean(K.square(tf.log1p(y) - tf.log1p(y0))))

r = rmsle(y=[5, 20, 12], y0=[8, 16, 12])
r1 = rmsle_loop(y=[5, 20, 12], y0=[8, 16, 12])
r2 = rmsle_K(y=[5., 20., 12.], y0=[8., 16., 12.])

print(r)

print(r1)

sess = tf.Session()

print(sess.run(r2))

Результат:

Использование бэкэнда TensorFlow

0.263978210565

0.263978210565

0.263978

Ответ 2

Используя список (to_sum) в реализации numpy, я подозреваю, что ваш массив numpy имеет форму (length,).

И на Keras, поскольку у вас разные результаты с axis=0 и axis=1, вы, вероятно, получили некоторую форму, например (length,1).

Кроме того, при создании списка to_sum вы используете y[i] и y_pred[i], что означает, что вы принимаете элементы из axis=0 в реализации numpy.

Реализация numpy также суммирует все для вычисления среднего значения в sum(to_sum). Таким образом, вам действительно не нужно использовать axis в K.mean.

Если вы убедитесь, что ваша форма вывода модели имеет значение (length,) или (length,1), вы можете использовать только K.mean(value), не передавая параметр оси.