Bagaimana cara mendefinisikan metrik kinerja khusus di Keras?


11

Saya mencoba mendefinisikan fungsi metrik khusus (Skor-F1) di Keras (backend Tensorflow) menurut yang berikut:

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

Sejauh ini, sangat bagus, tetapi ketika saya mencoba menerapkannya dalam kompilasi model:

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

itu memberikan kesalahan:

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.

Apa masalah yang terjadi di sini? Fakta bahwa input fungsi f1_score saya bukan array Tensorflow? Jika demikian, di mana / bagaimana saya bisa mengubahnya dengan benar?


Hmm, pesan kesalahan tidak menyiratkan Anda mendapatkan objek tensor. Mungkin Anda perlu eval setelah semua! Jika demikian, kesalahan Anda kemungkinan akan digunakan evalsaat yang Anda maksudeval()
Neil Slater

Jawaban:


16

Anda harus menggunakan fungsi backend Keras . Sayangnya mereka tidak mendukung &-operator, sehingga Anda harus membangun solusi: Kami menghasilkan matriks dimensi batch_size x 3, di mana (misalnya untuk true positive) kolom pertama adalah vektor kebenaran dasar, yang kedua adalah prediksi aktual dan yang ketiga adalah semacam label-helper kolom, yang berisi dalam kasus yang benar-benar positif saja. Kemudian kami memeriksa contoh mana yang positif, diprediksi positif dan label-helpernya juga positif. Itu adalah hal positif yang sebenarnya.

Kita dapat membuat analog ini dengan positif palsu, negatif palsu dan negatif asli dengan beberapa kalkulasi terbalik label.

Metrik f1 Anda mungkin terlihat sebagai berikut:

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))

Karena kalkulator Keras-backend mengembalikan nan untuk pembagian dengan nol, kita tidak memerlukan pernyataan if-else-untuk pernyataan pengembalian.

Sunting: Saya telah menemukan ide yang cukup bagus untuk implementasi yang tepat. Masalah dengan pendekatan pertama kami adalah, bahwa itu hanya "diperkirakan", karena dihitung secara batch dan kemudian dirata-rata. Satu juga bisa menghitung ini setelah setiap zaman dengan keras.callbacks. Silakan temukan idenya di sini: https://github.com/fchollet/keras/issues/5794

Contoh implementasi adalah:

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

Untuk membuat jaringan memanggil fungsi ini, Anda cukup menambahkannya seperti panggilan balik

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),
)

Maka Anda cukup mengakses anggota metricsvariabel.


3
Terima kasih, ini sudah sangat berguna. Apakah Anda tahu cara menggabungkan metrik khusus ke dalam panggilan balik papan tensor sehingga dapat dipantau selama pelatihan?
N.Kaiser
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.