Пользовательские метрики с керасом с использованием oop - PullRequest
1 голос
/ 17 марта 2019

Я пытаюсь передать пользовательские метрики в keras.compile. Я также изучаю ООП и пытаюсь применить его к машинному обучению. То, что я хочу сделать, это также отслеживать f1, точность и отзыв за эпоху.

Я могу передать, например, f1, возврат и точность функции как отдельные функции, но не как класс с методом init.

Вот что я пытался сделать:

class Metrics:

    def __init__(self, y_true, y_pred):
        self.y_true = y_true
        self.y_pred = y_pred
        self.tp = K.sum(K.cast(y_true * y_pred, 'float'), axis=0)
        self.fp = K.sum(K.cast((1 - y_true) * y_pred, 'float'), axis=0)
        self.fn = K.sum(K.cast(y_true*(1 - y_pred), 'float'), axis=0)

    def precision_score(self):
        precision = self.tp / (self.tp + self.fp + K.epsilon())
        return precision

    def recall_score(self):
        recall = self.tp / (self.tp + self.fn + K.epsilon())
        return recall

    def f1_score(self):
        precision = precision_score(self.y_true, self.y_pred)
        recall = recall_score(self.y_true, self.y_pred)

        f1 = 2 * precision * recall / (precision + recall + K.epsilon())
        f1 = tf.where(tf.is_nan(f1), tf.zeros_like(f1), f1)

        f1 = K.mean(f1)

        return f1



if __name__ == '__main__':

    # Some images
    train_generator = DataGenerator().create_data()
    validation_generator = DataGenerator().create_data()

    model = create_model(
        input_shape = INPUT_SHAPE, 
        n_out = N_CLASSES)

    model.compile(
        loss = 'binary_crossentropy',  
        optimizer = Adam(0.03),

        # This is the part in question:
        metrics = ['acc', Metrics.f1_score, Metrics.recall_score,     Metrics.precision_score]
        )

    history = model.fit_generator(
        train_generator,
        steps_per_epoch = 5, 
        epochs = 5,
        validation_data = next(validation_generator),
        validation_steps = 7,
        verbose = 1
        )

Он также работает без части def init , передавая Metrics.f1_score, но почему он не работает с инициализацией?

Если я передаю Metrics.f1_score, я получаю:

TypeError: f1_score() takes 1 positional argument but 2 were given

Если я передаю Metrics.f1_score (), я получаю:

TypeError: f1_score() missing 1 required positional argument: 'self'

Если я передам Метрику (). F1_score, я получу:

TypeError: __init__() missing 2 required positional arguments: 'y_true' and 'y_pred'

Если я передаю Метрики (). F1_score () я получаю:

TypeError: __init__() missing 2 required positional arguments: 'y_true' and 'y_pred'

1 Ответ

1 голос
/ 17 марта 2019

Боюсь, ты не сможешь этого сделать. Keras ожидает функцию, которая принимает 2 аргумента (y_true, y_pred). Вы передаете функцию, которая принимает 1 аргумент (self), поэтому она никогда не будет совместимой. Вы не можете изменить это поведение, потому что именно керасы определяют этот интерфейс. Вот почему вы получаете все ошибки:

TypeError: f1_score() takes 1 positional argument but 2 were given

Вы передали функцию, которая принимает 1 аргумент (self), но Keras передал 2 (y_true, y_pred).

TypeError: f1_score() missing 1 required positional argument: 'self'

Передавая с (), вы на самом деле не передаете функцию, а вызываете . Вы назвали это без аргументов, но он ожидает 1 (self).

TypeError: __init__() missing 2 required positional arguments: 'y_true' and 'y_pred'

Вы создаете экземпляр объекта Metrics с 0 аргументами, но ваш конструктор ( init ) ожидает 2: y_true и y_pred.

Если вы хотите сгруппировать все ваши собственные метрики в классе, они должны быть статическими методами. Статический метод не может получить доступ к переменным экземпляра, потому что он не получает аргумент self. Это будет выглядеть так:

class Metrics:
    @staticmethod
    def precision_score(tp, fp):
        precision = tp / (tp + fp + K.epsilon())
        return precision

    @staticmethod
    def recall_score(tp, fn):
        recall = tp / (tp + fn + K.epsilon())
        return recall

    @staticmethod
    def f1_score(y_true,y_pred):

        tp = K.sum(K.cast(y_true * y_pred, 'float'), axis=0)
        fp = K.sum(K.cast((1 - y_true) * y_pred, 'float'), axis=0)
        fn = K.sum(K.cast(y_true*(1 - y_pred), 'float'), axis=0)

        precision = Metrics.precision_score(tp,fp)
        recall = Metrics.recall_score(tp, fn)

        f1 = 2 * precision * recall / (precision + recall + K.epsilon())
        f1 = tf.where(tf.is_nan(f1), tf.zeros_like(f1), f1)

        f1 = K.mean(f1)

        return f1

Таким образом, вы можете передать Metrics.f1_score Керасу. Практически нет разницы между этим классом Metrics и наличием всех этих трех статических методов в качестве функций уровня модуля, это просто другой способ сгруппировать связанные функции вместе. Есть даже третий способ: использовать вложенные функции и вообще удалить класс:

def f1_score(y_true,y_pred):

    def precision_score(tp, fp):
        precision = tp / (tp + fp + K.epsilon())
        return precision

    def recall_score(tp, fn):
        recall = tp / (tp + fn + K.epsilon())
        return recall

    tp = K.sum(K.cast(y_true * y_pred, 'float'), axis=0)
    fp = K.sum(K.cast((1 - y_true) * y_pred, 'float'), axis=0)
    fn = K.sum(K.cast(y_true*(1 - y_pred), 'float'), axis=0)

    precision = precision_score(tp,fp)
    recall = recall_score(tp, fn)

    f1 = 2 * precision * recall / (precision + recall + K.epsilon())
    f1 = tf.where(tf.is_nan(f1), tf.zeros_like(f1), f1)

    f1 = K.mean(f1)

    return f1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...