หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

บันทึกและโหลดโมเดล Keras

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

บทนำ

โมเดล Keras ประกอบด้วยองค์ประกอบหลายอย่าง:

  • สถาปัตยกรรมหรือการกำหนดค่าซึ่งระบุว่าโมเดลประกอบด้วยเลเยอร์ใดบ้างและเชื่อมต่อกันอย่างไร
  • ชุดของค่าน้ำหนัก ("สถานะของแบบจำลอง")
  • เครื่องมือเพิ่มประสิทธิภาพ (กำหนดโดยการรวบรวมโมเดล)
  • ชุดของการสูญเสียและเมตริก (กำหนดโดยการคอมไพล์โมเดลหรือเรียก add_loss() หรือ add_metric() )

Keras API ทำให้สามารถบันทึกชิ้นส่วนเหล่านี้ทั้งหมดลงในดิสก์ได้ในคราวเดียวหรือเลือกบันทึกเฉพาะบางส่วนเท่านั้น:

  • บันทึกทุกอย่างลงในไฟล์เก็บถาวรเดียวในรูปแบบ TensorFlow SavedModel (หรือในรูปแบบ Keras H5 ที่เก่ากว่า) นี่คือมาตรฐานการปฏิบัติ
  • บันทึกสถาปัตยกรรม / คอนฟิกูเรชันเท่านั้นโดยทั่วไปเป็นไฟล์ JSON
  • บันทึกค่าน้ำหนักเท่านั้น โดยทั่วไปจะใช้เมื่อฝึกโมเดล

ลองมาดูตัวเลือกเหล่านี้กัน: คุณจะใช้ตัวเลือกใดตัวเลือกหนึ่งเมื่อใด พวกเขาทำงานอย่างไร?

คำตอบสั้น ๆ สำหรับการบันทึกและการโหลด

หากคุณมีเวลาเพียง 10 วินาทีในการอ่านคู่มือนี้นี่คือสิ่งที่คุณต้องรู้

การบันทึกโมเดล Keras:

model = ...  # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location')

การโหลดโมเดลกลับ:

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

ตอนนี้เรามาดูรายละเอียดกัน

ติดตั้ง

import numpy as np
import tensorflow as tf
from tensorflow import keras

การประหยัดและการโหลดทั้งรุ่น

คุณสามารถบันทึกทั้งโมเดลลงในอาร์ติแฟกต์เดียว จะรวมถึง:

  • สถาปัตยกรรม / config ของโมเดล
  • ค่าน้ำหนักของแบบจำลอง (ซึ่งเรียนรู้ระหว่างการฝึกซ้อม)
  • ข้อมูลการ compile() ของโมเดล (ถ้า compile() ) ถูกเรียก
  • เครื่องมือเพิ่มประสิทธิภาพและสถานะถ้ามี (สิ่งนี้ช่วยให้คุณสามารถเริ่มการฝึกอบรมใหม่จากจุดที่คุณออกไป)

API

มีสองรูปแบบที่คุณสามารถใช้เพื่อบันทึกโมเดลทั้งหมดลงในดิสก์: รูปแบบ TensorFlow SavedModel และรูปแบบ Keras H5 ที่เก่ากว่า รูปแบบที่แนะนำคือ SavedModel เป็นค่าเริ่มต้นเมื่อคุณใช้ model.save()

คุณสามารถเปลี่ยนเป็นรูปแบบ H5 ได้โดย:

  • ผ่าน save_format='h5' เพื่อ save()
  • ส่งชื่อไฟล์ที่ลงท้ายด้วย. .h5 หรือ. .keras เพื่อ save()

รูปแบบที่บันทึกไว้

ตัวอย่าง:

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="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')` creates a SavedModel folder `my_model`.
model.save("my_model")

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

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
4/4 [==============================] - 0s 1ms/step - loss: 0.3312
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: my_model/assets
4/4 [==============================] - 0s 1ms/step - loss: 0.2840

<tensorflow.python.keras.callbacks.History at 0x7f0fbc3f3780>

สิ่งที่ SavedModel ประกอบด้วย

การเรียก model.save('my_model') สร้างโฟลเดอร์ชื่อ my_model ซึ่งประกอบด้วยสิ่งต่อไปนี้:

