حفظ وتحميل نماذج Keras

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

مقدمة

يتكون نموذج Keras من مكونات متعددة:

  • العمارة ، أو التكوين ، الذي يحدد الطبقات التي يحتوي عليها النموذج ، وكيف يتم توصيلها.
  • مجموعة من قيم الأوزان ("حالة النموذج").
  • مُحسِّن (يتم تحديده من خلال تجميع النموذج).
  • وهناك مجموعة من الخسائر والمقاييس (المعرفة عن طريق تجميع نموذج أو الدعوة add_loss() أو add_metric() ).

تتيح واجهة برمجة تطبيقات 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

حفظ وتحميل نموذج كامل

يمكنك حفظ نموذج كامل في قطعة أثرية واحدة. سوف تشمل:

  • بنية النموذج / config
  • قيم وزن النموذج (التي تم تعلمها أثناء التدريب)
  • معلومات تجميع نموذج (إن compile() كان يسمى)
  • المُحسِّن وحالته ، إن وجدت (يتيح لك ذلك إعادة بدء التدريب من حيث تركت)

واجهات برمجة التطبيقات

هناك نوعان من الأشكال التي يمكن استخدامها لحفظ نموذج كامل على القرص: شكل 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 شكل مخازن اسم الفئة، وظيفة الدعوة، والخسائر، والأوزان (والتكوين، في حال تنفيذها). تحدد وظيفة الاستدعاء الرسم البياني لحساب النموذج / الطبقة.

في حالة عدم وجود تهيئة النموذج / الطبقة ، يتم استخدام وظيفة الاستدعاء لإنشاء نموذج موجود مثل النموذج الأصلي والذي يمكن تدريبه وتقييمه واستخدامه للاستدلال.

ومع ذلك، هو دائما ممارسة جيدة لتحديد get_config و from_config الأساليب عند كتابة نموذج مخصص أو فئة الطبقة. يتيح لك ذلك تحديث الحساب بسهولة لاحقًا إذا لزم الأمر. راجع المقطع حول الأشياء مخصص للمزيد من المعلومات.

مثال:

class CustomModel(keras.Model):
    def __init__(self, hidden_units):
        super(CustomModel, self).__init__()
        self.hidden_units = hidden_units
        self.dense_layers = [keras.layers.Dense(u) for u in hidden_units]

    def call(self, inputs):
        x = inputs
        for layer in self.dense_layers:
            x = layer(x)
        return x

    def get_config(self):
        return {"hidden_units": self.hidden_units}

    @classmethod
    def from_config(cls, config):
        return cls(**config)


model = CustomModel([16, 16, 10])
# Build the model by calling it
input_arr = tf.random.uniform((1, 5))
outputs = model(input_arr)
model.save("my_model")

# Option 1: Load with the custom_object argument.
loaded_1 = keras.models.load_model(
    "my_model", custom_objects={"CustomModel": CustomModel}
)

# Option 2: Load without the CustomModel class.

# Delete the custom-defined model class to ensure that the loader does not have
# access to it.
del CustomModel

loaded_2 = keras.models.load_model("my_model")
np.testing.assert_allclose(loaded_1(input_arr), outputs)
np.testing.assert_allclose(loaded_2(input_arr), outputs)

print("Original model:", model)
print("Model Loaded with custom objects:", loaded_1)
print("Model loaded without the custom object class:", loaded_2)
INFO:tensorflow:Assets written to: my_model/assets
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Original model: <__main__.CustomModel object at 0x7f9949c86810>
Model Loaded with custom objects: <__main__.CustomModel object at 0x7f99681f61d0>
Model loaded without the custom object class: <keras.saving.saved_model.load.CustomModel object at 0x7f9aaceefd10>

يتم تحميل أول نموذج تحميل باستخدام التكوين و CustomModel الصف. يتم تحميل النموذج الثاني عن طريق الإنشاء الديناميكي لفئة النموذج التي تعمل مثل النموذج الأصلي.

تكوين SavedModel

الجديد في TensoFlow 2.4 الحجة save_traces تمت إضافة إلى model.save ، والذي يسمح لك لتبديل SavedModel وظيفة البحث عن المفقودين. يتم حفظ وظائف للسماح للKeras إلى كائنات مخصصة إعادة التحميل دون definitons الطبقة الأصلية، وذلك عندما save_traces=False ، كل الكائنات المخصصة يجب أن يكون تعريف get_config / from_config الأساليب. عند تحميل، يجب أن يتم تمرير الكائنات مخصصة إلى custom_objects حجة. save_traces=False يقلل من مساحة القرص المستخدمة من قبل SavedModel وتوفير الوقت.

تنسيق Keras H5

يدعم Keras أيضا حفظ ملف HDF5 واحد يحتوي على الهندسة المعمارية للنموذج، قيم الأوزان، و compile() المعلومات. إنه بديل خفيف الوزن لـ SavedModel.

مثال:

model = get_model()

# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)

# Calling `save('my_model.h5')` creates a h5 file `my_model.h5`.
model.save("my_h5_model.h5")

# It can be used to reconstruct the model identically.
reconstructed_model = keras.models.load_model("my_h5_model.h5")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
4/4 [==============================] - 0s 1ms/step - loss: 1.6322
4/4 [==============================] - 0s 1ms/step - loss: 1.4790
<keras.callbacks.History at 0x7f9aacc0fd50>

محددات

