使用oop

时间:2019-03-17 16:38:39

标签: oop keras

我正在尝试将自定义指标传递给keras.compile。我也在学习OOP,并尝试将其应用于机器学习。我还想跟踪每个时期的f1,精度和召回率。

例如,我可以将f1,调用和precision作为单独的函数传递给该函数,但不能将其作为带有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'

1 个答案:

答案 0 :(得分:1)

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

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

您传递了一个带有1个参数(自身)的函数,而Keras传递了2个(y_true,y_pred)。

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

通过用()传递它,实际上并不是传递函数,而是调用。您不带参数调用它,但是期望值为1(自己)。

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