ls my_model
assets  saved_model.pb  variables

สถาปัตยกรรมโมเดลและคอนฟิกูเรชันการฝึกอบรม (รวมถึงเครื่องมือเพิ่มประสิทธิภาพการสูญเสียและเมตริก) จะถูกเก็บไว้ใน saved_model.pb น้ำหนักจะถูกบันทึกไว้ในไดเร็กทอรี variables/

สำหรับข้อมูลโดยละเอียดเกี่ยวกับรูปแบบ SavedModel โปรดดู คู่มือ SavedModel ( รูปแบบที่บันทึกไว้บนดิสก์ )

SavedModel จัดการกับออบเจ็กต์แบบกำหนดเองอย่างไร

เมื่อบันทึกโมเดลและเลเยอร์รูปแบบที่บันทึกไว้จะเก็บชื่อคลาส ฟังก์ชันการโทรการ สูญเสียและน้ำหนัก (และการกำหนดค่าหากมีการใช้งาน) ฟังก์ชันการโทรกำหนดกราฟการคำนวณของโมเดล / เลเยอร์

ในกรณีที่ไม่มีการกำหนดค่าโมเดล / เลเยอร์ฟังก์ชันการโทรจะใช้เพื่อสร้างโมเดลที่มีอยู่เหมือนกับโมเดลดั้งเดิมซึ่งสามารถฝึกฝนประเมินและใช้สำหรับการอนุมานได้

อย่างไรก็ตามการกำหนด get_config และ from_config วิธีปฏิบัติที่ดีเสมอเมื่อเขียนโมเดลหรือคลาสเลเยอร์แบบกำหนดเอง ซึ่งช่วยให้คุณสามารถอัปเดตการคำนวณในภายหลังได้อย่างง่ายดายหากจำเป็น ดูส่วนเกี่ยวกับอ อบเจ็กต์แบบกำหนดเอง สำหรับข้อมูลเพิ่มเติม

ด้านล่างนี้เป็นตัวอย่างของสิ่งที่เกิดขึ้นเมื่อโหลดเลเยอร์ที่กำหนดเองจากรูปแบบที่บันทึกไว้ โดยไม่ เขียนทับวิธีการกำหนดค่า

class CustomModel(keras.Model):
    def __init__(self, hidden_units):
        super(CustomModel, self).__init__()
        self.dense_layers = [keras.layers.Dense(u) for u in hidden_units]

    def call(self, inputs):
        x = inputs
        for layer in self.dense_layers:
            x = layer(x)
        return x


model = CustomModel([16, 16, 10])
# Build the model by calling it
input_arr = tf.random.uniform((1, 5))
outputs = model(input_arr)
model.save("my_model")

# Delete the custom-defined model class to ensure that the loader does not have
# access to it.
del CustomModel

loaded = keras.models.load_model("my_model")
np.testing.assert_allclose(loaded(input_arr), outputs)

print("Original model:", model)
print("Loaded model:", loaded)
INFO:tensorflow:Assets written to: my_model/assets
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Original model: <__main__.CustomModel object at 0x7f0fbc3f8208>
Loaded model: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7f1031981518>

ดังที่เห็นในตัวอย่างด้านบนตัวโหลดจะสร้างคลาสโมเดลใหม่แบบไดนามิกที่ทำหน้าที่เหมือนโมเดลเดิม

รูปแบบ Keras H5

Keras ยังสนับสนุนการบันทึกไฟล์ HDF5 ไฟล์เดียวที่มีสถาปัตยกรรมของโมเดลค่าน้ำหนักและข้อมูล compile() เป็นทางเลือกที่มีน้ำหนักเบาสำหรับ SavedModel

ตัวอย่าง:

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.h5')` creates a h5 file `my_model.h5`.
model.save("my_h5_model.h5")

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

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
4/4 [==============================] - 0s 1ms/step - loss: 0.3771
4/4 [==============================] - 0s 1ms/step - loss: 0.3432

<tensorflow.python.keras.callbacks.History at 0x7f1031030cf8>

ข้อ จำกัด

