![]() | ![]() | ![]() | ![]() |
Wstęp
Model Keras składa się z wielu elementów:
- Architektura lub konfiguracja, która określa, jakie warstwy zawiera model i jak są one połączone.
- Zestaw wartości wag ("stan modelu").
- Optymalizator (zdefiniowany przez kompilację modelu).
- Zestaw strat i metryk (zdefiniowany przez kompilacji modelu lub dzwoniąc
add_loss()
lubadd_metric()
).
Keras API umożliwia jednoczesne zapisanie wszystkich tych elementów na dysku lub tylko wybiórcze zapisanie niektórych z nich:
- Zapisywanie wszystkiego w jednym archiwum w formacie TensorFlow SavedModel (lub w starszym formacie Keras H5). To jest standardowa praktyka.
- Zapisanie samej architektury/konfiguracji, zazwyczaj 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żywałbyś jednego lub drugiego i jak one działają?
Jak zapisać i załadować 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')
Przyjrzyjmy się teraz szczegółom.
Ustawiać
import numpy as np
import tensorflow as tf
from tensorflow import keras
Zapisywanie i ładowanie całego modelu
Cały model można zapisać w jednym artefakcie. Obejmie:
- Architektura/konfiguracja modelu
- Wartości wag modelki (które zostały wyuczone podczas treningu)
- Informacje kompilacja modelu (jeśli
compile()
został powołany) - Optymalizator i jego stan, jeśli istnieje (pozwala to na wznowienie treningu od miejsca, w którym się zakończyłeś)
Pszczoła
-
model.save()
lubtf.keras.models.save_model()
-
tf.keras.models.load_model()
Istnieją dwa formaty można użyć, aby zapisać cały model na dysk: format TensorFlow SavedModel i starszego formatu Keras H5. Zalecany format to SavedModel. Jest to domyślny podczas korzystania model.save()
.
Możesz przełączyć się na format H5 poprzez:
- Przechodząc
save_format='h5'
abysave()
. - Przekazując nazwę pliku, który kończy się
.h5
lub.keras
dosave()
.
Zapisany format modelu
SavedModel to bardziej wszechstronny format zapisu, który zapisuje architekturę modelu, wagi i prześledzone podgrafy Tensorflow funkcji wywołania. Dzięki temu Keras może przywracać zarówno warstwy wbudowane, jak i obiekty niestandardowe.
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.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>
Co zawiera SavedModel
Wywołanie model.save('my_model')
tworzy folder o nazwie my_model
, zawierające następujące informacje:
ls my_model
assets keras_metadata.pb saved_model.pb variables
Model architektury i konfiguracji szkolenia (w tym optymalizator, strat oraz wskaźników) są przechowywane w saved_model.pb
. Masy są zapisywane w variables/
katalogu.
W celu uzyskania szczegółowych informacji na temat formatu SavedModel, zobacz przewodnik SavedModel (format SavedModel na dysku) .
Jak SavedModel obsługuje obiekty niestandardowe
Podczas zapisywania modelu i jego warstw, SavedModel formacie Przechowuje nazwę klasy, wywołania funkcji, strat i ciężarów (i konfiguracyjne, jeśli zostaną zrealizowane). Funkcja call definiuje wykres obliczeniowy modelu/warstwy.
W przypadku braku konfiguracji modelu/warstwy funkcja call służy do tworzenia modelu, który istnieje podobnie jak model oryginalny, który można trenować, oceniać i wykorzystywać do wnioskowania.
Niemniej jednak zawsze jest to dobra praktyka, aby zdefiniować get_config
i from_config
metod pisząc niestandardowy model lub klasę warstwy. Pozwala to w razie potrzeby na późniejszą aktualizację obliczeń. Zobacz sekcję o niestandardowych obiektów , aby uzyskać więcej informacji.
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 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>
Pierwszy model jest załadowany ładowane przy użyciu config i CustomModel
klasę. Drugi model jest ładowany przez dynamiczne tworzenie klasy modelu, która działa jak oryginalny model.
Konfiguracja zapisanego modelu
Nowy w TensoFlow 2.4 argument save_traces
został dodany do model.save
, który pozwala na przełączanie SavedModel funkcją śledzenia. Funkcje są zapisywane w celu umożliwienia Keras do obiektów niestandardowych ponowne obciążenie bez oryginalnych definicjami klas, więc kiedy save_traces=False
, wszystkie obiekty niestandardowe musi mieć zdefiniowane get_config
/ from_config
metod. Podczas ładowania, obiekty niestandardowe muszą być przekazane do custom_objects
argument. save_traces=False
zmniejsza ilość miejsca na dysku używanego przez SavedModel i oszczędność czasu.
Format Keras H5
Keras również obsługuje zapisywania pojedynczego pliku hdf5 zawierający architekturę modelu, wartości ciężarów, i compile()
informacji. 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.6322 4/4 [==============================] - 0s 1ms/step - loss: 1.4790 <keras.callbacks.History at 0x7f9aacc0fd50>
Ograniczenia
W porównaniu z formatem SavedModel są dwie rzeczy, które nie są zawarte w pliku H5:
- Straty i metryki zewnętrzne dodane przez
model.add_loss()
&model.add_metric()
nie są zapisywane (w przeciwieństwie SavedModel). Jeśli masz takie straty i metryki w swoim modelu i chcesz wznowić szkolenie, musisz samodzielnie dodać te straty po załadowaniu modelu. Należy pamiętać, że to nie ma zastosowania do strat / metryki wewnątrz warstw utworzonych przezself.add_loss()
&self.add_metric()
. Dopóki warstwa zostanie załadowana, straty te są przechowywane i metryki, ponieważ są one częściącall
metody warstwy. - Wykres obliczanie obiektów niestandardowych, takich jak niestandardowe warstwy nie jest wliczone w zapisanym pliku. W czasie ładowania Keras będzie potrzebował dostępu do klas/funkcji tych obiektów w Pythonie, aby zrekonstruować model. Zobacz obiektów klienta .
Ratowanie architektury
Konfiguracja (lub architektura) modelu określa, jakie warstwy zawiera model i jak te warstwy są połączone*. Jeśli masz konfigurację modelu, model można utworzyć ze świeżo zainicjowanym stanem dla wag i bez informacji o kompilacji.
*Zauważ, że dotyczy to tylko modeli zdefiniowanych przy użyciu funkcjonalnego lub sekwencyjnego interfejsu API niepodklasy.
Konfiguracja modelu sekwencyjnego lub modelu funkcjonalnego API
Tego typu modele są wyraźnymi grafami 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ływanie config = model.get_config()
powróci dict Pythona zawierający konfiguracji modelu. Ten sam wzór może być rekonstruowany przez Sequential.from_config(config)
(dla Sequential
modelu) lub Model.from_config(config)
(na funkcjonalny model API).
Ten sam przepływ pracy działa również dla dowolnej warstwy, którą można serializować.
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
wyjątkiem okazuje model na ciąg JSON, które następnie mogą być załadowane bez oryginalnego 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 podklasy modeli i warstwy są zdefiniowane w metodach __init__
i call
. Są one uważane Pythona kodu bajtowego, które nie mogą być serializowane do JSON-kompatybilnym config - można spróbować szeregowania kodu bajtowego (np poprzez pickle
), ale jest to całkowicie niebezpieczne i oznacza, że model ten nie może być załadowany w innym systemie.
Aby zapisać / wczytać model z własnych zdefiniowanych warstw, lub podklasy modelu, należy nadpisać get_config
i ewentualnie from_config
metod. Dodatkowo należy użyć rejestru niestandardowego obiektu, aby Keras był tego świadomy.
Funkcje niestandardowe
Określonych przez użytkownika (na przykład utraty funkcji aktywacji lub inicjalizacyjne) nie potrzebują get_config
metody. Nazwa funkcji jest wystarczająca do załadowania, o ile jest zarejestrowana jako obiekt niestandardowy.
Ładowanie tylko wykresu TensorFlow
Możliwe jest załadowanie wykresu TensorFlow wygenerowanego przez Keras. Jeśli to zrobisz, nie będziesz musiał dostarczyć ż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()
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
Zauważ, że ta metoda ma kilka wad:
- Ze względu na możliwość śledzenia należy zawsze mieć dostęp do użytych 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ędzie miał dostępu do.predict()
lub.fit()
Nawet jeśli jego użycie jest zalecane, może pomóc, jeśli jesteś w ciasnym miejscu, na przykład, jeśli stracił kod niestandardowych obiektów lub mają problemy z załadowaniem model z tf.keras.models.load_model()
.
Możesz dowiedzieć się więcej na stronie o tf.saved_model.load
Definiowanie metod konfiguracji
Dane techniczne:
-
get_config
powinien powrócić słownik JSON serializacji, aby być zgodna z Keras architecture- i API modelu oszczędzania. -
from_config(config)
(classmethod
) należy zwracać nową warstwę lub modelu przedmiotu, który tworzy się z konfiguracji. Powraca Domyślna implementacjacls(**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 niestandardowego obiektu
Keras odnotowuje, która klasa wygenerowała konfigurację. W powyższym przykładzie, tf.keras.layers.serialize
generuje szeregowany postać warstwy niestandardowy:
{'class_name': 'CustomLayer', 'config': {'a': 2} }
Keras utrzymuje główną listę wszystkich wbudowanych w warstwie modelu optymalizatora i klas metrycznych, który jest używany, aby znaleźć właściwą klasę zadzwonić from_config
. Jeśli klasa nie można znaleźć, wtedy powstaje błąd ( Value Error: Unknown layer
). Istnieje kilka sposobów zarejestrowania klas niestandardowych na tej liście:
- Ustawianie
custom_objects
argument w funkcji obciążenia. (patrz 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 niestandardowej warstwy i funkcji
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żna również zrobić klonowanie w pamięci modelu poprzez 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 wczytywanie tylko wartości wag modelu
Możesz wybrać tylko zapisywanie i wczytywanie ciężarów modelu. Może to być przydatne, jeśli:
- Potrzebujesz tylko modelu do wnioskowania: w tym przypadku nie musisz ponownie uruchamiać uczenia, więc nie potrzebujesz informacji o kompilacji ani stanu optymalizatora.
- Wykonujesz uczenie transferu: w tym przypadku będziesz trenować nowy model ponownie wykorzystując stan poprzedniego modelu, więc nie potrzebujesz informacji o kompilacji poprzedniego modelu.
Interfejsy API do przenoszenia wagi w pamięci
Ciężary mogą być kopiowane pomię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 model wagi do wartości zweights
argumentów.
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 ciężarów z jednego modelu do innego modelu o kompatybilnej architekturze 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ć zgodne 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 wag na dysku i ładowania ich z powrotem
Ciężary mogą być zapisywane na dysku poprzez wywołanie model.save_weights
w następujących formatach:
- Punkt kontrolny przepływu Tensor
- HDF5
Domyślny format model.save_weights
jest TensorFlow checkpoint. Istnieją dwa sposoby określenia formatu zapisu:
-
save_format
argumentem: Ustaw wartośćsave_format="tf"
lubsave_format="h5"
. -
path
Argument: Jeżeli końce Ścieżka z.h5
lub.hdf5
, następnie format HDF5 jest używany. Inne przyrostki spowoduje checkpoint TensorFlow chybasave_format
jest ustawiony.
Istnieje również opcja pobierania wag jako tablic numpy w pamięci. Każdy interfejs API ma swoje zalety i wady, które są szczegółowo opisane poniżej.
Format punktu kontrolnego TF
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 0x7f9aaca4ced0>
Szczegóły formatu
Format TensorFlow Checkpoint zapisuje i przywraca wagi przy użyciu nazw atrybutów obiektów. Na przykład, rozważmy tf.keras.layers.Dense
warstwę. Warstwa zawiera dwie grubości: dense.kernel
i dense.bias
. Gdy warstwa jest zapisany w tf
formacie uzyskany punkt kontrolny zawiera klucze "kernel"
i "bias"
i odpowiadających im wartości masy. Aby uzyskać więcej informacji, zobacz „Ładowanie mechanikę” w instrukcji TF Checkpoint .
Zauważ, że krawędź atrybut / wykres jest nazwany nazwa używana w obiektu nadrzędnego, a nie nazwą zmiennej. Rozważmy CustomLayer
w poniższym przykładzie. Zmienna CustomLayer.var
są zapisywane z "var"
jako część 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 uczenia się
Zasadniczo, dopóki 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 0x7f9aaca76990>
Generalnie zaleca się trzymanie tego samego interfejsu API do budowania modeli. Jeśli przełączasz się między sekwencyjnym i funkcjonalnym lub funkcjonalnym i podklasą itp., zawsze odbuduj wstępnie przeszkolony model i załaduj wstępnie przeszkolone 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
aby zapisać i odtworzyć dokładnie warstw / zmienne.
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.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>
Format HDF5
Format HDF5 zawiera wagi pogrupowane według nazw warstw. Masy są listy zamówionych przez złączenie listę wyszkolić ciężarków do listy ciężarami niż wyszkolić (tak samo jak layer.weights
). W ten sposób model może używać punktu kontrolnego hdf5, jeśli ma te same warstwy i statusy możliwe do trenowania, co 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")
Należy pamiętać, że zmieniając layer.trainable
może skutkować innym layer.weights
zamawiania kiedy model zawiera zagnieżdżonych warstw.
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 uczenia się
Podczas ładowania wstępnie wytrenowanych wag z HDF5 zaleca się załadowanie wag do oryginalnego modelu z punktem kontrolnym, a następnie wyodrębnienie żądanych wag/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 _________________________________________________________________