![]() | ![]() | ![]() | ![]() |
מבוא
דגם Keras מורכב ממספר רכיבים:
- הארכיטקטורה, או התצורה, המציינת אילו שכבות מכיל המודל, וכיצד הן מחוברות.
- קבוצה של ערכי משקלים ("מצב המודל").
- מייעל (מוגדר על ידי קומפילציה של המודל).
- סט של הפסדים ובערכים (המוגדר על ידי עריכת המודל או קוראים
add_loss()
אוadd_metric()
).
ה-API של Keras מאפשר לשמור את כל החלקים האלה בדיסק בבת אחת, או לשמור רק חלק מהם באופן סלקטיבי:
- שמירת הכל בארכיון בודד בפורמט 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
שמירה וטעינה בדגם שלם
אתה יכול לשמור דגם שלם לחפץ בודד. זה יכלול:
- הארכיטקטורה/תצורה של הדגם
- ערכי המשקל של הדגם (שנלמדו במהלך האימון)
- המידע האוסף של המודל (אם
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
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 שם המחלקה, קריאה לפונקציה, הפסדים, משקולות (ואת config, אם ייושם). פונקציית הקריאה מגדירה את גרף החישוב של המודל/שכבה.
בהיעדר תצורת המודל/שכבה, פונקציית הקריאה משמשת ליצירת מודל שקיים כמו המודל המקורי שניתן לאמן, להעריך ולהשתמש בו להסקת מסקנות.
אף על פי כן, זה תמיד טוב להתאמן כדי להגדיר את 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>
המודל הראשון נטען נטען באמצעות config ו 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 של אובייקטים אלה על מנת לשחזר את המודל. ראו Custom אובייקטים .
שמירת הארכיטקטורה
התצורה (או הארכיטקטורה) של המודל מציינת אילו שכבות המודל מכיל, וכיצד שכבות אלו מחוברות*. אם יש לך תצורה של מודל, ניתן ליצור את המודל עם מצב אתחול טרי עבור המשקולות וללא מידע קומפילציה.
*שימו לב שזה חל רק על מודלים שהוגדרו באמצעות ה-functional או Sequential apis לא במודלים של תת-סיווג.
תצורה של מודל Sequential או Functional 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()
תחזיר dict Python המכיל את התצורה של המודל. אותו דגם אז יכול להיות משוחזר באמצעות Sequential.from_config(config)
(עבור Sequential
מודל) או Model.from_config(config)
(עבור דגם ה- 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)
אובייקטים מותאמים אישית
דגמים ושכבות
הארכיטקטורה של דגמים ושכבות subclassed מוגדרת שיטות __init__
ואת call
. הם נחשבים bytecode פייתון, אשר לא ניתן בהמשכים לתוך config JSON תואם - אתה יכול לנסות בהמשכים את 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- ו- APIs מצילת מודל. -
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()
: מחזיר רשימה של מערכי 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
בתבניות הבאות:
- מחסום TensorFlow
- HDF5
תבנית ברירת המחדל עבור model.save_weights
היא מחסום TensorFlow. ישנן שתי דרכים לציין את פורמט השמירה:
-
save_format
טיעון: קבע את הערךsave_format="tf"
אוsave_format="h5"
. -
path
הטיעון: אם הקצוות נתיב עם.h5
או.hdf5
, אז הפורמט HDF5 משמש. סיומות אחרות תגרומנה במחסום TensorFlow אלא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 0x7f9aaca4ced0>
פרטי פורמט
פורמט TensorFlow Checkpoint שומר ומשחזר את המשקולות באמצעות שמות של תכונות אובייקט. למשל, לשקול את tf.keras.layers.Dense
השכבה. השכבה מכילה שתי משקולות: dense.kernel
ו dense.bias
. כאשר השכבה נשמרה tf
הפורמט, במחסום וכתוצאה מכיל את המפתחות "kernel"
ו "bias"
וערכי המשקל המתאימים שלהם. לקבלת מידע נוסף, ראה "מכניקה טוען" במדריך Checkpoint TF .
שים לב קצה תכונה / גרף קרוי על השם המשמש אובייקט הורה, ולא השם של המשתנה. קחו למשל את 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 לבניית דגמים. אם אתה עובר בין רצף לפונקציונלי, או פונקציונלי ותת-מחלקה וכו', אז תמיד בנה מחדש את הדגם המאומן מראש וטען את המשקולות המאומנות מראש לאותו דגם.
השאלה הבאה היא, כיצד ניתן לשמור משקלים ולהטעין אותם לדגמים שונים אם ארכיטקטורות המודלים שונות למדי? הפתרון הוא להשתמש 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 _________________________________________________________________