Keras 3 API 文档 / 损失函数

损失函数

损失函数的作用是计算模型在训练期间应努力最小化的量。

可用的损失函数

请注意,所有损失函数都可以通过类句柄和函数句柄使用。类句柄允许您将配置参数传递给构造函数(例如,loss_fn = CategoricalCrossentropy(from_logits=True)),并且它们在单独使用时默认执行缩减(请参见下面的详细信息)。

概率损失函数

回归损失函数

用于“最大间隔”分类的合页损失函数


基础损失 API

[来源]

Loss

keras.losses.Loss(name=None, reduction="sum_over_batch_size", dtype=None)

损失基类。

这是为了创建新的自定义损失而需要子类化的类。

参数

  • reduction:应用于损失的缩减类型。在几乎所有情况下,这都应为 "sum_over_batch_size"。支持的选项为 "sum""sum_over_batch_size""mean""mean_with_sample_weight"None"sum" 对损失求和,"sum_over_batch_size""mean" 对损失求和并除以样本大小,而 "mean_with_sample_weight" 对损失求和并除以样本权重的总和。"none"None 不执行聚合。默认为 "sum_over_batch_size"
  • name:损失实例的可选名称。
  • dtype:损失计算的 dtype。默认为 None,这意味着使用 keras.backend.floatx()。除非设置为不同的值(通过 keras.backend.set_floatx()),否则 keras.backend.floatx()"float32"。如果提供了 keras.DTypePolicy,则将使用 compute_dtype

要由子类实现

  • call():包含使用 y_truey_pred 计算损失的逻辑。

子类实现的示例

class MeanSquaredError(Loss):
    def call(self, y_true, y_pred):
        return ops.mean(ops.square(y_pred - y_true), axis=-1)


损失函数与 compile()fit() 的用法

损失函数是编译 Keras 模型所需的两个参数之一

import keras
from keras import layers

model = keras.Sequential()
model.add(layers.Dense(64, kernel_initializer='uniform', input_shape=(10,)))
model.add(layers.Activation('softmax'))

loss_fn = keras.losses.SparseCategoricalCrossentropy()
model.compile(loss=loss_fn, optimizer='adam')

所有内置损失函数也可以通过其字符串标识符传递

# pass optimizer by name: default parameters will be used
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')

损失函数通常通过实例化损失类来创建(例如,keras.losses.SparseCategoricalCrossentropy)。所有损失也作为函数句柄提供(例如,keras.losses.sparse_categorical_crossentropy)。

使用类可以在实例化时传递配置参数,例如

loss_fn = keras.losses.SparseCategoricalCrossentropy(from_logits=True)

损失函数的独立用法

损失是一个可调用对象,其参数为 loss_fn(y_true, y_pred, sample_weight=None)

  • y_true:真实值,形状为 (batch_size, d0, ... dN)。对于稀疏损失函数(例如,稀疏分类交叉熵),形状应为 (batch_size, d0, ... dN-1)
  • y_pred:预测值,形状为 (batch_size, d0, .. dN)
  • sample_weight:可选的 sample_weight 充当每个样本损失的缩减权重系数。如果提供了标量,则损失仅按给定值缩放。如果 sample_weight 是大小为 [batch_size] 的张量,则批次中每个样本的总损失将按 sample_weight 向量中的相应元素重新缩放。如果 sample_weight 的形状为 (batch_size, d0, ... dN-1)(或可以广播到此形状),则 y_pred 的每个损失元素都按 sample_weight 的相应值缩放。(dN-1 的说明:所有损失函数都会减少 1 个维度,通常为 axis=-1。)

默认情况下,损失函数为批次维度中的每个输入样本返回一个标量损失值,例如

>>> from keras import ops
>>> keras.losses.mean_squared_error(ops.ones((2, 2,)), ops.zeros((2, 2)))
<Array: shape=(2,), dtype=float32, numpy=array([1., 1.], dtype=float32)>

但是,损失类实例具有一个 reduction 构造函数参数,该参数默认为 "sum_over_batch_size"(即平均值)。允许的值为 "sum_over_batch_size"、"sum" 和 "none"

  • "sum_over_batch_size" 表示损失实例将返回批次中每个样本损失的平均值。
  • "sum" 表示损失实例将返回批次中每个样本损失的总和。
  • "none" 表示损失实例将返回每个样本损失的完整数组。
>>> loss_fn = keras.losses.MeanSquaredError(reduction='sum_over_batch_size')
>>> loss_fn(ops.ones((2, 2,)), ops.zeros((2, 2)))
<Array: shape=(), dtype=float32, numpy=1.0>
>>> loss_fn = keras.losses.MeanSquaredError(reduction='sum')
>>> loss_fn(ops.ones((2, 2,)), ops.zeros((2, 2)))
<Array: shape=(), dtype=float32, numpy=2.0>
>>> loss_fn = keras.losses.MeanSquaredError(reduction='none')
>>> loss_fn(ops.ones((2, 2,)), ops.zeros((2, 2)))
<Array: shape=(2,), dtype=float32, numpy=array([1., 1.], dtype=float32)>