เมื่อเทียบกับรูปแบบ SavedModel มีสองสิ่งที่ไม่รวมอยู่ในไฟล์ H5:

  • การสูญเสียภายนอกและเมตริกที่ เพิ่มผ่าน model.add_loss() & model.add_metric() จะไม่ถูกบันทึกไว้ (ไม่เหมือนกับ SavedModel) หากคุณมีการสูญเสียและเมตริกดังกล่าวในแบบจำลองของคุณและคุณต้องการกลับมาฝึกอบรมต่อคุณต้องเพิ่มการสูญเสียเหล่านี้กลับด้วยตัวคุณเองหลังจากโหลดแบบจำลองแล้ว โปรดทราบว่าสิ่งนี้ใช้ไม่ได้กับการสูญเสีย / เมตริกที่สร้างขึ้น ภายใน เลเยอร์ผ่าน self.add_loss() & self.add_metric() ตราบเท่าที่เลเยอร์ได้รับการโหลดการสูญเสียและเมตริกเหล่านี้จะถูกเก็บไว้เนื่องจากเป็นส่วนหนึ่งของวิธีการ call ของเลเยอร์
  • กราฟการคำนวณของออบเจ็กต์ที่กำหนดเอง เช่นเลเยอร์ที่กำหนดเองจะไม่รวมอยู่ในไฟล์ที่บันทึก ในขณะโหลด Keras จะต้องเข้าถึงคลาส / ฟังก์ชัน Python ของออบเจ็กต์เหล่านี้เพื่อสร้างโมเดลใหม่ ดู วัตถุที่กำหนดเอง

บันทึกสถาปัตยกรรม

คอนฟิกูเรชันของโมเดล (หรือสถาปัตยกรรม) จะระบุว่าโมเดลมีเลเยอร์ใดบ้างและเลเยอร์เหล่านี้เชื่อมต่อกันอย่างไร * หากคุณมีการกำหนดค่าโมเดลคุณสามารถสร้างแบบจำลองด้วยสถานะเริ่มต้นใหม่สำหรับน้ำหนักและไม่มีข้อมูลการรวบรวม

* หมายเหตุใช้เฉพาะกับโมเดลที่กำหนดโดยใช้ฟังก์ชันหรือตามลำดับ apis ไม่ใช่โมเดลคลาสย่อย

การกำหนดค่าแบบจำลองตามลำดับหรือแบบจำลอง API ที่ใช้งานได้

โมเดลประเภทเหล่านี้เป็นกราฟของเลเยอร์อย่างชัดเจนการกำหนดค่าจะมีอยู่ในรูปแบบโครงสร้างเสมอ

API

get_config() และ from_config()

การเรียก config = model.get_config() จะส่งคืน Python dict ที่มีการกำหนดค่าของโมเดล จากนั้นโมเดลเดียวกันสามารถสร้างขึ้นใหม่ได้ผ่าน Sequential.from_config(config) (สำหรับโมเดล Sequential ) หรือ Model.from_config(config) (สำหรับโมเดล Functional API)

เวิร์กโฟลว์เดียวกันนี้ยังใช้ได้กับเลเยอร์ที่ต่ออนุกรมกันได้

ตัวอย่างเลเยอร์:

layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_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() และ tf.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)

วัตถุที่กำหนดเอง

โมเดลและเลเยอร์

สถาปัตยกรรมของโมเดลและเลเยอร์ย่อยถูกกำหนดไว้ในเมธอด __init__ และ call พวกเขาถือเป็น Python bytecode ซึ่งไม่สามารถต่ออนุกรมในการกำหนดค่าที่เข้ากันได้กับ JSON คุณสามารถลองจัดลำดับไบต์โค้ด (เช่นผ่านการ pickle ) แต่มันไม่ปลอดภัยอย่างสมบูรณ์และหมายความว่าโมเดลของคุณไม่สามารถโหลดบนระบบอื่นได้

ในการบันทึก / โหลดโมเดลด้วยเลเยอร์ที่กำหนดเองหรือโมเดลคลาสย่อยคุณควรเขียนทับ get_config และเป็นทางเลือก from_config นอกจากนี้คุณควรใช้ register วัตถุที่กำหนดเองเพื่อให้ Keras ตระหนักถึงมัน

ฟังก์ชั่นที่กำหนดเอง

