केरस में कस्टम प्रदर्शन मीट्रिक कैसे परिभाषित करें?


12

मैंने निम्नलिखित के अनुसार केरस (टेंसरफ़्लो बैकएंड) में एक कस्टम मेट्रिक फ़्यूज़न (एफ 1-स्कोर) को परिभाषित करने की कोशिश की:

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 फ़ंक्शन इनपुट टेन्सरफ़्लो सरणियाँ नहीं हैं? यदि हां, तो मैं उन्हें सही तरीके से कहां / कैसे परिवर्तित कर सकता हूं?


हम्म, त्रुटि संदेश का अर्थ है कि आप दसियों वस्तुएं प्राप्त कर रहे हैं। शायद तुम सब के बाद eval की जरूरत है! यदि ऐसा है, तो आपकी गलती का evalमतलब है कि आप का उपयोग करने की संभावना हैeval()
नील स्लेटर

जवाबों:


17

आपको केरस बैकेंड फ़ंक्शन का उपयोग करना होगा । दुर्भाग्य से वे &-ओपरेटर का समर्थन नहीं करते हैं , ताकि आपको वर्कअराउंड का निर्माण करना पड़े: हम आयाम के मैट्रिक्स उत्पन्न करते हैं batch_size x 3, जहां (उदाहरण के लिए सही पॉजिटिव) पहला कॉलम ग्राउंड ट्रूथ वेक्टर है, दूसरा वास्तविक भविष्यवाणी और तीसरा है एक तरह का लेबल-हेल्पर कॉलम, जिसमें केवल सही पॉजिटिव के मामले होते हैं। फिर हम जांचते हैं कि कौन से उदाहरण सकारात्मक उदाहरण हैं, सकारात्मक के रूप में भविष्यवाणी की जाती है और लेबल-सहायक भी सकारात्मक है। वे ही सच्चे सकारात्मक हैं।

हम लेबल के कुछ रिवर्स-कैलकुलेशन के साथ झूठी सकारात्मक, गलत नकारात्मक और सच्चे नकारात्मक के साथ इस अनुरूप बना सकते हैं।

आपका f1-metric इस प्रकार लग सकता है:

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

चूंकि केरेस-बैकेंड कैलकुलेटर शून्य द्वारा विभाजन के लिए नान देता है, हमें रिटर्न स्टेटमेंट के लिए if-else-statement की आवश्यकता नहीं है।

संपादित करें: मुझे एक सटीक कार्यान्वयन के लिए एक बहुत अच्छा विचार मिला है। हमारे पहले दृष्टिकोण के साथ समस्या यह है कि यह केवल "अनुमानित" है, क्योंकि इसे बैचवाइज और बाद में औसत रूप से गणना की जाती है। प्रत्येक व्यक्ति के साथ प्रत्येक युग के बाद इसकी गणना भी कर सकता है keras.callback। कृपया इस विचार को यहां देखें: https://github.com/fchollet/keras/issues/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
धन्यवाद, यह वास्तव में पहले से ही उपयोगी रहा है। क्या आप जानते हैं कि कस्टम मेट्रिक्स को टेंसरबोर्ड कॉलबैक में कैसे शामिल किया जाए ताकि प्रशिक्षण के दौरान उन पर नजर रखी जा सके?
N.Kaiser
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.