自定义损失函数和指标¶
Note
自定损失函数和指标是常见的需求,我们看看在tensorflow中如何完成它们。
函数定义¶
import tensorflow as tf
def huber_fn(y_true, y_pred):
"""Huber损失: 小时l2大时l1"""
error = y_true - y_pred
is_small_error = tf.abs(error) < 1
squared_loss = tf.square(error) / 2
linear_loss = tf.abs(error) - 0.5
# tf.where(cond, a, b): a if cond else b
return tf.where(is_small_error, squared_loss, linear_loss)
from tensorflow import keras
import utils
model = keras.models.load_model("my_housing_model/")
# 像其他损失函数一样使用
model.compile(loss=huber_fn, optimizer="nadam")
子类定义¶
通过创建 keras.losses.Loss 类的子类来定义。
class HuberLoss(keras.losses.Loss):
def __init__(self, threshold=1.0, **kwargs):
super().__init__(**kwargs)
self.threshold = threshold
def call(self, y_true, y_pred):
# 计算损失函数
error = y_true - y_pred
is_small_error = tf.abs(error) < self.threshold
squared_loss = tf.square(error) / 2
linear_loss = self.threshold * tf.abs(error) - self.threshold**2 / 2
return tf.where(is_small_error, squared_loss, linear_loss)
def get_config(self):
# 获得超参数
base_config = super().get_config()
return {**base_config, "threshold": self.threshold}
# 一样的使用
model.compile(loss=HuberLoss(2.0), optimizer="nadam")
指标¶
损失和指标不是一回事。
损失在梯度下降中用于训练模型,必须可微。
指标用于评估模型,必须更容易被解释。
但有时候,损失也可以作为指标,比如说 HuberLoss。
def create_huber(threshold=1.0):
"""带阈值的Huber损失"""
def huber_fn(y_true, y_pred):
error = y_true - y_pred
is_small_error = tf.abs(error) < threshold
squared_loss = tf.square(error) / 2
linear_loss = threshold * tf.abs(error) - threshold**2 / 2
return tf.where(is_small_error, squared_loss, linear_loss)
return huber_fn
class HuberMetric(keras.metrics.Metric):
"""Huber指标,继承自keras.metrics.Metric"""
def __init__(self, threshold=1.0, **kwargs):
# handles base args (e.g., dtype)
super().__init__(**kwargs)
self.threshold = threshold
# 计算损失的函数
self.huber_fn = create_huber(threshold)
# 损失总值和总次数
self.total = self.add_weight("total", initializer="zeros")
self.count = self.add_weight("count", initializer="zeros")
def update_state(self, y_true, y_pred, sample_weight=None):
# 给定一个批次的标签和预测值,如何更新变量
metric = self.huber_fn(y_true, y_pred)
self.total.assign_add(tf.reduce_sum(metric))
self.count.assign_add(tf.cast(tf.size(y_true), tf.float32))
def result(self):
# 计算并返回最终结果
return self.total / self.count
def get_config(self):
base_config = super().get_config()
return {**base_config, "threshold": self.threshold}
# 在编译时指定metrics
model.compile(loss=create_huber(2.0), optimizer="nadam", metrics=[HuberMetric(3.0)])