ฟังก์ชันที่กำหนดเอง (เช่นการสูญเสียการเปิดใช้งานหรือการเริ่มต้น) ไม่จำเป็นต้องใช้เมธอด get_config ชื่อฟังก์ชันเพียงพอสำหรับการโหลดตราบเท่าที่มีการลงทะเบียนเป็นวัตถุที่กำหนดเอง

กำลังโหลดกราฟ TensorFlow เท่านั้น

เป็นไปได้ที่จะโหลดกราฟ TensorFlow ที่สร้างโดย Keras หากคุณทำเช่นนั้นคุณไม่จำเป็นต้องระบุ custom_objects ใด ๆ คุณสามารถทำได้ดังนี้:

model.save("my_model")
tensorflow_graph = tf.saved_model.load("my_model")
x = np.random.uniform(size=(4, 32)).astype(np.float32)
predicted = tensorflow_graph(x).numpy()
INFO:tensorflow:Assets written to: my_model/assets

โปรดทราบว่าวิธีนี้มีข้อบกพร่องหลายประการ:

  • ด้วยเหตุผลด้านการตรวจสอบย้อนกลับคุณควรเข้าถึงอ็อบเจ็กต์แบบกำหนดเองที่ใช้อยู่เสมอ คุณคงไม่ต้องการผลิตแบบจำลองที่คุณไม่สามารถสร้างขึ้นใหม่ได้
  • วัตถุที่ส่งคืนโดย tf.saved_model.load ไม่ใช่โมเดล Keras ดังนั้นจึงไม่ง่ายที่จะใช้ ตัวอย่างเช่นคุณจะไม่สามารถเข้าถึง. .predict() หรือ . .fit()

แม้ว่าการใช้งานจะถูกกีดกัน แต่ก็สามารถช่วยคุณได้หากคุณอยู่ในจุดที่ จำกัด ตัวอย่างเช่นหากคุณทำรหัสของวัตถุที่กำหนดเองของคุณหายหรือมีปัญหาในการโหลดโมเดลด้วย tf.keras.models.load_model()

คุณสามารถดูข้อมูลเพิ่มเติมได้ใน หน้าเกี่ยวกับ tf.saved_model.load

การกำหนดวิธีการกำหนดค่า

ข้อมูลจำเพาะ:

  • get_config ควรส่งคืนพจนานุกรม JSON ที่สามารถทำให้เป็นอนุกรมได้เพื่อให้เข้ากันได้กับสถาปัตยกรรม Keras และ API แบบประหยัดโมเดล
  • from_config(config) ( classmethod ) ควรส่งคืนเลเยอร์หรือโมเดลอ็อบเจ็กต์ใหม่ที่สร้างขึ้นจาก config การใช้งานเริ่มต้นจะส่งคืน cls(**config)

ตัวอย่าง:

class CustomLayer(keras.layers.Layer):
    def __init__(self, a):
        self.var = tf.Variable(a, name="var_a")

    def call(self, inputs, training=False):
        if training:
            return inputs * self.var
        else:
            return inputs

    def get_config(self):
        return {"a": self.var.numpy()}

    # There's actually no need to define `from_config` here, since returning
    # `cls(**config)` is the default behavior.
    @classmethod
    def from_config(cls, config):
        return cls(**config)


layer = CustomLayer(5)
layer.var.assign(2)

serialized_layer = keras.layers.serialize(layer)
new_layer = keras.layers.deserialize(
    serialized_layer, custom_objects={"CustomLayer": CustomLayer}
)

การลงทะเบียนวัตถุที่กำหนดเอง

Keras จะจดบันทึกว่าคลาสใดสร้างการกำหนดค่า จากตัวอย่างด้านบน tf.keras.layers.serialize สร้างรูปแบบอนุกรมของเลเยอร์ที่กำหนดเอง:

{'class_name': 'CustomLayer', 'config': {'a': 2} }

Keras เก็บรายการหลักของคลาสเลเยอร์โมเดลเครื่องมือเพิ่มประสิทธิภาพและเมตริกในตัวทั้งหมดซึ่งใช้เพื่อค้นหาคลาสที่ถูกต้องเพื่อเรียกใช้ from_config ถ้าไม่พบคลาสแสดงว่าเกิดข้อผิดพลาดขึ้น ( Value Error: Unknown layer ) มีสองสามวิธีในการลงทะเบียนคลาสแบบกำหนดเองในรายการนี้:

  1. การตั้งค่าอาร์กิวเมนต์ custom_objects ในฟังก์ชันการโหลด (ดูตัวอย่างในส่วนด้านบน "การกำหนดวิธีการกำหนดค่า")
  2. tf.keras.utils.custom_object_scope หรือ tf.keras.utils.CustomObjectScope
  3. tf.keras.utils.register_keras_serializable