مقارنةً بتنسيق SavedModel ، هناك شيئان لا يتم تضمينهما في ملف H5:

  • خسائر الخارجية ومقاييس وأضاف عبر model.add_loss() و model.add_metric() لا يتم حفظ (خلافا SavedModel). إذا كانت لديك مثل هذه الخسائر والمقاييس في نموذجك وترغب في استئناف التدريب ، فأنت بحاجة إلى إضافة هذه الخسائر مرة أخرى بنفسك بعد تحميل النموذج. لاحظ أن هذا لا ينطبق على خسائر / المقاييس التي تم إنشاؤها داخل طبقات عبر self.add_loss() و self.add_metric() . ما دام يحصل على تحميل الطبقة، وتحفظ هذه الخسائر والمقاييس، لأنها جزء من call طريقة طبقة.
  • الرسم البياني حساب مخصص كائنات مثل لا يتم تضمين طبقات مخصصة في الملف المحفوظ. في وقت التحميل ، سيحتاج Keras إلى الوصول إلى فئات / وظائف Python لهذه الكائنات لإعادة بناء النموذج. انظر كائنات مخصص .

إنقاذ العمارة

يحدد تكوين النموذج (أو البنية) الطبقات التي يحتوي عليها النموذج ، وكيفية توصيل هذه الطبقات *. إذا كان لديك تكوين نموذج ، فيمكن إنشاء النموذج بحالة تمت تهيئتها حديثًا للأوزان ولا توجد معلومات تجميع.

* لاحظ أن هذا ينطبق فقط على النماذج المحددة باستخدام النماذج الوظيفية أو التتابعية لواجهة برمجة التطبيقات غير المصنفة ضمن الفئات الفرعية.

تكوين نموذج تسلسلي أو نموذج API وظيفي

هذه الأنواع من النماذج عبارة عن رسوم بيانية صريحة للطبقات: تكوينها متاح دائمًا في شكل منظم.

واجهات برمجة التطبيقات

get_config() و from_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)

كائنات مخصصة

النماذج والطبقات

يتم تعريف بنية نماذج subclassed وطبقات في أساليب __init__ و call . وهي تعتبر بيثون بايت كود، التي لا يمكن إجراء تسلسل إلى التكوين JSON متوافق مع - قد تتمكن من محاولة تسلسل بايت كود (على سبيل المثال عن طريق 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 للتسلسل لكي تكون متوافقة مع Keras architecture- واجهات برمجة التطبيقات الموفرة للنموذج.
  • from_config(config) ( classmethod ) يجب إرجاع كائن طبقة أو النموذج الجديد الذي تم إنشاؤه من التكوين. يعود تطبيق الافتراضي cls(**config) .

مثال:

class CustomLayer(keras.layers.Layer):
    def __init__(self, a):
        self.var = tf.Variable(a, name="var_a")

    def call(self, inputs, training=False):
        if training:
            return inputs * self.var
        else:
            return inputs

    def get_config(self):
        return {"a": self.var.numpy()}

    # There's actually no need to define `from_config` here, since returning
    # `cls(**config)` is the default behavior.
    @classmethod
    def from_config(cls, config):
        return cls(**config)


layer = CustomLayer(5)
layer.var.assign(2)

serialized_layer = keras.layers.serialize(layer)
new_layer = keras.layers.deserialize(
    serialized_layer, custom_objects={"CustomLayer": CustomLayer}
)

تسجيل الكائن المخصص

يحتفظ Keras بملاحظة حول الفئة التي أنشأت ملف config. من المثال أعلاه، 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)

حفظ وتحميل قيم أوزان النموذج فقط

يمكنك اختيار حفظ أوزان النموذج وتحميلها فقط. يمكن أن يكون هذا مفيدًا إذا:

  • تحتاج فقط إلى نموذج للاستدلال: في هذه الحالة لن تحتاج إلى إعادة التدريب ، لذلك لا تحتاج إلى معلومات التجميع أو حالة المحسن.
  • أنت تقوم بالتعلم عن طريق التحويل: في هذه الحالة سوف تقوم بتدريب نموذج جديد لإعادة استخدام حالة النموذج السابق ، لذلك لا تحتاج إلى معلومات التجميع الخاصة بالنموذج السابق.

واجهات برمجة التطبيقات لنقل الوزن في الذاكرة

يمكن نسخ الأوزان بين كائنات مختلفة باستخدام 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())

واجهات برمجة التطبيقات لحفظ الأوزان على القرص وإعادة تحميلها

يمكن حفظ الأوزان إلى القرص عن طريق الاتصال model.save_weights في الأشكال التالية:

  • TensorFlow Checkpoint
  • HDF5

التنسيق الافتراضي لل model.save_weights هو نقطة تفتيش TensorFlow. هناك طريقتان لتحديد تنسيق الحفظ:

  1. save_format حجة: تعيين القيمة إلى save_format="tf" أو save_format="h5" .
  2. path حجة: إذا كان طرفي الطريق مع .h5 أو .hdf5 ، ثم يتم استخدام تنسيق HDF5. واللواحق الأخرى يؤدي في نقطة تفتيش TensorFlow ما لم save_format ومن المقرر.

هناك أيضًا خيار لاسترداد الأوزان كمصفوفات فارغة في الذاكرة. كل واجهة برمجة تطبيقات لها مزاياها وعيوبها الموضحة أدناه.

تنسيق 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" والقيم الوزن يناظرها. لمزيد من المعلومات، راجع "الميكانيكا تحميل" في دليل 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>

يوصى عمومًا بالالتزام بنفس واجهة برمجة التطبيقات لنماذج البناء. إذا قمت بالتبديل بين المتسلسلة والوظيفية ، أو الوظيفية والفئة الفرعية ، وما إلى ذلك ، فقم دائمًا بإعادة بناء النموذج المدرب مسبقًا وتحميل الأوزان المدربة مسبقًا على هذا النموذج.

السؤال التالي هو ، كيف يمكن حفظ الأوزان وتحميلها على نماذج مختلفة إذا كانت بنيات النموذج مختلفة تمامًا؟ الحل هو استخدام 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
_________________________________________________________________