【问题标题】:Custom metrics with keras using oop使用 oop 使用 keras 自定义指标
【发布时间】:2019-03-18 01:11:16
【问题描述】:

我正在尝试将自定义指标传递给 keras.compile。我也在学习 OOP 并尝试将其应用于机器学习。我想做的也是跟踪每个时期的 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
        )

它也可以通过传入 Metrics.f1_score 来在没有 def init 部分的情况下工作,但为什么它不能用于初始化?

如果我传入 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'

如果我传入 Metrics().f1_score 我会得到:

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

如果我传入 Metrics().f1_score() 我会得到:

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

【问题讨论】:

    标签: oop keras


    【解决方案1】:

    恐怕你做不到。 Keras 需要一个带有 2 个参数(y_true、y_pred)的函数。您正在传递一个带有 1 个参数(self)的函数,因此它永远不会兼容。您无法更改此行为,因为定义此接口的是 keras。这就是你得到所有错误的原因:

    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'
    

    您正在用 0 个参数实例化一个 Metrics 对象,但您的构造函数 (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 传递给Keras。这个 Metrics 类与将所有这 3 个静态方法作为模块级函数几乎没有区别,它只是将相关功能组合在一起的一种不同方式。甚至还有第三种方法:使用嵌套函数并完全删除类:

    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
    

    【讨论】:

      猜你喜欢
      • 2019-01-24
      • 2021-03-26
      • 2017-10-02
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 2020-03-04
      • 2017-09-20
      相关资源
      最近更新 更多