ตัวอย่างเลเยอร์และฟังก์ชันที่กำหนดเอง

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

    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            shape=(self.units,), initializer="random_normal", trainable=True
        )

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

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


def custom_activation(x):
    return tf.nn.tanh(x) ** 2


# Make a model with the CustomLayer and custom_activation
inputs = keras.Input((32,))
x = CustomLayer(32)(inputs)
outputs = keras.layers.Activation(custom_activation)(x)
model = keras.Model(inputs, outputs)

# Retrieve the config
config = model.get_config()

# At loading time, register the custom objects with a `custom_object_scope`:
custom_objects = {"CustomLayer": CustomLayer, "custom_activation": custom_activation}
with keras.utils.custom_object_scope(custom_objects):
    new_model = keras.Model.from_config(config)

การโคลนโมเดลในหน่วยความจำ

คุณยังสามารถทำการโคลนโมเดลในหน่วยความจำผ่าน tf.keras.models.clone_model() สิ่งนี้เทียบเท่ากับการรับการกำหนดค่าจากนั้นสร้างโมเดลใหม่จากการกำหนดค่า (ดังนั้นจึงไม่เก็บข้อมูลการคอมไพล์หรือค่าน้ำหนักของเลเยอร์)

ตัวอย่าง:

with keras.utils.custom_object_scope(custom_objects):
    new_model = keras.models.clone_model(model)

บันทึกและโหลดเฉพาะค่าน้ำหนักของโมเดล

คุณสามารถเลือกบันทึกและโหลดน้ำหนักของโมเดลเท่านั้น สิ่งนี้จะมีประโยชน์หาก:

  • คุณต้องการโมเดลสำหรับการอนุมานเท่านั้น: ในกรณีนี้คุณไม่จำเป็นต้องเริ่มการฝึกอบรมใหม่ดังนั้นคุณไม่จำเป็นต้องมีข้อมูลการคอมไพล์หรือสถานะของเครื่องมือเพิ่มประสิทธิภาพ
  • คุณกำลังถ่ายทอดการเรียนรู้: ในกรณีนี้คุณจะต้องฝึกโมเดลใหม่โดยใช้สถานะของโมเดลก่อนหน้านี้ซ้ำดังนั้นคุณจึงไม่จำเป็นต้องมีข้อมูลการรวบรวมของโมเดลก่อนหน้านี้

API สำหรับการถ่ายโอนน้ำหนักในหน่วยความจำ

สามารถคัดลอกน้ำหนักระหว่างวัตถุต่างๆได้โดยใช้ get_weights และ set_weights :

ตัวอย่างด้านล่าง

