keras.metrics解析

前言

metrics用于判断模型性能。度量函数类似于损失函数,只是度量的结果不用于训练模型。可以使用任何损失函数作为度量(如logloss等)。在训练期间监控metrics的最佳方式是通过Tensorboard。

官方提供的metrics最重要的概念就是有状态(stateful)变量,通过更新状态变量,可以不断累积统计数据,并可以随时输出状态变量的计算结果。这是区别于losses的重要特性,losses是无状态的(stateless)。

本文部分内容参考了:

代码运行环境为:tf.__version__==2.6.2 。

metrics原理解析(以metrics.Mean为例)

metrics是有状态的(stateful),即Metric 实例会存储、记录和返回已经累积的结果,有助于未来事务的信息。下面以tf.keras.metrics.Mean()为例进行解释:

创建tf.keras.metrics.Mean的实例:

m = tf.keras.metrics.Mean()

通过help(m) 可以看到MRO为:

Mean
Reduce
Metric
keras.engine.base_layer.Layer
...

可见Metric和Mean是 keras.layers.Layer 的子类。相比于类Layer,其子类Mean多出了几个方法:

  • r e s u l t \mathsf{result} result: 计算并返回标量度量值(tensor形式)或标量字典,即状态变量简单地计算度量值。例如,m.result(),就是计算均值并返回
  • t o t a l \mathsf{total} total: 状态变量m目前累积的数字总和
  • c o u n t \mathsf{count} count: 状态变量m目前累积的数字个数(m.total/m.count就是m.result()的返回值)
  • u p d a t e _ s t a t e \mathsf{update\_state} update_state: 累积统计数字用于计算指标。每次调用m.update_state都会更新m.totalm.count
  • r e s e t _ s t a t e \mathsf{reset\_state} reset_state: 将状态变量重置到初始化状态;
  • r e s e t _ s t a t e s \mathsf{reset\_states} reset_states: 等价于reset_state,参见keras源代码metrics.py L355
  • r e d u c t i o n \mathsf{reduction} reduction: 目前来看,没什么用。

这也决定了Mean的特殊性质。其使用参见如下代码:

# 创建状态变量m,由于m未刚初始化,
# 所以total,count和result()均为0
m = tf.keras.metrics.Mean()
print("m.total:",m.total)
print("m.count:",m.count)
print("m.result():",m.result())

"""
# 输出:
m.total: <tf.Variable 'total:0' shape=() dtype=float32, numpy=0.0>
m.count: <tf.Variable 'count:0' shape=() dtype=float32, numpy=0.0>
m.result(): tf.Tensor(0.0, shape=(), dtype=float32)
"""
# 更新状态变量,可以看到total累加了总和,
# count累积了个数,result()返回total/count
m.update_state([1,2,3])
print("m.total:",m.total)
print("m.count:",m.count)
print("m.result():",m.result())

"""
# 输出:
m.total: <tf.Variable 'total:0' shape=() dtype=float32, numpy=6.0>
m.count: <tf.Variable 'count:0' shape=() dtype=float32, numpy=3.0>
m.result(): tf.Tensor(2.0, shape=(), dtype=float32)
"""
# 重置状态变量, 重置到初始化状态
m.reset_state()
print("m.total:",m.total)
print("m.count:",m.count)
print("m.result():",m.result())


"""
# 输出:
m.total: <tf.Variable 'total:0' shape=() dtype=float32, numpy=0.0>
m.count: <tf.Variable 'count:0' shape=() dtype=float32, numpy=0.0>
m.result(): tf.Tensor(0.0, shape=(), dtype=float32)
"""

创建自定义metrics

创建无状态 metrics

与损失函数类似,任何带有类似于metric_fn(y_true, y_pred)、返回损失数组(如输入一个batch的数据,会返回一个batch的损失标量)的函数,都可以作为metric传递给compile()

import tensorflow as tf
import numpy as np

inputs = tf.keras.Input(shape=(3,))
x = tf.keras.layers.Dense(4, activation=tf.nn.relu)(inputs)
outputs = tf.keras.layers.Dense(1, activation=tf.nn.softmax)(x)
model1 = tf.keras.Model(inputs=inputs, outputs=outputs)

def my_metric_fn(y_true, y_pred):
    squared_difference = tf.square(y_true - y_pred)
    return tf.reduce_mean(squared_difference, axis=-1) # shape=(None,)

model1.compile(optimizer='adam', loss='mse', metrics=[my_metric_fn])

x = np.random.random((100, 3))
y = np.random.random((100, 1))
model1.fit(x, y, epochs=3)

输出:

Epoch 1/3
4/4 [==============================] - 0s 667us/step - loss: 0.0971 - my_metric_fn: 0.0971
Epoch 2/3
4/4 [==============================] - 0s 667us/step - loss: 0.0958 - my_metric_fn: 0.0958
Epoch 3/3
4/4 [==============================] - 0s 1ms/step - loss: 0.0946 - my_metric_fn: 0.0946

注意,因为本例创建的是无状态的度量,所以上面跟踪的度量值(my_metric_fn后面的值)是每个batch的平均度量值,并不是一个epoch(完整数据集)的累积值。(这一点需要理解,这也是为什么要使用有状态度量的原因!)

