開發者指南 / 儲存、序列化和匯出模型

儲存、序列化和匯出模型

作者:Neel Kovelamudi, Francois Chollet
建立日期 2023/06/14
上次修改日期 2023/06/30
說明:儲存、序列化和匯出模型的完整指南。

在 Colab 中檢視 GitHub 來源


簡介

一個 Keras 模型由多個組件組成

  • 架構或組態,指定模型包含哪些層,以及它們如何連接。
  • 一組權重值(「模型狀態」)。
  • 一個最佳化器(透過編譯模型定義)。
  • 一組損失和指標(透過編譯模型定義)。

Keras API 將所有這些部分以統一的格式儲存在一起,並以 .keras 副檔名標記。這是一個 zip 壓縮檔,包含以下內容

  • 基於 JSON 的組態檔 (config.json):模型、層和其他可追蹤物件組態的記錄。
  • 基於 H5 的狀態檔,例如 model.weights.h5(適用於整個模型),具有用於層及其權重的目錄鍵。
  • 一個 JSON 格式的中繼資料檔案,儲存諸如目前 Keras 版本之類的東西。

讓我們來看看這是如何運作的。


如何儲存和載入模型

如果您只有 10 秒的時間閱讀本指南,以下是您需要知道的內容。

儲存 Keras 模型

model = ...  # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location.keras')  # The file needs to end with the .keras extension

將模型載入回去

model = keras.models.load_model('path/to/location.keras')

現在,讓我們看看細節。


設定

import numpy as np
import keras
from keras import ops

儲存

本節介紹如何將整個模型儲存到單一檔案中。該檔案將包含

  • 模型的架構/組態
  • 模型的權重值(在訓練期間學習到的)
  • 模型的編譯資訊(如果呼叫了 compile()
  • 最佳化器及其狀態(如果有的話)(這使您可以從上次停止的地方重新開始訓練)

API

您可以使用 model.save()keras.models.save_model()(這是等效的)儲存模型。您可以使用 keras.models.load_model() 將其載入回去。

Keras 3 中唯一支援的格式是「Keras v3」格式,它使用 .keras 副檔名。

範例

def get_model():
    # Create a simple model.
    inputs = keras.Input(shape=(32,))
    outputs = keras.layers.Dense(1)(inputs)
    model = keras.Model(inputs, outputs)
    model.compile(optimizer=keras.optimizers.Adam(), loss="mean_squared_error")
    return model


model = get_model()

# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)

# Calling `save('my_model.keras')` creates a zip archive `my_model.keras`.
model.save("my_model.keras")

# It can be used to reconstruct the model identically.
reconstructed_model = keras.models.load_model("my_model.keras")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)
 4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step - loss: 0.4232  
 4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 281us/step
 4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 373us/step

自訂物件

本節涵蓋在 Keras 儲存和重新載入中處理自訂層、函式和模型的基本工作流程。

當儲存包含自訂物件(例如子類別化的層)的模型時,您必須在物件類別上定義 get_config() 方法。如果傳遞給自訂物件的建構函式 (__init__() 方法) 的引數不是 Python 物件(任何基本類型(如整數、字串等)以外的任何東西),那麼您也必須from_config() 類別方法中顯式反序列化這些引數。

像這樣

class CustomLayer(keras.layers.Layer):
    def __init__(self, sublayer, **kwargs):
        super().__init__(**kwargs)
        self.sublayer = sublayer

    def call(self, x):
        return self.sublayer(x)

    def get_config(self):
        base_config = super().get_config()
        config = {
            "sublayer": keras.saving.serialize_keras_object(self.sublayer),
        }
        return {**base_config, **config}

    @classmethod
    def from_config(cls, config):
        sublayer_config = config.pop("sublayer")
        sublayer = keras.saving.deserialize_keras_object(sublayer_config)
        return cls(sublayer, **config)

請參閱定義組態方法章節,以取得更多詳細資訊和範例。

儲存的 .keras 檔案很輕量,並且不儲存自訂物件的 Python 程式碼。因此,要重新載入模型,load_model 需要透過下列方法之一存取所使用任何自訂物件的定義

  1. 註冊自訂物件(首選)
  2. 在載入時直接傳遞自訂物件,或
  3. 使用自訂物件範圍

以下是每個工作流程的範例

