Keras 3 API 文件 / 損失函數

損失函數

損失函數的目的是計算模型在訓練期間應盡力最小化的量。

可用的損失函數

請注意,所有損失函數都可以透過類別控制代碼和函數控制代碼取得。類別控制代碼可讓您將配置引數傳遞給建構函式 (例如 loss_fn = CategoricalCrossentropy(from_logits=True)),而且當以獨立方式使用時,它們預設會執行縮減 (請參閱下方的詳細資訊)。

機率損失函數

迴歸損失函數

用於「最大邊界」分類的 Hinge 損失函數


基本損失函數 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.floatx()"float32",除非設定為不同的值 (透過 keras.backend.set_floatx())。如果提供 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() 文件