![]() | ![]() | ![]() | ![]() |
บทนำ
โมเดล 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
-
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.9237 INFO:tensorflow:Assets written to: my_model/assets 4/4 [==============================] - 0s 1ms/step - loss: 0.7730 <tensorflow.python.keras.callbacks.History at 0x7fd0a032a390>
สิ่งที่ 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.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 0x7fd0a035bcf8> Model Loaded with custom objects: <__main__.CustomModel object at 0x7fd1455d04e0> Model loaded without the custom object class: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7fd14553af98>
โหลดโมเดลแรกโดยใช้คลาส config และ CustomModel
โมเดลที่สองถูกโหลดโดยการสร้างคลาสโมเดลแบบไดนามิกที่ทำหน้าที่เหมือนโมเดลดั้งเดิม
การกำหนดค่า SavedModel
ใหม่ใน TensoFlow 2.4 อาร์กิวเมนต์ save_traces
ได้ถูกเพิ่มลงใน model.save
ซึ่งช่วยให้คุณสามารถสลับการติดตามฟังก์ชัน SavedModel ฟังก์ชันจะถูกบันทึกเพื่ออนุญาตให้ Keras โหลดอ็อบเจ็กต์แบบกำหนดเองซ้ำโดยไม่มีคลาสดั้งเดิมดังนั้นเมื่อ 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.0153 4/4 [==============================] - 0s 1ms/step - loss: 0.9104 <tensorflow.python.keras.callbacks.History at 0x7fd1455c66a0>
ข้อ จำกัด
เมื่อเทียบกับรูปแบบ 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()
-
tf.keras.models.model_to_json()
และtf.keras.models.model_from_json()
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
) มีสองสามวิธีในการลงทะเบียนคลาสแบบกำหนดเองในรายการนี้:
- การตั้งค่าอาร์กิวเมนต์
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()
: ส่งคืนรายการอาร์เรย์tf.keras.layers.Layer.get_weights()
-
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
ในรูปแบบต่อไปนี้:
- ด่าน TensorFlow
- HDF5
รูปแบบเริ่มต้นสำหรับ model.save_weights
คือจุดตรวจ TensorFlow มีสองวิธีในการระบุรูปแบบการบันทึก:
- อาร์กิวเมนต์
save_format
: ตั้งค่าเป็นsave_format="tf"
หรือsave_format="h5"
- อาร์กิวเมนต์
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 0x7fd0a065f128>
รายละเอียดรูปแบบ
รูปแบบ 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 0x7fd144b20b38>
โดยทั่วไปขอแนะนำให้ใช้ 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.6/site-packages/tensorflow/python/keras/engine/base_layer.py:2281: 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 0x7fd1455c6cc0>
รูปแบบ 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 _________________________________________________________________