![]() | ![]() | ![]() | ![]() |
Wprowadzenie
Model Keras składa się z wielu komponentów:
- Architektura lub konfiguracja, która określa, jakie warstwy zawiera model i jak są one połączone.
- Zbiór wartości wag („stan modelu”).
- Optymalizator (definiowany przez kompilację modelu).
- Zestaw strat i metryk (zdefiniowanych przez kompilację modelu lub wywołanie
add_loss()
lubadd_metric()
).
Interfejs API Keras umożliwia jednoczesne zapisanie wszystkich tych elementów na dysku lub wybiórcze zapisywanie tylko niektórych z nich:
- Zapisywanie wszystkiego w jednym archiwum w formacie TensorFlow SavedModel (lub w starszym formacie Keras H5). To jest standardowa praktyka.
- Zapisywanie tylko architektury / konfiguracji, zwykle jako plik JSON.
- Zapisywanie tylko wartości wag. Jest to zwykle używane podczas uczenia modelu.
Przyjrzyjmy się każdej z tych opcji. Kiedy użyłbyś jednego lub drugiego i jak one działają?
Jak zapisać i wczytać model
Jeśli masz tylko 10 sekund na przeczytanie tego przewodnika, oto, co musisz wiedzieć.
Zapisywanie modelu Keras:
model = ... # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location')
Ładowanie modelu z powrotem:
from tensorflow import keras
model = keras.models.load_model('path/to/location')
Teraz spójrzmy na szczegóły.
Ustawiać
import numpy as np
import tensorflow as tf
from tensorflow import keras
Zapisywanie i ładowanie całego modelu
Możesz zapisać cały model jako pojedynczy artefakt. Będzie zawierał:
- Architektura / config modelu
- Wartości masy modelu (których nauczyliśmy się podczas treningu)
- Informacje o kompilacji modelu (jeśli
compile()
funkcjęcompile()
) - Optymalizator i jego stan, jeśli istnieje (umożliwia to ponowne uruchomienie treningu w miejscu, w którym został)
Pszczoła
-
model.save()
lubtf.keras.models.save_model()
-
tf.keras.models.load_model()
Istnieją dwa formaty, których można użyć do zapisania całego modelu na dysku: format TensorFlow SavedModel i starszy format Keras H5 . Zalecany format to SavedModel. Jest to ustawienie domyślne, gdy używasz model.save()
.
Możesz przejść do formatu H5 przez:
- Przekazywanie
save_format='h5'
dosave()
. - Przekazanie nazwy pliku kończącej się na
.h5
lub.keras
abysave()
.
Format SavedModel
SavedModel to bardziej wszechstronny format zapisu, który zapisuje architekturę modelu, wagi i śledzone podgrafy Tensorflow funkcji wywołań. Dzięki temu Keras może przywrócić zarówno wbudowane warstwy, jak i niestandardowe obiekty.
Przykład:
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>
Co zawiera SavedModel
Wywołanie model.save('my_model')
tworzy folder o nazwie my_model
, zawierający następujące elementy:
ls my_model
assets saved_model.pb variables
Architektura modelu i konfiguracja treningowa (w tym optymalizator, straty i metryki) są przechowywane w saved_model.pb
. Wagi są zapisywane w katalogu variables/
.
Aby uzyskać szczegółowe informacje na temat formatu SavedModel, zobacz przewodnik SavedModel ( format SavedModel na dysku ) .
Jak SavedModel obsługuje obiekty niestandardowe
Podczas zapisywania modelu i jego warstw format SavedModel przechowuje nazwę klasy, funkcję wywołania , straty i wagi (oraz konfigurację, jeśli zaimplementowano). Funkcja wywołania definiuje wykres obliczeniowy modelu / warstwy.
W przypadku braku konfiguracji modelu / warstwy funkcja wywołania jest używana do tworzenia modelu, który istnieje jak oryginalny model, który można wytrenować, ocenić i wykorzystać do wnioskowania.
Niemniej jednak zawsze dobrą praktyką jest definiowanie metod get_config
i from_config
podczas pisania niestandardowego modelu lub klasy warstw. Pozwala to w razie potrzeby łatwo zaktualizować obliczenia później. Więcej informacji można znaleźć w sekcji dotyczącej obiektów niestandardowych .
Przykład:
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>
Pierwszy załadowany model jest ładowany przy użyciu klasy config i CustomModel
. Drugi model jest ładowany przez dynamiczne tworzenie klasy modelu, która działa jak oryginalny model.
Konfigurowanie SavedModel
Nowość w TensoFlow 2.4 Argument save_traces
został dodany do model.save
, który umożliwia przełączanie śledzenia funkcji SavedModel. Funkcje są zapisywane, aby umożliwić Kerasowi ponowne ładowanie obiektów niestandardowych bez oryginalnych save_traces=False
klas, więc gdy save_traces=False
, wszystkie obiekty niestandardowe muszą mieć zdefiniowane metody get_config
/ from_config
. Podczas ładowania obiekty niestandardowe muszą zostać przekazane do argumentu custom_objects
. save_traces=False
zmniejsza przestrzeń dyskową używaną przez SavedModel i oszczędza czas.
Format Keras H5
Keras obsługuje również zapisywanie pojedynczego pliku HDF5 zawierającego architekturę modelu, wartości wag i informacje compile()
. Jest to lekka alternatywa dla SavedModel.
Przykład:
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>
Ograniczenia
W porównaniu z formatem SavedModel istnieją dwie rzeczy, które nie są uwzględniane w pliku H5:
- Zewnętrzne straty i metryki dodane przez
model.add_loss()
imodel.add_metric()
nie są zapisywane (w przeciwieństwie do SavedModel). Jeśli masz takie straty i metryki w swoim modelu i chcesz wznowić szkolenie, musisz dodać te straty samodzielnie po załadowaniu modelu. Zwróć uwagę, że nie dotyczy to strat / metryk utworzonych wewnątrz warstw za pomocą funkcjiself.add_loss()
iself.add_metric()
. Dopóki warstwa jest ładowana, te straty i metryki są zachowywane, ponieważ są częścią metodycall
warstwy. - Wykres obliczeniowy obiektów niestandardowych, takich jak warstwy niestandardowe, nie jest zawarty w zapisanym pliku. W czasie ładowania Keras będzie potrzebować dostępu do klas / funkcji tych obiektów w języku Python, aby zrekonstruować model. Zobacz Obiekty niestandardowe .
Ratowanie architektury
Konfiguracja (lub architektura) modelu określa, jakie warstwy zawiera model i jak te warstwy są połączone *. Jeśli masz konfigurację modelu, możesz utworzyć model ze świeżo zainicjowanym stanem dla wag i bez informacji o kompilacji.
* Należy pamiętać, że dotyczy to tylko modeli zdefiniowanych za pomocą funkcji lub sekwencyjnych interfejsów API bez modeli podklas.
Konfiguracja modelu sekwencyjnego lub modelu funkcjonalnego interfejsu API
Te typy modeli są jawnymi wykresami warstw: ich konfiguracja jest zawsze dostępna w ustrukturyzowanej formie.
Pszczoła
-
get_config()
ifrom_config()
-
tf.keras.models.model_to_json()
itf.keras.models.model_from_json()
get_config()
i from_config()
Wywołanie config = model.get_config()
zwróci config = model.get_config()
Pythona zawierające konfigurację modelu. Ten sam model można następnie zrekonstruować za pomocą Sequential.from_config(config)
(dla modelu Sequential
) lub Model.from_config(config)
(dla modelu funkcjonalnego interfejsu API).
Ten sam przepływ pracy działa również dla każdej warstwy możliwej do serializacji.
Przykład warstwy:
layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_config()
new_layer = keras.layers.Dense.from_config(layer_config)
Przykład modelu sekwencyjnego:
model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
config = model.get_config()
new_model = keras.Sequential.from_config(config)
Przykład modelu funkcjonalnego:
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()
i tf.keras.models.model_from_json()
Jest to podobne do get_config
/ from_config
, z tą różnicą, że zamienia model w ciąg JSON, który można następnie załadować bez oryginalnej klasy modelu. Jest również specyficzny dla modeli, nie jest przeznaczony dla warstw.
Przykład:
model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
json_config = model.to_json()
new_model = keras.models.model_from_json(json_config)
Obiekty niestandardowe
Modele i warstwy
Architektura modeli i warstw podklasowych jest zdefiniowana w metodach __init__
i call
. Są one uważane za kod bajtowy Pythona, którego nie można serializować do konfiguracji zgodnej z JSON - możesz spróbować serializować kod bajtowy (np. Przez pickle
), ale jest to całkowicie niebezpieczne i oznacza, że twój model nie może być załadowany w innym systemie.
Aby zapisać / załadować model z niestandardowymi warstwami lub modelem get_config
, należy nadpisać metody get_config
i opcjonalnie from_config
. Ponadto należy użyć funkcji zarejestruj niestandardowy obiekt, aby Keras był tego świadomy.
Funkcje niestandardowe
Funkcje zdefiniowane przez użytkownika (np. Utrata aktywacji lub inicjalizacja) nie wymagają metody get_config
. Nazwa funkcji jest wystarczająca do załadowania, o ile jest zarejestrowana jako obiekt niestandardowy.
Ładowanie samego wykresu TensorFlow
Możliwe jest załadowanie wykresu TensorFlow wygenerowanego przez Keras. Jeśli to zrobisz, nie będziesz musiał podawać żadnych custom_objects
. Możesz to zrobić w ten sposób:
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
Zwróć uwagę, że ta metoda ma kilka wad:
- Ze względu na możliwość śledzenia należy zawsze mieć dostęp do używanych obiektów niestandardowych. Nie chciałbyś wprowadzić do produkcji modelu, którego nie możesz odtworzyć.
- Obiekt zwrócony przez
tf.saved_model.load
nie jest modelem Keras. Więc nie jest tak łatwy w użyciu. Na przykład nie będziesz mieć dostępu do.predict()
ani.fit()
Nawet jeśli jego użycie jest odradzane, może ci pomóc, jeśli jesteś w trudnej sytuacji, na przykład jeśli zgubiłeś kod swoich niestandardowych obiektów lub masz problemy z ładowaniem modelu za pomocą tf.keras.models.load_model()
.
Możesz dowiedzieć się więcej na stronie o tf.saved_model.load
Definiowanie metod konfiguracji
Specyfikacje:
-
get_config
powinien zwrócić słownik z możliwością serializacji JSON, aby był zgodny z interfejsami API zapisującymi architekturę i modele Keras. -
from_config(config)
(classmethod
) powinien zwrócić nową warstwę lub obiekt modelu, który jest tworzony zclassmethod
config. Domyślna implementacja zwracacls(**config)
.
Przykład:
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}
)
Rejestracja obiektu niestandardowego
Keras odnotowuje, która klasa wygenerowała plik config. Z powyższego przykładu, tf.keras.layers.serialize
generuje serializowaną postać warstwy niestandardowej:
{'class_name': 'CustomLayer', 'config': {'a': 2} }
Keras przechowuje główną listę wszystkich wbudowanych klas warstw, modeli, optymalizatorów i metryk, która jest używana do znalezienia właściwej klasy do wywołania from_config
. Jeśli nie można znaleźć klasy, zgłaszany jest błąd ( Value Error: Unknown layer
). Istnieje kilka sposobów rejestrowania niestandardowych klas na tej liście:
- Ustawienie argumentu
custom_objects
w funkcji ładowania. (zobacz przykład w sekcji powyżej „Definiowanie metod konfiguracji”) -
tf.keras.utils.custom_object_scope
lubtf.keras.utils.CustomObjectScope
-
tf.keras.utils.register_keras_serializable
Przykład warstwy i funkcji niestandardowej
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)
Klonowanie modelu w pamięci
Możesz także tf.keras.models.clone_model()
model w pamięci za pomocą tf.keras.models.clone_model()
. Jest to równoważne pobraniu konfiguracji, a następnie odtworzeniu modelu z jego konfiguracji (więc nie zachowuje informacji o kompilacji ani wartości wag warstw).
Przykład:
with keras.utils.custom_object_scope(custom_objects):
new_model = keras.models.clone_model(model)
Zapisywanie i ładowanie tylko wartości wag modelu
Możesz wybrać tylko zapisywanie i ładowanie ciężarów modelu. Może to być przydatne, jeśli:
- Potrzebujesz tylko modelu do wnioskowania: w tym przypadku nie będziesz musiał ponownie uruchamiać uczenia, więc nie potrzebujesz informacji o kompilacji ani stanu optymalizatora.
- Robisz uczenie transferowe: w tym przypadku będziesz trenować nowy model, ponownie wykorzystując stan poprzedniego modelu, więc nie potrzebujesz informacji kompilacji z poprzedniego modelu.
API do przenoszenia wagi w pamięci
Wagi można kopiować między różnymi obiektami za pomocą get_weights
i set_weights
:
-
tf.keras.layers.Layer.get_weights()
: Zwraca listę numpy tablic. -
tf.keras.layers.Layer.set_weights()
: ustawia wagi modelu na wartości w argumencieweights
.
Przykłady poniżej.
Przenoszenie ciężarów z jednej warstwy na drugą w pamięci
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())
Przenoszenie wag z jednego modelu do innego z kompatybilną architekturą w pamięci
# 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())
Przypadek warstw bezpaństwowych
Ponieważ warstwy bezstanowe nie zmieniają kolejności ani liczby wag, modele mogą mieć kompatybilne architektury, nawet jeśli istnieją dodatkowe / brakujące warstwy bezstanowe.
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())
Interfejsy API do zapisywania ciężarów na dysku i wczytywania ich z powrotem
Wagi można zapisać na dysku, wywołując model.save_weights
w następujących formatach:
- Punkt kontrolny TensorFlow
- HDF5
Domyślnym formatem dla model.save_weights
jest punkt kontrolny TensorFlow. Istnieją dwa sposoby określenia formatu zapisu:
-
save_format
argument: Ustaw wartość nasave_format="tf"
lubsave_format="h5"
. - argument
path
: jeśli ścieżka kończy się na.h5
lub.hdf5
, używany jest format HDF5. Inne sufiksy spowodują utworzenie punktu kontrolnegosave_format
chyba że ustawionosave_format
.
Istnieje również opcja pobierania wag jako tablic numpy w pamięci. Każdy interfejs API ma swoje wady i zalety, które opisano szczegółowo poniżej.
Format TF Checkpoint
Przykład:
# 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>
Szczegóły formatu
Format TensorFlow Checkpoint zapisuje i przywraca wagi przy użyciu nazw atrybutów obiektów. tf.keras.layers.Dense
na przykład pod uwagę warstwę tf.keras.layers.Dense
. Warstwa zawiera dwie wagi: dense.bias
. dense.kernel
i dense.bias
. dense.bias
. Gdy warstwa jest zapisywana w formacie tf
, wynikowy punkt kontrolny zawiera klucze "kernel"
i "bias"
oraz odpowiadające im wartości wagi. Aby uzyskać więcej informacji, zobacz „Mechanika ładowania” w przewodniku po punkcie kontrolnym TF .
Zauważ, że nazwa krawędzi atrybutu / wykresu pochodzi od nazwy użytej w obiekcie nadrzędnym, a nie po nazwie zmiennej . Rozważmy CustomLayer
w poniższym przykładzie. Zmienna CustomLayer.var
jest zapisywana z "var"
zmienną "var"
jako częścią klucza, a nie "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}
Przenieś przykład nauki
Zasadniczo, o ile dwa modele mają tę samą architekturę, mogą współdzielić ten sam punkt kontrolny.
Przykład:
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>
Generalnie zaleca się trzymanie się tego samego API do budowania modeli. Jeśli przełączysz się między sekwencyjną i funkcjonalną lub funkcjonalną i podklasą itp., Zawsze odbuduj wstępnie wytrenowany model i załaduj wstępnie wytrenowane wagi do tego modelu.
Kolejne pytanie brzmi: w jaki sposób można zapisywać wagi i ładować je do różnych modeli, jeśli architektury modeli są zupełnie inne? Rozwiązaniem jest użycie tf.train.Checkpoint
do zapisywania i przywracania dokładnych warstw / zmiennych.
Przykład:
# 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>
Format HDF5
Format HDF5 zawiera wagi pogrupowane według nazw warstw. Wagi są listami uporządkowanymi przez konkatenację listy ciężarów, które można layer.weights
z listą ciężarów, których nie można layer.weights
(tak samo jak layer.weights
). W ten sposób model może używać punktu kontrolnego hdf5, jeśli ma te same warstwy i możliwe do wyszkolenia statusy, jak zapisane w punkcie kontrolnym.
Przykład:
# 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")
Zwróć uwagę, że zmiana layer.trainable
może spowodować layer.weights
kolejności layer.weights
gdy model zawiera warstwy zagnieżdżone.
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
Przenieś przykład nauki
Podczas ładowania wstępnie wytrenowanych ciężarów z HDF5, zaleca się załadowanie ciężarów do oryginalnego modelu z punktami kontrolnymi, a następnie wyodrębnienie żądanych ciężarów / warstw do nowego modelu.
Przykład:
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 _________________________________________________________________