השב / י לאירוע TensorFlow Everywhere המקומי שלך היום!
דף זה תורגם על ידי Cloud Translation API.
Switch to English

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

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

מבוא

מודל Keras מורכב ממספר רכיבים:

  • הארכיטקטורה, או התצורה, המציינת אילו שכבות מכיל המודל וכיצד הם מחוברים.
  • קבוצה של ערכי משקולות ("מצב המודל").
  • אופטימיזציה (מוגדרת על ידי קומפילציה של המודל).
  • קבוצה של הפסדים וערכים (מוגדרים על ידי קומפילציה של המודל או קריאת add_loss() או add_metric() ).

ממשק ה- API של Keras מאפשר לשמור את כל החלקים הללו בדיסק בו זמנית, או לשמור רק חלק מהם באופן סלקטיבי:

  • שמירת הכל בארכיון יחיד בפורמט TensorFlow SavedModel (או בפורמט הישן יותר של Keras H5). זה הנוהג המקובל.
  • שמירת הארכיטקטורה / התצורה בלבד, בדרך כלל כקובץ JSON.
  • שמירת ערכי המשקולות בלבד. זה משמש בדרך כלל בעת אימון המודל.

בואו נסתכל על כל אחת מהאפשרויות הללו. מתי היית משתמש בזה או אחר, ואיך הם עובדים?

כיצד לשמור ולהעמיס דגם

אם יש לך רק 10 שניות לקרוא את המדריך הזה, הנה מה שאתה צריך לדעת.

שמירת מודל קרס:

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 המעקב אחר פונקציות השיחה. זה מאפשר 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 בדיסק ) .

כיצד 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 לאובייקטים מנהג מחדש עומס ללא definitons המעמד המקורי, ולכן כאשר save_traces=False , כל האובייקטים מנהג ודאי מוגדר get_config / from_config שיטות. בעת הטעינה יש להעביר את האובייקטים המותאמים אישית לארגומנט custom_objects . save_traces=False מצמצם את שטח הדיסק המשמש את ה- SavedModel וחוסך זמן.

פורמט H5 של Keras

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 של אובייקטים אלה על מנת לשחזר את המודל. ראה אובייקטים בהתאמה אישית .

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

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

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

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

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

ממשקי API

get_config() ו- from_config()

קריאה ל config = model.get_config() תחזיר config = model.get_config() של פייתון המכילה את תצורת המודל. לאחר מכן ניתן לשחזר את אותו דגם באמצעות 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)

אובייקטים בהתאמה אישית

דגמים ושכבות

הארכיטקטורה של מודלים ושכבות __init__ משנה מוגדרת בשיטות __init__ ו- call . הם נחשבים ל- Python bytecode, שלא ניתן לסדרו לתצורה תואמת JSON - אתה יכול לנסות לסדר את קוד ה- bytecode (למשל באמצעות pickle ), אבל זה לגמרי לא בטוח ומשמעותו שלא ניתן לטעון את המודל שלך במערכת אחרת.

על מנת לשמור / לטעון מודל עם שכבות שהוגדרו בהתאמה אישית, או דגם get_config משנה, עליכם להחליף את שיטות from_config . בנוסף, עליך להשתמש ברישום האובייקט המותאם אישית כך שקרס יהיה מודע לו.

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

פונקציות המוגדרות בהתאמה אישית (למשל אובדן הפעלה או אתחול) אינן זקוקות לשיטת 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 שניתן לסדרו על מנת להיות תואם לממשקי ה- API של Keras לארכיטקטורה ולשמירת מודלים.
  • 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}
)

רישום האובייקט המותאם אישית

קרס רושם הערה איזה מחלקה יצרה את התצורה. מהדוגמה לעיל, tf.keras.layers.serialize מייצר צורה סדרתית של השכבה המותאמת אישית:

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

Keras שומר רשימת מאסטר של כל הכיתות, הדגמים, האופטימיזציה from_config המשמשים למציאת המחלקה הנכונה להתקשר מ- 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. סיומות אחרות יביאו למחסום save_format אלא אם כן מוגדר save_format .

יש גם אפשרות לשלוף משקולות כמערכים קהים בזיכרון. לכל 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 0x7fd0a065f128>

לעצב פרטים

הפורמט של נקודת הבדיקה TensorFlow שומר ומשקם את המשקולות באמצעות שמות תכונות אובייקטים. לדוגמה, שקול את השכבה tf.keras.layers.Dense . השכבה מכילה שני משקלים: dense.kernel ו- dense.bias . כאשר השכבה נשמרת בפורמט tf , נקודת הבידוק המתקבלת מכילה את המפתחות "kernel" "bias" וערכי המשקל המתאימים להם. למידע נוסף ראה "מכניקת טעינה" במדריך למחסום 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 0x7fd144b20b38>

בדרך כלל מומלץ להקפיד על אותו 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.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 לרשימת המשקלים שאינם ניתנים לאימון (זהה 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
_________________________________________________________________