Як визначити спеціальний показник ефективності в Керасі?


12

Я спробував визначити власну метричну фукцію (F1-Score) у Керасі (тензорфлоу), відповідно до наступного:

def f1_score(tags, predicted):

    tags = set(tags)
    predicted = set(predicted)

    tp = len(tags & predicted)
    fp = len(predicted) - tp 
    fn = len(tags) - tp

    if tp>0:
        precision=float(tp)/(tp+fp)
        recall=float(tp)/(tp+fn)
        return 2*((precision*recall)/(precision+recall))
    else:
        return 0

Поки що так добре, але коли я намагаюся застосувати це при компіляції моделі:

model1.compile(loss="binary_crossentropy", optimizer=Adam(), metrics=[f1_score])

це дає помилку:

TypeError                                 Traceback (most recent call last)
<ipython-input-85-4eca4def003f> in <module>()
      5 model1.add(Dense(output_dim=10, activation="sigmoid"))
      6 
----> 7 model1.compile(loss="binary_crossentropy", optimizer=Adam(), metrics=[f1_score])
      8 
      9 h=model1.fit(X_train, Y_train, batch_size=500, nb_epoch=5, verbose=True, validation_split=0.1)

/home/buda/anaconda2/lib/python2.7/site-packages/keras/models.pyc in compile(self, optimizer, loss, metrics, sample_weight_mode, **kwargs)
    522                            metrics=metrics,
    523                            sample_weight_mode=sample_weight_mode,
--> 524                            **kwargs)
    525         self.optimizer = self.model.optimizer
    526         self.loss = self.model.loss

/home/buda/anaconda2/lib/python2.7/site-packages/keras/engine/training.pyc in compile(self, optimizer, loss, metrics, loss_weights, sample_weight_mode, **kwargs)
    664                 else:
    665                     metric_fn = metrics_module.get(metric)
--> 666                     self.metrics_tensors.append(metric_fn(y_true, y_pred))
    667                     if len(self.output_names) == 1:
    668                         self.metrics_names.append(metric_fn.__name__)

<ipython-input-84-b8a5752b6d55> in f1_score(tags, predicted)
      4     #tf.convert_to_tensor(img.eval())
      5 
----> 6     tags = set(tags)
      7     predicted = set(predicted)
      8 

/home/buda/anaconda2/lib/python2.7/site-packages/tensorflow/python/framework/ops.pyc in __iter__(self)
    493       TypeError: when invoked.
    494     """
--> 495     raise TypeError("'Tensor' object is not iterable.")
    496 
    497   def __bool__(self):

TypeError: 'Tensor' object is not iterable.

У чому тут проблема? Те, що вхідні функції моєї f1_score - це не масиви Tensorflow? Якщо так, то де / як я можу їх правильно перетворити?


Гм, повідомлення про помилку означає, що ви отримуєте об'єкти тензора. Можливо, вам потрібен евал, зрештою! Якщо так, ваша помилка, ймовірно, буде використовуватись, evalколи ви маєте на увазіeval()
Ніл Слейтер

Відповіді:


17

Ви повинні використовувати функції Keras backkend . На жаль, вони не підтримують &оператора, так що вам доведеться побудувати обхідний шлях: ми генеруємо матриці виміру batch_size x 3, де (наприклад, для справжнього додатного) перший стовпець є основним вектором істини, другий - фактичним прогнозом, а третій - вид стовпчика-помічника, що містить у випадку справжніх позитивних лише ті. Потім ми перевіряємо, які екземпляри є позитивними, чи прогнозуються як позитивні, а помічник міток - також як позитивний. Це справжні позитиви.

Ми можемо зробити цей аналог з помилковими позитивами, помилковими негативами та справжніми негативами з деякими зворотними розрахунками міток.

Ваш показник f1 може виглядати наступним чином:

def f1_score(y_true, y_pred):
    """
    f1 score

    :param y_true:
    :param y_pred:
    :return:
    """
    tp_3d = K.concatenate(
        [
            K.cast(y_true, 'bool'),
            K.cast(K.round(y_pred), 'bool'),
            K.cast(K.ones_like(y_pred), 'bool')
        ], axis=1
    )

    fp_3d = K.concatenate(
        [
            K.cast(K.abs(y_true - K.ones_like(y_true)), 'bool'),
            K.cast(K.round(y_pred), 'bool'),
            K.cast(K.ones_like(y_pred), 'bool')
        ], axis=1
    )

    fn_3d = K.concatenate(
        [
            K.cast(y_true, 'bool'),
            K.cast(K.abs(K.round(y_pred) - K.ones_like(y_pred)), 'bool'),
            K.cast(K.ones_like(y_pred), 'bool')
        ], axis=1
    )

    tp = K.sum(K.cast(K.all(tp_3d, axis=1), 'int32'))
    fp = K.sum(K.cast(K.all(fp_3d, axis=1), 'int32'))
    fn = K.sum(K.cast(K.all(fn_3d, axis=1), 'int32'))

    precision = tp / (tp + fp)
    recall = tp / (tp + fn)
    return 2 * ((precision * recall) / (precision + recall))

Оскільки калькулятор Keras-backkend повертає nan для поділу на нуль, нам не потрібен оператор if-else для оператора return.

Редагувати: Я знайшов досить гарну ідею для точної реалізації. Проблема нашого першого підходу полягає в тому, що він лише "наближений", оскільки він обчислюється послідовно і згодом усереднюється. Можна було також обчислити це після кожної епохи з keras.callbacks. Знайдіть ідею тут: https://github.com/fchollet/keras/isissue/5794

Прикладом реалізації може бути:

import keras
import numpy as np
import sklearn.metrics as sklm


class Metrics(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.confusion = []
        self.precision = []
        self.recall = []
        self.f1s = []
        self.kappa = []
        self.auc = []

    def on_epoch_end(self, epoch, logs={}):
        score = np.asarray(self.model.predict(self.validation_data[0]))
        predict = np.round(np.asarray(self.model.predict(self.validation_data[0])))
        targ = self.validation_data[1]

        self.auc.append(sklm.roc_auc_score(targ, score))
        self.confusion.append(sklm.confusion_matrix(targ, predict))
        self.precision.append(sklm.precision_score(targ, predict))
        self.recall.append(sklm.recall_score(targ, predict))
        self.f1s.append(sklm.f1_score(targ, predict))
        self.kappa.append(sklm.cohen_kappa_score(targ, predict))

        return

Щоб змусити мережу викликати цю функцію, ви просто додати її до зворотних викликів

metrics = Metrics()
model.fit(
    train_instances.x,
    train_instances.y,
    batch_size,
    epochs,
    verbose=2,
    callbacks=[metrics],
    validation_data=(valid_instances.x, valid_instances.y),
)

Тоді ви можете просто отримати доступ до членів metricsзмінної.


4
Дякую, це вже було дуже корисно. Чи знаєте ви, як включити спеціальні показники до зворотного виклику тензорних плат, щоб їх можна було контролювати під час навчання?
Н.Кайзер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.