註冊自訂物件(首選

這是首選方法,因為自訂物件註冊大大簡化了儲存和載入程式碼。將 @keras.saving.register_keras_serializable 裝飾器新增至自訂物件的類別定義,會在主清單中全域註冊該物件,使 Keras 能夠在載入模型時識別該物件。

讓我們建立一個包含自訂層和自訂啟用函式的自訂模型來示範這一點。

範例

# Clear all previously registered custom objects
keras.saving.get_custom_objects().clear()


# Upon registration, you can optionally specify a package or a name.
# If left blank, the package defaults to `Custom` and the name defaults to
# the class name.
@keras.saving.register_keras_serializable(package="MyLayers")
class CustomLayer(keras.layers.Layer):
    def __init__(self, factor):
        super().__init__()
        self.factor = factor

    def call(self, x):
        return x * self.factor

    def get_config(self):
        return {"factor": self.factor}


@keras.saving.register_keras_serializable(package="my_package", name="custom_fn")
def custom_fn(x):
    return x**2


# Create the model.
def get_model():
    inputs = keras.Input(shape=(4,))
    mid = CustomLayer(0.5)(inputs)
    outputs = keras.layers.Dense(1, activation=custom_fn)(mid)
    model = keras.Model(inputs, outputs)
    model.compile(optimizer="rmsprop", loss="mean_squared_error")
    return model


# Train the model.
def train_model(model):
    input = np.random.random((4, 4))
    target = np.random.random((4, 1))
    model.fit(input, target)
    return model


test_input = np.random.random((4, 4))
test_target = np.random.random((4, 1))

model = get_model()
model = train_model(model)
model.save("custom_model.keras")

# Now, we can simply load without worrying about our custom objects.
reconstructed_model = keras.models.load_model("custom_model.keras")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 46ms/step - loss: 0.2571
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step

將自訂物件傳遞至 load_model()

model = get_model()
model = train_model(model)

# Calling `save('my_model.keras')` creates a zip archive `my_model.keras`.
model.save("custom_model.keras")

# Upon loading, pass a dict containing the custom objects used in the
# `custom_objects` argument of `keras.models.load_model()`.
reconstructed_model = keras.models.load_model(
    "custom_model.keras",
    custom_objects={"CustomLayer": CustomLayer, "custom_fn": custom_fn},
)

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 37ms/step - loss: 0.0535
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step

使用自訂物件範圍

自訂物件範圍內的任何程式碼都將能夠識別傳遞給範圍引數的自訂物件。因此,在範圍內載入模型將允許載入我們的自訂物件。

範例

model = get_model()
model = train_model(model)
model.save("custom_model.keras")

# Pass the custom objects dictionary to a custom object scope and place
# the `keras.models.load_model()` call within the scope.
custom_objects = {"CustomLayer": CustomLayer, "custom_fn": custom_fn}

with keras.saving.custom_object_scope(custom_objects):
    reconstructed_model = keras.models.load_model("custom_model.keras")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 40ms/step - loss: 0.0868
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step
 1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step

模型序列化

本節介紹如何僅儲存模型的組態,而不儲存其狀態。模型的組態(或架構)指定模型包含哪些層,以及這些層如何連接。如果您擁有模型的組態,則可以使用全新初始化的狀態(沒有權重或編譯資訊)建立模型。

API

以下序列化 API 可用

  • keras.models.clone_model(model):建立模型的(隨機初始化)副本。
  • get_config()cls.from_config():分別擷取層或模型的組態,並從其組態重新建立模型執行個體。
  • keras.models.model_to_json()keras.models.model_from_json():類似,但作為 JSON 字串。
  • keras.saving.serialize_keras_object():擷取任何任意 Keras 物件的組態。
  • keras.saving.deserialize_keras_object():從其組態重新建立物件執行個體。

記憶體中模型複製

您可以使用 keras.models.clone_model() 執行模型的記憶體中複製。這相當於取得組態,然後從其組態重新建立模型(因此它不會保留編譯資訊或層權重值)。

範例

new_model = keras.models.clone_model(model)

get_config()from_config()

呼叫 model.get_config()layer.get_config() 將分別傳回一個包含模型或層組態的 Python 字典。您應該定義 get_config() 以包含模型或層的 __init__() 方法所需的引數。在載入時,from_config(config) 方法將使用這些引數呼叫 __init__() 來重建模型或層。

層範例

layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_config()
print(layer_config)
{'name': 'dense_4', 'trainable': True, 'dtype': 'float32', 'units': 3, 'activation': 'relu', 'use_bias': True, 'kernel_initializer': {'module': 'keras.src.initializers.random_initializers', 'class_name': 'GlorotUniform', 'config': {'seed': None}, 'registered_name': 'GlorotUniform'}, 'bias_initializer': {'module': 'keras.src.initializers.constant_initializers', 'class_name': 'Zeros', 'config': {}, 'registered_name': 'Zeros'}, 'kernel_regularizer': None, 'bias_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}

現在讓我們使用 from_config() 方法重建層

new_layer = keras.layers.Dense.from_config(layer_config)

序列模型範例

model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
config = model.get_config()
new_model = keras.Sequential.from_config(config)

函數式模型範例

inputs = keras.Input((32,))
outputs = keras.layers.Dense(1)(inputs)
model = keras.Model(inputs, outputs)
config = model.get_config()
new_model = keras.Model.from_config(config)

to_json()keras.models.model_from_json()

這與 get_config / from_config 類似,只是它將模型轉換為 JSON 字串,然後可以在沒有原始模型類別的情況下載入。它也特定於模型,不適用於層。

範例

model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
json_config = model.to_json()
new_model = keras.models.model_from_json(json_config)

任意物件序列化和反序列化

keras.saving.serialize_keras_object()keras.saving.deserialize_keras_object() API 是通用 API,可用於序列化或反序列化任何 Keras 物件和任何自訂物件。它是儲存模型架構的基礎,並且是 keras 中所有 serialize()/deserialize() 呼叫的幕後推手。

範例:

my_reg = keras.regularizers.L1(0.005)
config = keras.saving.serialize_keras_object(my_reg)
print(config)
{'module': 'keras.src.regularizers.regularizers', 'class_name': 'L1', 'config': {'l1': 0.004999999888241291}, 'registered_name': 'L1'}

請注意序列化格式,其中包含正確重建所需的所有資訊

  • module 包含 Keras 模組的名稱或其他識別物件來源的模組
  • class_name 包含物件類別的名稱。
  • config 包含重建物件所需的所有資訊
  • 自訂物件的 registered_name。請參閱此處

現在我們可以重建正規化器。

new_reg = keras.saving.deserialize_keras_object(config)

模型權重儲存

您可以選擇僅儲存和載入模型的權重。這在以下情況下很有用

  • 您只需要將模型用於推論:在這種情況下,您不需要重新啟動訓練,因此您不需要編譯資訊或最佳化器狀態。
  • 您正在進行遷移學習:在這種情況下,您將訓練一個新的模型,重新使用先前模型的狀態,因此您不需要先前模型的編譯資訊。

記憶體中權重傳輸的 API

可以使用 get_weights()set_weights() 在不同物件之間複製權重

  • keras.layers.Layer.get_weights():傳回權重值的 NumPy 陣列清單。
  • keras.layers.Layer.set_weights(weights):將模型權重設定為提供的值(作為 NumPy 陣列)。

範例

在記憶體中將權重從一個層傳輸到另一個層

def create_layer():
    layer = keras.layers.Dense(64, activation="relu", name="dense_2")
    layer.build((None, 784))
    return layer


layer_1 = create_layer()
layer_2 = create_layer()

# Copy weights from layer 1 to layer 2
layer_2.set_weights(layer_1.get_weights())

在記憶體中將權重從一個模型傳輸到另一個具有相容架構的模型

# Create a simple functional model
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")


# Define a subclassed model with the same architecture
class SubclassedModel(keras.Model):
    def __init__(self, output_dim, name=None):
        super().__init__(name=name)
        self.output_dim = output_dim
        self.dense_1 = keras.layers.Dense(64, activation="relu", name="dense_1")
        self.dense_2 = keras.layers.Dense(64, activation="relu", name="dense_2")
        self.dense_3 = keras.layers.Dense(output_dim, name="predictions")

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

    def get_config(self):
        return {"output_dim": self.output_dim, "name": self.name}


subclassed_model = SubclassedModel(10)
# Call the subclassed model once to create the weights.
subclassed_model(np.ones((1, 784)))

# Copy weights from functional_model to subclassed_model.
subclassed_model.set_weights(functional_model.get_weights())

assert len(functional_model.weights) == len(subclassed_model.weights)
for a, b in zip(functional_model.weights, subclassed_model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())

無狀態層的情況

由於無狀態層不會變更權重的順序或數量,即使有額外/遺失的無狀態層,模型也可以具有相容的架構。

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)

# Add a dropout layer, which does not contain any weights.
x = keras.layers.Dropout(0.5)(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model_with_dropout = keras.Model(
    inputs=inputs, outputs=outputs, name="3_layer_mlp"
)

functional_model_with_dropout.set_weights(functional_model.get_weights())

將權重儲存到磁碟並將其載入回去的 API

可以透過呼叫 model.save_weights(filepath) 將權重儲存到磁碟。檔案名稱應以 .weights.h5 結尾。

範例

# Runnable example
sequential_model = keras.Sequential(
    [
        keras.Input(shape=(784,), name="digits"),
        keras.layers.Dense(64, activation="relu", name="dense_1"),
        keras.layers.Dense(64, activation="relu", name="dense_2"),
        keras.layers.Dense(10, name="predictions"),
    ]
)
sequential_model.save_weights("my_model.weights.h5")
sequential_model.load_weights("my_model.weights.h5")

請注意,當模型包含巢狀層時,變更 layer.trainable 可能會導致不同的 layer.weights 順序。

class NestedDenseLayer(keras.layers.Layer):
    def __init__(self, units, name=None):
        super().__init__(name=name)
        self.dense_1 = keras.layers.Dense(units, name="dense_1")
        self.dense_2 = keras.layers.Dense(units, name="dense_2")

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


nested_model = keras.Sequential([keras.Input((784,)), NestedDenseLayer(10, "nested")])
variable_names = [v.name for v in nested_model.weights]
print("variables: {}".format(variable_names))

print("\nChanging trainable status of one of the nested layers...")
nested_model.get_layer("nested").dense_1.trainable = False

variable_names_2 = [v.name for v in nested_model.weights]
print("\nvariables: {}".format(variable_names_2))
print("variable ordering changed:", variable_names != variable_names_2)
variables: ['kernel', 'bias', 'kernel', 'bias']
Changing trainable status of one of the nested layers...
variables: ['kernel', 'bias', 'kernel', 'bias']
variable ordering changed: False
遷移學習範例

當從權重檔案載入預訓練權重時,建議將權重載入原始的檢查點模型,然後將所需的權重/層提取到新的模型中。

範例

def create_functional_model():
    inputs = keras.Input(shape=(784,), name="digits")
    x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
    x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
    outputs = keras.layers.Dense(10, name="predictions")(x)
    return keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")


functional_model = create_functional_model()
functional_model.save_weights("pretrained.weights.h5")

# In a separate program:
pretrained_model = create_functional_model()
pretrained_model.load_weights("pretrained.weights.h5")

# Create a new model by extracting layers from the original model:
extracted_layers = pretrained_model.layers[:-1]
extracted_layers.append(keras.layers.Dense(5, name="dense_3"))
model = keras.Sequential(extracted_layers)
model.summary()
Model: "sequential_4"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Layer (type)                     Output Shape                  Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ dense_1 (Dense)                 │ (None, 64)                │     50,240 │
├─────────────────────────────────┼───────────────────────────┼────────────┤
│ dense_2 (Dense)                 │ (None, 64)                │      4,160 │
├─────────────────────────────────┼───────────────────────────┼────────────┤
│ dense_3 (Dense)                 │ (None, 5)                 │        325 │
└─────────────────────────────────┴───────────────────────────┴────────────┘
 Total params: 54,725 (213.77 KB)
 Trainable params: 54,725 (213.77 KB)
 Non-trainable params: 0 (0.00 B)

附錄:處理自訂物件

定義 config 方法

規格

  • get_config() 應該回傳一個 JSON 可序列化的字典,以便與 Keras 架構和模型儲存 API 相容。
  • from_config(config) (一個 classmethod) 應該回傳一個從 config 建立的新層或模型物件。預設實作回傳 cls(**config)

注意:如果您的所有建構子引數已經是可序列化的,例如字串和整數,或非自訂的 Keras 物件,則不需要覆寫 from_config。然而,對於更複雜的物件,例如傳遞給 __init__ 的層或模型,反序列化必須在 __init__ 本身或覆寫 from_config() 方法中明確處理。

範例

@keras.saving.register_keras_serializable(package="MyLayers", name="KernelMult")
class MyDense(keras.layers.Layer):
    def __init__(
        self,
        units,
        *,
        kernel_regularizer=None,
        kernel_initializer=None,
        nested_model=None,
        **kwargs
    ):
        super().__init__(**kwargs)
        self.hidden_units = units
        self.kernel_regularizer = kernel_regularizer
        self.kernel_initializer = kernel_initializer
        self.nested_model = nested_model

    def get_config(self):
        config = super().get_config()
        # Update the config with the custom layer's parameters
        config.update(
            {
                "units": self.hidden_units,
                "kernel_regularizer": self.kernel_regularizer,
                "kernel_initializer": self.kernel_initializer,
                "nested_model": self.nested_model,
            }
        )
        return config

    def build(self, input_shape):
        input_units = input_shape[-1]
        self.kernel = self.add_weight(
            name="kernel",
            shape=(input_units, self.hidden_units),
            regularizer=self.kernel_regularizer,
            initializer=self.kernel_initializer,
        )

    def call(self, inputs):
        return ops.matmul(inputs, self.kernel)


layer = MyDense(units=16, kernel_regularizer="l1", kernel_initializer="ones")
layer3 = MyDense(units=64, nested_model=layer)

config = keras.layers.serialize(layer3)

print(config)

new_layer = keras.layers.deserialize(config)

print(new_layer)
{'module': None, 'class_name': 'MyDense', 'config': {'name': 'my_dense_1', 'trainable': True, 'dtype': 'float32', 'units': 64, 'kernel_regularizer': None, 'kernel_initializer': None, 'nested_model': {'module': None, 'class_name': 'MyDense', 'config': {'name': 'my_dense', 'trainable': True, 'dtype': 'float32', 'units': 16, 'kernel_regularizer': 'l1', 'kernel_initializer': 'ones', 'nested_model': None}, 'registered_name': 'MyLayers>KernelMult'}}, 'registered_name': 'MyLayers>KernelMult'}
<MyDense name=my_dense_1, built=False>

請注意,對於 MyDense,以上覆寫 from_config 是不必要的,因為 hidden_unitskernel_initializerkernel_regularizer 分別是整數、字串和內建的 Keras 物件。這表示 cls(**config) 的預設 from_config 實作會如預期般運作。

對於更複雜的物件,例如傳遞給 __init__ 的層和模型,您必須明確地反序列化這些物件。讓我們來看一個需要覆寫 from_config 的模型的範例。

範例:

@keras.saving.register_keras_serializable(package="ComplexModels")
class CustomModel(keras.layers.Layer):
    def __init__(self, first_layer, second_layer=None, **kwargs):
        super().__init__(**kwargs)
        self.first_layer = first_layer
        if second_layer is not None:
            self.second_layer = second_layer
        else:
            self.second_layer = keras.layers.Dense(8)

    def get_config(self):
        config = super().get_config()
        config.update(
            {
                "first_layer": self.first_layer,
                "second_layer": self.second_layer,
            }
        )
        return config

    @classmethod
    def from_config(cls, config):
        # Note that you can also use [`keras.saving.deserialize_keras_object`](/api/models/model_saving_apis/serialization_utils#deserializekerasobject-function) here
        config["first_layer"] = keras.layers.deserialize(config["first_layer"])
        config["second_layer"] = keras.layers.deserialize(config["second_layer"])
        return cls(**config)

    def call(self, inputs):
        return self.first_layer(self.second_layer(inputs))


# Let's make our first layer the custom layer from the previous example (MyDense)
inputs = keras.Input((32,))
outputs = CustomModel(first_layer=layer)(inputs)
model = keras.Model(inputs, outputs)

config = model.get_config()
new_model = keras.Model.from_config(config)

自訂物件如何序列化

序列化格式對於透過 @keras.saving.register_keras_serializable 註冊的自訂物件具有特殊鍵。這個 registered_name 鍵允許在載入/反序列化時輕鬆檢索,同時也允許使用者新增自訂命名。

讓我們看看從序列化我們在上面定義的自訂層 MyDense 中產生的 config。

範例:

layer = MyDense(
    units=16,
    kernel_regularizer=keras.regularizers.L1L2(l1=1e-5, l2=1e-4),
    kernel_initializer="ones",
)
config = keras.layers.serialize(layer)
print(config)
{'module': None, 'class_name': 'MyDense', 'config': {'name': 'my_dense_2', 'trainable': True, 'dtype': 'float32', 'units': 16, 'kernel_regularizer': {'module': 'keras.src.regularizers.regularizers', 'class_name': 'L1L2', 'config': {'l1': 1e-05, 'l2': 0.0001}, 'registered_name': 'L1L2'}, 'kernel_initializer': 'ones', 'nested_model': None}, 'registered_name': 'MyLayers>KernelMult'}

如所示,registered_name 鍵包含 Keras 主列表的查找資訊,包括套件 MyLayers 和我們在 @keras.saving.register_keras_serializable 修飾詞中給予的自訂名稱 KernelMult。再次查看自訂類別定義/註冊 這裡

請注意,class_name 鍵包含類別的原始名稱,允許在 from_config 中正確地重新初始化。

此外,請注意,由於這是自訂物件,module 鍵為 None