การถ่ายโอนน้ำหนักจากชั้นหนึ่งไปยังอีกชั้นหนึ่งในหน่วยความจำ

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 2 to layer 1
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(SubclassedModel, self).__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(tf.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 ในรูปแบบต่อไปนี้:

  • ด่าน TensorFlow
  • HDF5

รูปแบบเริ่มต้นสำหรับ model.save_weights คือจุดตรวจ TensorFlow มีสองวิธีในการระบุรูปแบบการบันทึก:

  1. อาร์กิวเมนต์ save_format : ตั้งค่าเป็น save_format="tf" หรือ save_format="h5"
  2. อาร์กิวเมนต์ path : หากเส้นทางลงท้ายด้วย. .h5 หรือ. .hdf5 จะใช้รูปแบบ HDF5 คำต่อท้ายอื่น ๆ จะส่งผลให้เกิดจุดตรวจ save_format เว้นแต่จะตั้งค่า save_format

นอกจากนี้ยังมีตัวเลือกในการดึงน้ำหนักเป็นอาร์เรย์ numpy ในหน่วยความจำ แต่ละ API มีข้อดีข้อเสียซึ่งมีรายละเอียดด้านล่าง

รูปแบบ TF Checkpoint

ตัวอย่าง:

# 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("ckpt")
load_status = sequential_model.load_weights("ckpt")

# `assert_consumed` can be used as validation that all variable values have been
# restored from the checkpoint. See `tf.train.Checkpoint.restore` for other
# methods in the Status object.
load_status.assert_consumed()
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f103069d828>

รายละเอียดรูปแบบ

รูปแบบ TensorFlow Checkpoint จะบันทึกและเรียกคืนน้ำหนักโดยใช้ชื่อแอตทริบิวต์ออบเจ็กต์ ตัวอย่างเช่นพิจารณาเลเยอร์ tf.keras.layers.Dense เลเยอร์ประกอบด้วยสองน้ำหนัก: dense.kernel และ dense.bias เมื่อเลเยอร์ถูกบันทึกเป็นรูปแบบ tf จุดตรวจที่ได้จะมีคีย์ "kernel" และ "bias" และค่าน้ำหนักที่สอดคล้องกัน สำหรับข้อมูลเพิ่มเติมโปรดดู "การโหลดกลไก" ในคู่มือ TF Checkpoint

โปรดสังเกตว่าแอตทริบิวต์ / ขอบกราฟตั้งชื่อตาม ชื่อที่ใช้ในออบเจ็กต์หลักไม่ใช่ชื่อของตัวแปร พิจารณา CustomLayer ในตัวอย่างด้านล่าง ตัวแปร CustomLayer.var ถูกบันทึกโดยมี "var" เป็นส่วนหนึ่งของคีย์ไม่ใช่ "var_a"

class CustomLayer(keras.layers.Layer):
    def __init__(self, a):
        self.var = tf.Variable(a, name="var_a")


layer = CustomLayer(5)
layer_ckpt = tf.train.Checkpoint(layer=layer).save("custom_layer")

ckpt_reader = tf.train.load_checkpoint(layer_ckpt)

ckpt_reader.get_variable_to_dtype_map()
{'save_counter/.ATTRIBUTES/VARIABLE_VALUE': tf.int64,
 '_CHECKPOINTABLE_OBJECT_GRAPH': tf.string,
 'layer/var/.ATTRIBUTES/VARIABLE_VALUE': tf.int32}

ถ่ายทอดตัวอย่างการเรียนรู้

โดยพื้นฐานแล้วตราบใดที่โมเดลสองรุ่นมีสถาปัตยกรรมเดียวกันพวกเขาจะสามารถแชร์จุดตรวจสอบเดียวกันได้

ตัวอย่าง:

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")

# Extract a portion of the functional model defined in the Setup section.
# The following lines produce a new model that excludes the final output
# layer of the functional model.
pretrained = keras.Model(
    functional_model.inputs, functional_model.layers[-1].input, name="pretrained_model"
)
# Randomly assign "trained" weights.
for w in pretrained.weights:
    w.assign(tf.random.normal(w.shape))
pretrained.save_weights("pretrained_ckpt")
pretrained.summary()

# Assume this is a separate program where only 'pretrained_ckpt' exists.
# Create a new functional model with a different output dimension.
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(5, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs, name="new_model")

# Load the weights from pretrained_ckpt into model.
model.load_weights("pretrained_ckpt")

# Check that all of the pretrained weights have been loaded.
for a, b in zip(pretrained.weights, model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())

print("\n", "-" * 50)
model.summary()

# Example 2: Sequential model
# Recreate the pretrained model, and load the saved weights.
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)
pretrained_model = keras.Model(inputs=inputs, outputs=x, name="pretrained")

# Sequential example:
model = keras.Sequential([pretrained_model, keras.layers.Dense(5, name="predictions")])
model.summary()

pretrained_model.load_weights("pretrained_ckpt")

# Warning! Calling `model.load_weights('pretrained_ckpt')` won't throw an error,
# but will *not* work as expected. If you inspect the weights, you'll see that
# none of the weights will have loaded. `pretrained_model.load_weights()` is the
# correct method to call.
Model: "pretrained_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
digits (InputLayer)          [(None, 784)]             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
=================================================================
Total params: 54,400
Trainable params: 54,400
Non-trainable params: 0
_________________________________________________________________

 --------------------------------------------------