请注意,这是诸如 keras.losses.mean_squared_error 之类的损失函数与诸如 keras.losses.MeanSquaredError 之类的默认损失类实例之间的重要区别:函数版本不执行缩减,但默认情况下类实例执行缩减。

>>> loss_fn = keras.losses.mean_squared_error
>>> loss_fn(ops.ones((2, 2,)), ops.zeros((2, 2)))
<Array: shape=(2,), dtype=float32, numpy=array([1., 1.], dtype=float32)>
>>> loss_fn = keras.losses.MeanSquaredError()
>>> loss_fn(ops.ones((2, 2,)), ops.zeros((2, 2)))
<Array: shape=(), dtype=float32, numpy=1.0>

当使用 fit() 时,这种差异无关紧要,因为缩减由框架处理。

以下是如何在简单训练循环中使用损失类实例的方法

loss_fn = keras.losses.CategoricalCrossentropy(from_logits=True)
optimizer = keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for x, y in dataset:
    with tf.GradientTape() as tape:
        logits = model(x)
        # Compute the loss value for this batch.
        loss_value = loss_fn(y, logits)

    # Update the weights of the model to minimize the loss value.
    gradients = tape.gradient(loss_value, model.trainable_weights)
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))

创建自定义损失函数

任何具有签名 loss_fn(y_true, y_pred) 并返回损失数组(输入批次中的一个样本)的可调用对象都可以作为损失传递给 compile()。请注意,任何此类损失都会自动支持样本加权。

这是一个简单的示例

from keras import ops

def my_loss_fn(y_true, y_pred):
    squared_difference = ops.square(y_true - y_pred)
    return ops.mean(squared_difference, axis=-1)  # Note the `axis=-1`

model.compile(optimizer='adam', loss=my_loss_fn)

add_loss() API

应用于模型输出的损失函数不是创建损失的唯一方法。

在编写自定义层或子类化模型的 call 方法时,您可能希望计算要在训练期间最小化的标量值(例如,正则化损失)。您可以使用 add_loss() 层方法来跟踪此类损失项。

以下是一个层示例,该层基于输入的 L2 范数添加稀疏正则化损失

from keras import ops

class MyActivityRegularizer(keras.layers.Layer):
  """Layer that creates an activity sparsity regularization loss."""

    def __init__(self, rate=1e-2):
        super().__init__()
        self.rate = rate

    def call(self, inputs):
        # We use `add_loss` to create a regularization loss
        # that depends on the inputs.
        self.add_loss(self.rate * ops.sum(ops.square(inputs)))
        return inputs

通过 add_loss 添加的损失值可以在任何 LayerModel.losses 列表属性中检索(它们会从每个底层层递归检索)

from keras import layers
from keras import ops

class SparseMLP(layers.Layer):
  """Stack of Linear layers with a sparsity regularization loss."""

  def __init__(self, output_dim):
      super().__init__()
      self.dense_1 = layers.Dense(32, activation=ops.relu)
      self.regularization = MyActivityRegularizer(1e-2)
      self.dense_2 = layers.Dense(output_dim)

  def call(self, inputs):
      x = self.dense_1(inputs)
      x = self.regularization(x)
      return self.dense_2(x)


mlp = SparseMLP(1)
y = mlp(ops.ones((10, 10)))

print(mlp.losses)  # List containing one float32 scalar

这些损失在每次正向传递开始时由顶层清除 – 它们不会累积。因此,layer.losses 始终只包含上次正向传递期间创建的损失。在编写训练循环时,您通常会在计算梯度之前对这些损失求和。

# Losses correspond to the *last* forward pass.
mlp = SparseMLP(1)
mlp(ops.ones((10, 10)))
assert len(mlp.losses) == 1
mlp(ops.ones((10, 10)))
assert len(mlp.losses) == 1  # No accumulation.

当使用 model.fit() 时,此类损失项会自动处理。

在编写自定义训练循环时,您应该像这样从 model.losses 手动检索这些项

loss_fn = keras.losses.CategoricalCrossentropy(from_logits=True)
optimizer = keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for x, y in dataset:
    with tf.GradientTape() as tape:
        # Forward pass.
        logits = model(x)
        # Loss value for this batch.
        loss_value = loss_fn(y, logits)
        # Add extra loss terms to the loss value.
        loss_value += sum(model.losses)

    # Update the weights of the model to minimize the loss value.
    gradients = tape.gradient(loss_value, model.trainable_weights)
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))

有关更多详细信息,请参见 add_loss() 文档