יום הקהילה ML הוא 9 בנובמבר! הצטרפו אלינו עדכונים מ- TensorFlow, JAX, ועוד למידע נוסף

שמור וטען מודלים של Keras

צפה ב- TensorFlow.org הפעל ב- Google Colab צפה במקור ב- GitHub הורד מחברת

מבוא

דגם 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

ישנם שני פורמטים שבהם ניתן להשתמש כדי לשמור המודל כולו לדיסק: פורמט TensorFlow SavedModel, ואת הפורמט Keras H5 המבוגר. הפורמט המומלץ הוא SavedModel. זוהי ברירת המחדל כאשר אתה משתמש model.save() .

אתה יכול לעבור לפורמט H5 על ידי:

  • עובר save_format='h5' כדי save() .
  • עובר שם קובץ שמסתיים ב- .h5 או .keras כדי save() .

פורמט SavedModel

SavedModel הוא פורמט השמירה המקיף יותר ששומר את ארכיטקטורת המודל, המשקולות ותתי התצלומים של Tensorflow העוקבים אחר פונקציות השיחה. זה מאפשר לקרס לשחזר הן שכבות מובנות והן אובייקטים מותאמים אישית.

דוגמא:

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 אובייקטים .

שמירת האדריכלות

תצורת הדגם (או הארכיטקטורה) מציינת אילו שכבות מכיל המודל וכיצד מחוברות שכבות אלה*. אם יש לך את התצורה של דגם, ניתן ליצור את המודל עם מצב מאתחל למשקלים וללא מידע על אוסף.

*שים לב שזה חל רק על דגמים המוגדרים באמצעות הדגמים הפונקציונליים או רצפים שלא בדגמי משנה.

תצורת מודל רציף או מודל API פונקציונלי

סוגים אלה של מודלים הם גרפים מפורשים של שכבות: התצורה שלהם זמינה תמיד בצורה מובנית.

ממשקי API

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 שיטות. בנוסף, עליך להשתמש ברישום האובייקט המותאם אישית כך שקרס יהיה מודע לכך.

פונקציות בהתאמה אישית

מוגדרות אישית פונקציות (פסד הפעלה למשל או אתחול) לא צריכים get_config שיטה. שם הפונקציה מספיק לטעינה כל עוד הוא רשום כאובייקט מותאם אישית.

טוען את גרף TensorFlow בלבד

אפשר לטעון את גרף TensorFlow שנוצר על ידי הקרס. אם תעשה זאת, אתה לא תצטרך לספק כל 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 ). ישנן מספר דרכים לרשום שיעורים מותאמים אישית לרשימה זו:

  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 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. ישנן שתי דרכים לציין את פורמט השמירה:

  1. save_format טיעון: קבע את הערך save_format="tf" או save_format="h5" .
  2. path הטיעון: אם הקצוות נתיב עם .h5 או .hdf5 , אז הפורמט HDF5 משמש. סיומות אחרות תגרומנה במחסום TensorFlow אלא save_format מוגדר.

יש גם אפשרות לאחזור משקולות כמערכים חסרי זיכרון. לכל 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
_________________________________________________________________