![]() | ![]() | ![]() | ![]() |
บทนำ
โมเดล 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()
ถูกเรียกว่า) - เครื่องมือเพิ่มประสิทธิภาพและสถานะของมัน หากมี (สิ่งนี้ทำให้คุณสามารถเริ่มการฝึกใหม่จากที่ที่คุณไป)
APIs
-
model.save()
หรือtf.keras.models.save_model()
-
tf.keras.models.load_model()
มีสองรูปแบบที่คุณสามารถใช้เพื่อบันทึกรูปแบบทั้งดิสก์: รูปแบบ TensorFlow SavedModel และรูปแบบเก่า Keras H5 รูปแบบที่แนะนำคือ SavedModel มันเป็นค่าเริ่มต้นเมื่อคุณใช้ model.save()
คุณสามารถเปลี่ยนเป็นรูปแบบ H5 ได้โดย:
- ผ่าน
save_format='h5'
เพื่อsave()
- ผ่านชื่อไฟล์ที่สิ้นสุดใน
.h5
หรือ.keras
การsave()
รูปแบบโมเดลที่บันทึกไว้
SavedModel เป็นรูปแบบการบันทึกที่ครอบคลุมมากขึ้น ซึ่งจะบันทึกสถาปัตยกรรมโมเดล น้ำหนัก และกราฟย่อย Tensorflow ที่ติดตามของฟังก์ชันการโทร สิ่งนี้ทำให้ 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="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 [==============================] - 1s 2ms/step - loss: 0.5884 2021-08-25 17:49:05.320893: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them. INFO:tensorflow:Assets written to: my_model/assets 4/4 [==============================] - 0s 2ms/step - loss: 0.5197 <keras.callbacks.History at 0x7f99486ad490>
สิ่งที่ SavedModel ประกอบด้วย
โทร model.save('my_model')
สร้างโฟลเดอร์ชื่อ my_model
มีดังต่อไปนี้:
ls my_model
assets keras_metadata.pb saved_model.pb variables
สถาปัตยกรรมรูปแบบการฝึกอบรมและการกำหนดค่า (รวมถึงการเพิ่มประสิทธิภาพการสูญเสียและตัวชี้วัด) จะถูกเก็บไว้ใน saved_model.pb
น้ำหนักจะถูกบันทึกไว้ใน variables/
ไดเรกทอรี
สำหรับข้อมูลรายละเอียดเกี่ยวกับรูปแบบ SavedModel ให้ดูที่ คู่มือ SavedModel (รูปแบบ SavedModel บนดิสก์)
วิธีที่ SavedModel จัดการกับอ็อบเจ็กต์ที่กำหนดเอง
เมื่อบันทึกรูปแบบและชั้นของร้านค้ารูปแบบ SavedModel ชื่อชั้น, ฟังก์ชั่นการโทร, การสูญเสียและน้ำหนัก (และการตั้งค่าหากดำเนินการ) ฟังก์ชันการเรียกกำหนดกราฟการคำนวณของโมเดล/เลเยอร์
ในกรณีที่ไม่มีการกำหนดค่าโมเดล/เลเยอร์ ฟังก์ชันการเรียกจะใช้เพื่อสร้างโมเดลที่มีอยู่เหมือนกับโมเดลดั้งเดิมซึ่งสามารถฝึก ประเมิน และใช้สำหรับอนุมานได้
แต่มันก็มักจะเป็นวิธีที่ดีในการกำหนด get_config
และ from_config
วิธีเมื่อเขียนรูปแบบที่กำหนดเองหรือระดับชั้น ซึ่งจะทำให้คุณสามารถอัปเดตการคำนวณได้อย่างง่ายดายในภายหลังหากจำเป็น ดูในส่วนที่เกี่ยวกับ วัตถุที่กำหนดเอง สำหรับข้อมูลเพิ่มเติม
ตัวอย่าง:
class CustomModel(keras.Model):
def __init__(self, hidden_units):
super(CustomModel, self).__init__()
self.hidden_units = hidden_units
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
def get_config(self):
return {"hidden_units": self.hidden_units}
@classmethod
def from_config(cls, config):
return cls(**config)
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")
# Option 1: Load with the custom_object argument.
loaded_1 = keras.models.load_model(
"my_model", custom_objects={"CustomModel": CustomModel}
)
# Option 2: Load without the CustomModel class.
# Delete the custom-defined model class to ensure that the loader does not have
# access to it.
del CustomModel
loaded_2 = keras.models.load_model("my_model")
np.testing.assert_allclose(loaded_1(input_arr), outputs)
np.testing.assert_allclose(loaded_2(input_arr), outputs)
print("Original model:", model)
print("Model Loaded with custom objects:", loaded_1)
print("Model loaded without the custom object class:", loaded_2)
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. WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually. Original model: <__main__.CustomModel object at 0x7f9949c86810> Model Loaded with custom objects: <__main__.CustomModel object at 0x7f99681f61d0> Model loaded without the custom object class: <keras.saving.saved_model.load.CustomModel object at 0x7f9aaceefd10>
รูปแบบการโหลดครั้งแรกที่มีการโหลดโดยใช้การตั้งค่าและ CustomModel
ระดับ โมเดลที่สองถูกโหลดโดยการสร้างคลาสโมเดลแบบไดนามิกซึ่งทำหน้าที่เหมือนโมเดลดั้งเดิม
การกำหนดค่า SavedModel
ใหม่ใน TensoFlow 2.4 อาร์กิวเมนต์ save_traces
ได้รับการเพิ่ม model.save
ซึ่งช่วยให้คุณสามารถสลับการทำงาน SavedModel การติดตาม ฟังก์ชั่นจะถูกบันทึกไว้เพื่อให้ Keras กับวัตถุอีกครั้งโหลดที่กำหนดเองได้โดยไม่ต้อง definitons ระดับเดิมดังนั้นเมื่อ save_traces=False
วัตถุที่กำหนดเองทั้งหมดจะต้องมีการกำหนด get_config
/ from_config
วิธี เมื่อโหลดวัตถุที่กำหนดเองจะต้องมีการส่งผ่านไปยัง custom_objects
โต้แย้ง save_traces=False
ลดพื้นที่ดิสก์ที่ใช้โดย SavedModel และประหยัดเวลา
รูปแบบ 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: 1.6322 4/4 [==============================] - 0s 1ms/step - loss: 1.4790 <keras.callbacks.History at 0x7f9aacc0fd50>
ข้อจำกัด
เมื่อเทียบกับรูปแบบ SavedModel มีสองสิ่งที่ไม่รวมอยู่ในไฟล์ H5:
- ความเสียหายที่เกิดจากภายนอกและตัวชี้วัดที่เพิ่มผ่าน
model.add_loss()
และmodel.add_metric()
จะไม่ถูกบันทึก (ไม่เหมือน SavedModel) หากคุณมีการสูญเสียและตัววัดดังกล่าวในแบบจำลองของคุณ และต้องการดำเนินการฝึกอบรมต่อ คุณต้องเพิ่มการสูญเสียเหล่านี้กลับคืนมาด้วยตนเองหลังจากโหลดแบบจำลอง หมายเหตุว่านี้ไม่ได้นำไปใช้กับการสูญเสีย / ตัวชี้วัดที่สร้างขึ้นภายในชั้นผ่านself.add_loss()
และself.add_metric()
ตราบใดที่ชั้นได้รับการโหลดการสูญเสียและตัวชี้วัดเหล่านี้จะถูกเก็บไว้เนื่องจากพวกเขาเป็นส่วนหนึ่งของcall
วิธีการของชั้น - กราฟการคำนวณที่กำหนดเองวัตถุเช่นชั้นที่กำหนดเองไม่รวมอยู่ในไฟล์ที่บันทึกไว้ ขณะโหลด Keras จะต้องเข้าถึงคลาส/ฟังก์ชัน Python ของอ็อบเจ็กต์เหล่านี้เพื่อสร้างโมเดลใหม่ ดู วัตถุที่กำหนดเอง
อนุรักษ์สถาปัตยกรรม
การกำหนดค่าของโมเดล (หรือสถาปัตยกรรม) ระบุเลเยอร์ที่โมเดลประกอบด้วย และวิธีที่เลเยอร์เหล่านี้เชื่อมต่อ* หากคุณมีคอนฟิกูเรชันของโมเดล คุณสามารถสร้างโมเดลด้วยสถานะเริ่มต้นใหม่สำหรับน้ำหนักและไม่มีข้อมูลการคอมไพล์
*โปรดทราบว่าสิ่งนี้ใช้ได้กับโมเดลที่กำหนดโดยใช้ functional หรือ Sequential api ไม่ใช่โมเดลย่อย
การกำหนดค่าโมเดลตามลำดับหรือโมเดล Functional API
โมเดลประเภทนี้เป็นกราฟชั้นที่ชัดเจน: การกำหนดค่าจะพร้อมใช้งานในรูปแบบที่มีโครงสร้างเสมอ
APIs
-
get_config()
และfrom_config()
-
tf.keras.models.model_to_json()
และtf.keras.models.model_from_json()
get_config()
และ from_config()
โทร config = model.get_config()
จะกลับ Dict งูหลามที่มีการกำหนดค่าของรูปแบบ รูปแบบเดียวกันจากนั้นจะสามารถสร้างขึ้นใหม่ผ่าน Sequential.from_config(config)
(สำหรับ Sequential
รุ่น) หรือ Model.from_config(config)
(สำหรับรุ่น API Functional)
เวิร์กโฟลว์เดียวกันนี้ใช้ได้กับเลเยอร์ที่เรียงลำดับได้
ตัวอย่างชั้น:
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)
วัตถุที่กำหนดเอง
รุ่นและชั้น
สถาปัตยกรรมของรุ่น subclassed และชั้นที่กำหนดไว้ในวิธีการที่ __init__
และ call
พวกเขาจะถือว่า Python bytecode ซึ่งไม่สามารถต่อเนื่องเป็น config ของ JSON ที่เข้ากันได้ - คุณอาจจะลอง serializing bytecode (เช่นผ่าน pickle
) แต่มันไม่ปลอดภัยอย่างสมบูรณ์และหมายถึงรูปแบบของคุณไม่สามารถโหลดในระบบที่แตกต่างกัน
เพื่อที่จะบันทึก / โหลดรูปแบบที่มีชั้นที่กำหนดเองหรือรูปแบบ subclassed คุณควรเขียนทับ get_config
และเลือก from_config
วิธี นอกจากนี้ คุณควรใช้รีจิสเตอร์อ็อบเจ็กต์แบบกำหนดเองเพื่อให้ 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()
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model. 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-serializable ในการที่จะเข้ากันได้กับ Keras architecture- และ API รุ่นประหยัด -
from_config(config)
(classmethod
) ควรกลับใหม่ชั้นหรือรูปแบบวัตถุที่ถูกสร้างขึ้นจากการตั้งค่า เริ่มต้นใช้งานผลตอบแทน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
) มีสองสามวิธีในการลงทะเบียนคลาสที่กำหนดเองในรายการนี้:
- การตั้งค่า
custom_objects
โต้แย้งในฟังก์ชั่นในการโหลด (ดูตัวอย่างในหัวข้อ "การกำหนดวิธีการกำหนดค่า") -
tf.keras.utils.custom_object_scope
หรือtf.keras.utils.CustomObjectScope
-
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
:
-
tf.keras.layers.Layer.get_weights()
: คืนรายชื่อของอาร์เรย์ numpy -
tf.keras.layers.Layer.set_weights()
: ตั้งค่าน้ำหนักแบบจำลองเพื่อค่าใน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 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(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
ในรูปแบบต่อไปนี้:
- จุดตรวจเทนเซอร์โฟลว์
- HDF5
รูปแบบเริ่มต้นสำหรับ model.save_weights
เป็น TensorFlow ด่าน มีสองวิธีในการระบุรูปแบบการบันทึก:
-
save_format
อาร์กิวเมนต์: ตั้งค่าเป็นsave_format="tf"
หรือsave_format="h5"
-
path
อาร์กิวเมนต์: ถ้าเส้นทางนี้จบลงด้วย.h5
หรือ.hdf5
แล้วรูปแบบ HDF5 ถูกนำมาใช้ คำต่อท้ายอื่น ๆ จะมีผลในด่าน TensorFlow เว้นแต่save_format
เป็นชุด
นอกจากนี้ยังมีตัวเลือกในการดึงน้ำหนักเป็นอาร์เรย์ numpy ในหน่วยความจำ API แต่ละรายการมีข้อดีและข้อเสียซึ่งมีรายละเอียดด้านล่าง
รูปแบบจุดตรวจ TF
ตัวอย่าง:
# 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 0x7f9aaca4ced0>
รายละเอียดรูปแบบ
รูปแบบ TensorFlow Checkpoint จะบันทึกและคืนค่าน้ำหนักโดยใช้ชื่อแอตทริบิวต์ของวัตถุ ยกตัวอย่างเช่นพิจารณา tf.keras.layers.Dense
ชั้น ชั้นมีสองน้ำหนัก: dense.kernel
และ dense.bias
เมื่อชั้นที่มีการบันทึกไว้ใน tf
รูปแบบด่านส่งผลให้มีปุ่ม "kernel"
และ "bias"
และค่าน้ำหนักที่สอดคล้องกันของพวกเขา สำหรับข้อมูลเพิ่มเติมโปรดดูที่ "การโหลดกลศาสตร์" ในคู่มือลุยด่าน
โปรดทราบว่าขอบแอตทริบิวต์ / กราฟการตั้งชื่อตามชื่อที่ใช้ในวัตถุแม่ไม่ใช่ชื่อของตัวแปร พิจารณา 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 0x7f9aaca76990>
โดยทั่วไป ขอแนะนำให้ใช้ 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()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer.py:2223: UserWarning: `layer.add_variable` is deprecated and will be removed in a future version. Please use `layer.add_weight` method instead. warnings.warn('`layer.add_variable` is deprecated and ' <tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9aaca6f390>
รูปแบบ 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 _________________________________________________________________