Model: "new_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
digits (InputLayer)          [(None, 784)]             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_________________________________________________________________
predictions (Dense)          (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
pretrained (Functional)      (None, 64)                54400     
_________________________________________________________________
predictions (Dense)          (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________

<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f103066e630>

โดยทั่วไปขอแนะนำให้ใช้ API เดียวกันในการสร้างโมเดล หากคุณสลับไปมาระหว่าง Sequential และ Functional หรือ Functional และ subclassed เป็นต้นให้สร้างแบบจำลองที่ได้รับการฝึกอบรมมาแล้วใหม่และโหลดน้ำหนักที่ได้รับการฝึกฝนมาแล้วให้กับโมเดลนั้น

คำถามต่อไปคือจะบันทึกและโหลดน้ำหนักไปยังโมเดลต่างๆได้อย่างไรหากสถาปัตยกรรมแบบจำลองต่างกันมาก? วิธีแก้ปัญหาคือใช้ tf.train.Checkpoint เพื่อบันทึกและกู้คืนเลเยอร์ / ตัวแปรที่แน่นอน

ตัวอย่าง:

# Create a subclassed model that essentially uses functional_model's first
# and last layers.
# First, save the weights of functional_model's first and last dense layers.
first_dense = functional_model.layers[1]
last_dense = functional_model.layers[-1]
ckpt_path = tf.train.Checkpoint(
    dense=first_dense, kernel=last_dense.kernel, bias=last_dense.bias
).save("ckpt")

# Define the subclassed model.
class ContrivedModel(keras.Model):
    def __init__(self):
        super(ContrivedModel, self).__init__()
        self.first_dense = keras.layers.Dense(64)
        self.kernel = self.add_variable("kernel", shape=(64, 10))
        self.bias = self.add_variable("bias", shape=(10,))

    def call(self, inputs):
        x = self.first_dense(inputs)
        return tf.matmul(x, self.kernel) + self.bias


model = ContrivedModel()
# Call model on inputs to create the variables of the dense layer.
_ = model(tf.ones((1, 784)))

# Create a Checkpoint with the same structure as before, and load the weights.
tf.train.Checkpoint(
    dense=model.first_dense, kernel=model.kernel, bias=model.bias
).restore(ckpt_path).assert_consumed()
WARNING:tensorflow:From <ipython-input-1-eec1d28bc826>:15: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.

<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f10306c6518>

รูปแบบ HDF5

รูปแบบ HDF5 มีน้ำหนักที่จัดกลุ่มตามชื่อเลเยอร์ น้ำหนักเป็นรายการที่เรียงลำดับโดยการเชื่อมรายการน้ำหนักที่ฝึกได้เข้ากับรายการน้ำหนักที่ไม่สามารถฝึกได้ (เช่นเดียวกับ layer.weights ) ดังนั้นโมเดลสามารถใช้ด่าน hdf5 ได้หากมีเลเยอร์และสถานะที่ฝึกฝนได้เหมือนกันกับที่บันทึกไว้ในด่าน

ตัวอย่าง:

# 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("weights.h5")
sequential_model.load_weights("weights.h5")

โปรดทราบว่าการเปลี่ยน layer.trainable อาจส่งผลให้ layer.weights ลำดับที่แตกต่างกันเมื่อโมเดลมีเลเยอร์ซ้อนกัน

class NestedDenseLayer(keras.layers.Layer):
    def __init__(self, units, name=None):
        super(NestedDenseLayer, self).__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: ['nested/dense_1/kernel:0', 'nested/dense_1/bias:0', 'nested/dense_2/kernel:0', 'nested/dense_2/bias:0']

Changing trainable status of one of the nested layers...

variables: ['nested/dense_2/kernel:0', 'nested/dense_2/bias:0', 'nested/dense_1/kernel:0', 'nested/dense_1/bias:0']
variable ordering changed: True

ถ่ายทอดตัวอย่างการเรียนรู้

เมื่อโหลดน้ำหนักที่กำหนดไว้ล่วงหน้าจาก HDF5 ขอแนะนำให้โหลดน้ำหนักลงในแบบจำลองที่มีจุดตรวจสอบดั้งเดิมจากนั้นแยกน้ำหนัก / เลเยอร์ที่ต้องการออกเป็นโมเดลใหม่

ตัวอย่าง:

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_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_3 (Dense)              (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________