值得一提的是,如果上述代码使用

model1.compile(optimizer='adam', loss='mse', metrics=["mse"])

进行compile,则输出的结果是累积的,在每个epoch结束时的结果就是整个数据集的结果,因为metrics=["mse"]是直接调用了标准库的有状态度量。


通过继承Metric创建有状态 metrics

如果想查看整个数据集的指标,就需要传入有状态的metrics,这样就会在一个epoch内累加,并在epoch结束时输出整个数据集的度量值。

创建有状态度量指标,需要创建Metric的子类,它可以跨batch维护状态,步骤如下:

  • __init__中创建状态变量(state variables)
  • 更新update_state()y_truey_pred的变量
  • result()中返回标量度量结果
  • reset_states()中清除状态
class BinaryTruePositives(tf.keras.metrics.Metric):

    def __init__(self, name='binary_true_positives', **kwargs):
        super(BinaryTruePositives, self).__init__(name=name, **kwargs)
        self.true_positives = self.add_weight(name='tp', initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
        y_true = tf.cast(y_true, tf.bool)
        y_pred = tf.cast(y_pred, tf.bool)

        values = tf.logical_and(tf.equal(y_true, True), tf.equal(y_pred, True))
        values = tf.cast(values, self.dtype)
        if sample_weight is not None:
            sample_weight = tf.cast(sample_weight, self.dtype)
            values = tf.multiply(values, sample_weight)
        self.true_positives.assign_add(tf.reduce_sum(values))

    def result(self):
        return self.true_positives

    def reset_states(self):
        self.true_positives.assign(0)

m = BinaryTruePositives()
m.update_state([0, 1, 1, 1], [0, 1, 0, 0])
print('Intermediate result:', float(m.result()))

m.update_state([1, 1, 1, 1], [0, 1, 1, 0])
print('Final result:', float(m.result()))

add_metric() 方法

add_metric 方法是 tf.keras.layers.Layer类添加的方法,Layer的父类tf.Module并没有这个方法,因此在编写Layer子类如包括自定义层、官方提供的层(Dense)或模型(tf.keras.Model也是Layer的子类)时,可以使用add_metric()来与层相关的统计量。比如,将类似Dense的自定义层的激活平均值记录为metric。可以执行以下操作:

class DenseLike(Layer):
    """y = w.x + b"""
    ...
    def call(self, inputs):
        output = tf.matmul(inputs, self.w) + self.b
        self.add_metric(tf.reduce_mean(output), aggregation='mean', name='activation_mean')
        return output

将在名称为activation_mean的度量下跟踪output,跟踪的值为每个批次度量值的平均值。

更详细的信息,参阅官方文档The base Layer class - add_metric method

参考

Keras-Metrics官方文档

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码使用了 TensorFlow 和 Keras 库来创建、编译、训练和保存一个基本的卷积神经网络模型。以下是代码的详细解析: 1. 导入库 ```python import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers ``` 这里导入了 TensorFlow 和 Keras 库以及其中的一些模块和类。 2. 创建模型 ```python model = keras.Sequential([ layers.Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)), layers.MaxPooling2D((2, 2)), layers.Conv2D(64, (3, 3), activation='relu'), layers.MaxPooling2D((2, 2)), layers.Conv2D(64, (3, 3), activation='relu'), layers.Flatten(), layers.Dense(64, activation='relu'), layers.Dense(6, activation='softmax') ]) ``` 这段代码创建了一个序列模型,它由一系列层组成。首先是一个卷积层,它包含 32 个过滤器(即输出通道数),每个过滤器大小为 3x3,激活函数为 ReLU。这层的输入形状为 (224, 224, 3),即图像大小为 224x224,通道数为 3。接着是一个最大池化层,它的池化窗口大小为 2x2。然后再次添加一个卷积层,它包含 64 个过滤器,大小为 3x3,ReLU 激活函数。接着是另一个最大池化层。最后添加一个具有 64 个神经元和 ReLU 激活函数的密集层,以及一个具有 6 个神经元和 softmax 激活函数的输出层。 3. 编译模型 ```python model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) ``` 这段代码编译了模型,指定了优化器、损失函数和评估指标。这里使用 Adam 优化器,交叉熵损失函数和准确率评估指标。 4. 加载数据集 ```python train_dataset = keras.preprocessing.image_dataset_from_directory( 'path/to/training/folder', validation_split=0.2, subset='training', seed=123, image_size=(224, 224), batch_size=32) validation_dataset = keras.preprocessing.image_dataset_from_directory( 'path/to/training/folder', validation_split=0.2, subset='validation', seed=123, image_size=(224, 224), batch_size=32) ``` 这段代码使用 Keras 提供的 `image_dataset_from_directory` 函数从文件夹中加载训练和验证数据集。这里指定了数据集所在文件夹的路径、验证集占总数据集的比例、随机种子、图像大小和批量大小等参数。 5. 训练模型 ```python model.fit(train_dataset, epochs=10, validation_data=validation_dataset) ``` 这段代码使用 `fit` 函数对模型进行训练,指定了训练数据集、训练轮数和验证数据集等参数。 6. 保存模型 ```python model.save('path/to/save/model') ``` 这段代码使用 `save` 函数将训练好的模型保存到指定路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值