Zobacz na TensorFlow.org | Uruchom w Google Colab | Wyświetl źródło na GitHub | Pobierz notatnik |
Aby przeprowadzić uczenie maszynowe w TensorFlow, prawdopodobnie będziesz musiał zdefiniować, zapisać i przywrócić model.
Model to abstrakcyjnie:
- Funkcja, która oblicza coś na tensorach (przebieg w przód )
- Niektóre zmienne, które można aktualizować w odpowiedzi na szkolenie
W tym przewodniku zejdziesz pod powierzchnię Keras, aby zobaczyć, jak zdefiniowane są modele TensorFlow. Przygląda się, w jaki sposób TensorFlow zbiera zmienne i modele, a także w jaki sposób są one zapisywane i przywracane.
Ustawiać
import tensorflow as tf
from datetime import datetime
%load_ext tensorboard
Definiowanie modeli i warstw w TensorFlow
Większość modeli składa się z warstw. Warstwy to funkcje o znanej strukturze matematycznej, które można ponownie wykorzystać i które mają zmienne, które można trenować. W TensorFlow większość wysokopoziomowych implementacji warstw i modeli, takich jak Keras czy Sonnet , jest zbudowanych na tej samej podstawowej klasie: tf.Module
.
Oto przykład bardzo prostego tf.Module
, który działa na tensorze skalarnym:
class SimpleModule(tf.Module):
def __init__(self, name=None):
super().__init__(name=name)
self.a_variable = tf.Variable(5.0, name="train_me")
self.non_trainable_variable = tf.Variable(5.0, trainable=False, name="do_not_train_me")
def __call__(self, x):
return self.a_variable * x + self.non_trainable_variable
simple_module = SimpleModule(name="simple")
simple_module(tf.constant(5.0))
<tf.Tensor: shape=(), dtype=float32, numpy=30.0>
Moduły i, co za tym idzie, warstwy są terminologią głębokiego uczenia się dla „obiektów”: mają stan wewnętrzny i metody, które go używają.
Nie ma nic szczególnego w __call__
poza działaniem jak wywoływalny Python ; możesz wywoływać swoje modele z dowolnymi funkcjami.
Możesz włączać i wyłączać możliwość trenowania zmiennych z dowolnego powodu, w tym warstw zamrażania i zmiennych podczas dostrajania.
Dzięki podklasy tf.Module
, wszystkie instancje tf.Variable
lub tf.Module
przypisane do właściwości tego obiektu są automatycznie zbierane. Pozwala to zapisywać i ładować zmienne, a także tworzyć kolekcje tf.Module
s.
# All trainable variables
print("trainable variables:", simple_module.trainable_variables)
# Every variable
print("all variables:", simple_module.variables)
trainable variables: (<tf.Variable 'train_me:0' shape=() dtype=float32, numpy=5.0>,) all variables: (<tf.Variable 'train_me:0' shape=() dtype=float32, numpy=5.0>, <tf.Variable 'do_not_train_me:0' shape=() dtype=float32, numpy=5.0>) 2021-10-26 01:29:45.284549: 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.
Jest to przykład dwuwarstwowego liniowego modelu warstwowego wykonanego z modułów.
Najpierw gęsta (liniowa) warstwa:
class Dense(tf.Module):
def __init__(self, in_features, out_features, name=None):
super().__init__(name=name)
self.w = tf.Variable(
tf.random.normal([in_features, out_features]), name='w')
self.b = tf.Variable(tf.zeros([out_features]), name='b')
def __call__(self, x):
y = tf.matmul(x, self.w) + self.b
return tf.nn.relu(y)
A potem kompletny model, który tworzy dwie instancje warstw i je stosuje:
class SequentialModule(tf.Module):
def __init__(self, name=None):
super().__init__(name=name)
self.dense_1 = Dense(in_features=3, out_features=3)
self.dense_2 = Dense(in_features=3, out_features=2)
def __call__(self, x):
x = self.dense_1(x)
return self.dense_2(x)
# You have made a model!
my_model = SequentialModule(name="the_model")
# Call it, with random results
print("Model results:", my_model(tf.constant([[2.0, 2.0, 2.0]])))
Model results: tf.Tensor([[7.706234 3.0919805]], shape=(1, 2), dtype=float32)
Instancje tf.Module
automatycznie, rekurencyjnie, będą gromadzić wszystkie przypisane do nich instancje tf.Variable
lub tf.Module
. Pozwala to na zarządzanie kolekcjami tf.Module
s za pomocą pojedynczej instancji modelu oraz zapisywanie i ładowanie całych modeli.
print("Submodules:", my_model.submodules)
Submodules: (<__main__.Dense object at 0x7f7ab2391290>, <__main__.Dense object at 0x7f7b6869ea10>)
for var in my_model.variables:
print(var, "\n")
<tf.Variable 'b:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)> <tf.Variable 'w:0' shape=(3, 3) dtype=float32, numpy= array([[ 0.05711935, 0.22440144, 0.6370985 ], [ 0.3136791 , -1.7006774 , 0.7256515 ], [ 0.16120772, -0.8412193 , 0.5250952 ]], dtype=float32)> <tf.Variable 'b:0' shape=(2,) dtype=float32, numpy=array([0., 0.], dtype=float32)> <tf.Variable 'w:0' shape=(3, 2) dtype=float32, numpy= array([[-0.5353216 , 1.2815404 ], [ 0.62764466, 0.47087234], [ 2.19187 , 0.45777202]], dtype=float32)>
Czekam na utworzenie zmiennych
Być może zauważyłeś tutaj, że musisz zdefiniować zarówno rozmiar wejściowy, jak i wyjściowy dla warstwy. Dzieje się tak dlatego, że zmienna w
ma znany kształt i może być przydzielona.
Odraczając tworzenie zmiennych do pierwszego wywołania modułu z określonym kształtem wejściowym, nie musisz z góry określać rozmiaru wejściowego.
class FlexibleDenseModule(tf.Module):
# Note: No need for `in_features`
def __init__(self, out_features, name=None):
super().__init__(name=name)
self.is_built = False
self.out_features = out_features
def __call__(self, x):
# Create variables on first call.
if not self.is_built:
self.w = tf.Variable(
tf.random.normal([x.shape[-1], self.out_features]), name='w')
self.b = tf.Variable(tf.zeros([self.out_features]), name='b')
self.is_built = True
y = tf.matmul(x, self.w) + self.b
return tf.nn.relu(y)
# Used in a module
class MySequentialModule(tf.Module):
def __init__(self, name=None):
super().__init__(name=name)
self.dense_1 = FlexibleDenseModule(out_features=3)
self.dense_2 = FlexibleDenseModule(out_features=2)
def __call__(self, x):
x = self.dense_1(x)
return self.dense_2(x)
my_model = MySequentialModule(name="the_model")
print("Model results:", my_model(tf.constant([[2.0, 2.0, 2.0]])))
Model results: tf.Tensor([[4.0598335 0. ]], shape=(1, 2), dtype=float32)
Ta elastyczność powoduje, że warstwy TensorFlow często muszą jedynie określić kształt swoich danych wyjściowych, takich jak tf.keras.layers.Dense
, a nie zarówno rozmiar wejściowy, jak i wyjściowy.
Oszczędzanie ciężarów
Możesz zapisać tf.Module
zarówno jako punkt kontrolny , jak i jako SavedModel .
Punkty kontrolne to tylko wagi (czyli wartości zbioru zmiennych wewnątrz modułu i jego podmodułów):
chkp_path = "my_checkpoint"
checkpoint = tf.train.Checkpoint(model=my_model)
checkpoint.write(chkp_path)
'my_checkpoint'
Punkty kontrolne składają się z dwóch rodzajów plików: samych danych i pliku indeksu metadanych. Plik indeksu zawiera informacje o tym, co jest faktycznie zapisywane i numerację punktów kontrolnych, podczas gdy dane punktów kontrolnych zawierają wartości zmiennych i ich ścieżki wyszukiwania atrybutów.
ls my_checkpoint*
my_checkpoint.data-00000-of-00001 my_checkpoint.index
Możesz zajrzeć do punktu kontrolnego, aby upewnić się, że cała kolekcja zmiennych została zapisana, posortowana według obiektu Pythona, który je zawiera.
tf.train.list_variables(chkp_path)
[('_CHECKPOINTABLE_OBJECT_GRAPH', []), ('model/dense_1/b/.ATTRIBUTES/VARIABLE_VALUE', [3]), ('model/dense_1/w/.ATTRIBUTES/VARIABLE_VALUE', [3, 3]), ('model/dense_2/b/.ATTRIBUTES/VARIABLE_VALUE', [2]), ('model/dense_2/w/.ATTRIBUTES/VARIABLE_VALUE', [3, 2])]
Podczas szkolenia rozproszonego (wieloma maszynowego) można je shardować, dlatego są ponumerowane (np. '00000-of-00001'). Jednak w tym przypadku jest tylko jeden odłamek.
Kiedy ponownie ładujesz modele, nadpisujesz wartości w swoim obiekcie Pythona.
new_model = MySequentialModule()
new_checkpoint = tf.train.Checkpoint(model=new_model)
new_checkpoint.restore("my_checkpoint")
# Should be the same result as above
new_model(tf.constant([[2.0, 2.0, 2.0]]))
<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[4.0598335, 0. ]], dtype=float32)>
Funkcje zapisywania
TensorFlow może uruchamiać modele bez oryginalnych obiektów Pythona, jak pokazują TensorFlow Serving i TensorFlow Lite , nawet po pobraniu wytrenowanego modelu z TensorFlow Hub .
TensorFlow musi wiedzieć, jak wykonać obliczenia opisane w Pythonie, ale bez oryginalnego kodu . Aby to zrobić, możesz utworzyć wykres , który jest opisany w przewodniku Wprowadzenie do wykresów i funkcji .
Ten wykres zawiera operacje lub ops , które implementują funkcję.
Możesz zdefiniować wykres w powyższym modelu, dodając dekorator @tf.function
, aby wskazać, że ten kod powinien działać jako wykres.
class MySequentialModule(tf.Module):
def __init__(self, name=None):
super().__init__(name=name)
self.dense_1 = Dense(in_features=3, out_features=3)
self.dense_2 = Dense(in_features=3, out_features=2)
@tf.function
def __call__(self, x):
x = self.dense_1(x)
return self.dense_2(x)
# You have made a model with a graph!
my_model = MySequentialModule(name="the_model")
Stworzony przez Ciebie moduł działa dokładnie tak samo jak poprzednio. Każda unikatowa sygnatura przekazana do funkcji tworzy osobny wykres. Zapoznaj się z przewodnikiem Wprowadzenie do wykresów i funkcji, aby uzyskać szczegółowe informacje.
print(my_model([[2.0, 2.0, 2.0]]))
print(my_model([[[2.0, 2.0, 2.0], [2.0, 2.0, 2.0]]]))
tf.Tensor([[0.62891716 0. ]], shape=(1, 2), dtype=float32) tf.Tensor( [[[0.62891716 0. ] [0.62891716 0. ]]], shape=(1, 2, 2), dtype=float32)
Możesz zwizualizować wykres, śledząc go w podsumowaniu TensorBoard.
# Set up logging.
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
logdir = "logs/func/%s" % stamp
writer = tf.summary.create_file_writer(logdir)
# Create a new model to get a fresh trace
# Otherwise the summary will not see the graph.
new_model = MySequentialModule()
# Bracket the function call with
# tf.summary.trace_on() and tf.summary.trace_export().
tf.summary.trace_on(graph=True)
tf.profiler.experimental.start(logdir)
# Call only one tf.function when tracing.
z = print(new_model(tf.constant([[2.0, 2.0, 2.0]])))
with writer.as_default():
tf.summary.trace_export(
name="my_func_trace",
step=0,
profiler_outdir=logdir)
tf.Tensor([[0. 0.01750386]], shape=(1, 2), dtype=float32)
Uruchom TensorBoard, aby wyświetlić wynikowy ślad:
#docs_infra: no_execute
%tensorboard --logdir logs/func
Tworzenie SavedModel
Zalecanym sposobem udostępniania całkowicie wytrenowanych modeli jest użycie SavedModel
. SavedModel
zawiera zarówno zbiór funkcji, jak i zbiór wag.
Możesz zapisać wytrenowany model w następujący sposób:
tf.saved_model.save(my_model, "the_saved_model")
INFO:tensorflow:Assets written to: the_saved_model/assets
# Inspect the SavedModel in the directory
ls -l the_saved_model
total 24 drwxr-sr-x 2 kbuilder kokoro 4096 Oct 26 01:29 assets -rw-rw-r-- 1 kbuilder kokoro 14702 Oct 26 01:29 saved_model.pb drwxr-sr-x 2 kbuilder kokoro 4096 Oct 26 01:29 variables
# The variables/ directory contains a checkpoint of the variables
ls -l the_saved_model/variables
total 8 -rw-rw-r-- 1 kbuilder kokoro 408 Oct 26 01:29 variables.data-00000-of-00001 -rw-rw-r-- 1 kbuilder kokoro 356 Oct 26 01:29 variables.index
Plik saved_model.pb
jest buforem protokołu opisującym funkcjonalny tf.Graph
.
Modele i warstwy mogą być ładowane z tej reprezentacji bez faktycznego tworzenia instancji klasy, która ją utworzyła. Jest to pożądane w sytuacjach, w których nie masz (lub nie chcesz) interpretera Pythona, na przykład obsługujących na dużą skalę lub na urządzeniu brzegowym, lub w sytuacjach, gdy oryginalny kod Pythona nie jest dostępny lub praktyczny w użyciu.
Możesz wczytać model jako nowy obiekt:
new_model = tf.saved_model.load("the_saved_model")
new_model
, utworzony z załadowania zapisanego modelu, jest wewnętrznym obiektem użytkownika TensorFlow bez jakiejkolwiek wiedzy o klasie. Nie jest typu SequentialModule
.
isinstance(new_model, SequentialModule)
False
Ten nowy model działa na już zdefiniowanych sygnaturach wejściowych. Nie możesz dodać więcej podpisów do modelu przywróconego w ten sposób.
print(my_model([[2.0, 2.0, 2.0]]))
print(my_model([[[2.0, 2.0, 2.0], [2.0, 2.0, 2.0]]]))
tf.Tensor([[0.62891716 0. ]], shape=(1, 2), dtype=float32) tf.Tensor( [[[0.62891716 0. ] [0.62891716 0. ]]], shape=(1, 2, 2), dtype=float32)
Tak więc, używając SavedModel
, możesz zapisać wagi i wykresy TensorFlow za pomocą tf.Module
, a następnie załadować je ponownie.
Modele i warstwy Keras
Zauważ, że do tego momentu nie ma wzmianki o Kerasie. Możesz zbudować własne wysokopoziomowe API na bazie tf.Module
, a ludzie mają.
W tej sekcji zbadasz, w jaki sposób Keras używa tf.Module
. Kompletną instrukcję obsługi modeli Keras można znaleźć w przewodniku Keras .
Warstwy Keras
tf.keras.layers.Layer
jest klasą bazową wszystkich warstw Keras i dziedziczy po tf.Module
.
Możesz przekonwertować moduł na warstwę Keras, po prostu zamieniając rodzica, a następnie zmieniając __call__
na call
:
class MyDense(tf.keras.layers.Layer):
# Adding **kwargs to support base Keras layer arguments
def __init__(self, in_features, out_features, **kwargs):
super().__init__(**kwargs)
# This will soon move to the build step; see below
self.w = tf.Variable(
tf.random.normal([in_features, out_features]), name='w')
self.b = tf.Variable(tf.zeros([out_features]), name='b')
def call(self, x):
y = tf.matmul(x, self.w) + self.b
return tf.nn.relu(y)
simple_layer = MyDense(name="simple", in_features=3, out_features=3)
Warstwy Keras mają swoje własne __call__
, które wykonuje pewne czynności księgowe opisane w następnej sekcji, a następnie wywołuje call()
. Nie powinieneś zauważyć żadnych zmian w funkcjonalności.
simple_layer([[2.0, 2.0, 2.0]])
<tf.Tensor: shape=(1, 3), dtype=float32, numpy=array([[0. , 0.179402, 0. ]], dtype=float32)>
Krok build
Jak już wspomniano, w wielu przypadkach wygodnie jest poczekać z utworzeniem zmiennych, dopóki nie upewnisz się co do kształtu wejściowego.
Warstwy Keras mają dodatkowy etap cyklu życia, który zapewnia większą elastyczność w definiowaniu warstw. Jest to zdefiniowane w funkcji build
.
build
jest wywoływany dokładnie raz i jest wywoływany z kształtem danych wejściowych. Zwykle służy do tworzenia zmiennych (wag).
Możesz przepisać warstwę MyDense
powyżej, aby była elastyczna do rozmiaru jej danych wejściowych:
class FlexibleDense(tf.keras.layers.Layer):
# Note the added `**kwargs`, as Keras supports many arguments
def __init__(self, out_features, **kwargs):
super().__init__(**kwargs)
self.out_features = out_features
def build(self, input_shape): # Create the state of the layer (weights)
self.w = tf.Variable(
tf.random.normal([input_shape[-1], self.out_features]), name='w')
self.b = tf.Variable(tf.zeros([self.out_features]), name='b')
def call(self, inputs): # Defines the computation from inputs to outputs
return tf.matmul(inputs, self.w) + self.b
# Create the instance of the layer
flexible_dense = FlexibleDense(out_features=3)
W tym momencie model nie został zbudowany, więc nie ma zmiennych:
flexible_dense.variables
[]
Wywołanie funkcji przydziela zmienne o odpowiedniej wielkości:
# Call it, with predictably random results
print("Model results:", flexible_dense(tf.constant([[2.0, 2.0, 2.0], [3.0, 3.0, 3.0]])))
Model results: tf.Tensor( [[-1.6998017 1.6444504 -1.3103955] [-2.5497022 2.4666753 -1.9655929]], shape=(2, 3), dtype=float32)
flexible_dense.variables
[<tf.Variable 'flexible_dense/w:0' shape=(3, 3) dtype=float32, numpy= array([[ 1.277462 , 0.5399406 , -0.301957 ], [-1.6277349 , 0.7374014 , -1.7651852 ], [-0.49962795, -0.45511687, 1.4119445 ]], dtype=float32)>, <tf.Variable 'flexible_dense/b:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>]
Ponieważ build
jest wywoływany tylko raz, dane wejściowe zostaną odrzucone, jeśli kształt wejściowy nie jest zgodny ze zmiennymi warstwy:
try:
print("Model results:", flexible_dense(tf.constant([[2.0, 2.0, 2.0, 2.0]])))
except tf.errors.InvalidArgumentError as e:
print("Failed:", e)
Failed: In[0] mismatch In[1] shape: 4 vs. 3: [1,4] [3,3] 0 0 [Op:MatMul]
Warstwy Keras mają o wiele więcej dodatkowych funkcji, w tym:
- Straty opcjonalne
- Wsparcie dla metryk
- Wbudowana obsługa opcjonalnego argumentu
training
w celu rozróżnienia pomiędzy treningiem a wykorzystaniem wnioskowania - metody
get_config
ifrom_config
, które pozwalają dokładnie przechowywać konfiguracje, aby umożliwić klonowanie modeli w Pythonie
Przeczytaj o nich w pełnym przewodniku po niestandardowych warstwach i modelach.
Modele Keras
Możesz zdefiniować swój model jako zagnieżdżone warstwy Keras.
Jednak Keras udostępnia również w pełni funkcjonalną klasę modelu o nazwie tf.keras.Model
. Dziedziczy z tf.keras.layers.Layer
, więc model Keras może być używany, zagnieżdżany i zapisany w taki sam sposób jak warstwy Keras. Modele Keras są wyposażone w dodatkowe funkcje, dzięki którym można je łatwo trenować, oceniać, ładować, zapisywać, a nawet trenować na wielu maszynach.
Możesz zdefiniować SequentialModule
od góry za pomocą prawie identycznego kodu, ponownie konwertując __call__
na call()
i zmieniając rodzica:
class MySequentialModel(tf.keras.Model):
def __init__(self, name=None, **kwargs):
super().__init__(**kwargs)
self.dense_1 = FlexibleDense(out_features=3)
self.dense_2 = FlexibleDense(out_features=2)
def call(self, x):
x = self.dense_1(x)
return self.dense_2(x)
# You have made a Keras model!
my_sequential_model = MySequentialModel(name="the_model")
# Call it on a tensor, with random results
print("Model results:", my_sequential_model(tf.constant([[2.0, 2.0, 2.0]])))
Model results: tf.Tensor([[5.5604653 3.3511646]], shape=(1, 2), dtype=float32)
Dostępne są wszystkie te same funkcje, w tym zmienne śledzenia i podmoduły.
my_sequential_model.variables
[<tf.Variable 'my_sequential_model/flexible_dense_1/w:0' shape=(3, 3) dtype=float32, numpy= array([[ 0.05627853, -0.9386015 , -0.77410126], [ 0.63149 , 1.0802224 , -0.37785745], [-0.24788402, -1.1076807 , -0.5956209 ]], dtype=float32)>, <tf.Variable 'my_sequential_model/flexible_dense_1/b:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>, <tf.Variable 'my_sequential_model/flexible_dense_2/w:0' shape=(3, 2) dtype=float32, numpy= array([[-0.93912166, 0.77979285], [ 1.4049559 , -1.9380962 ], [-2.6039495 , 0.30885765]], dtype=float32)>, <tf.Variable 'my_sequential_model/flexible_dense_2/b:0' shape=(2,) dtype=float32, numpy=array([0., 0.], dtype=float32)>]
my_sequential_model.submodules
(<__main__.FlexibleDense at 0x7f7b48525550>, <__main__.FlexibleDense at 0x7f7b48508d10>)
Zastępowanie tf.keras.Model
to bardzo pytoniczne podejście do budowania modeli TensorFlow. Jeśli migrujesz modele z innych frameworków, może to być bardzo proste.
Jeśli konstruujesz modele, które są prostymi zestawami istniejących warstw i danych wejściowych, możesz zaoszczędzić czas i miejsce, korzystając z funkcjonalnego interfejsu API , który zawiera dodatkowe funkcje związane z rekonstrukcją i architekturą modelu.
Oto ten sam model z funkcjonalnym API:
inputs = tf.keras.Input(shape=[3,])
x = FlexibleDense(3)(inputs)
x = FlexibleDense(2)(x)
my_functional_model = tf.keras.Model(inputs=inputs, outputs=x)
my_functional_model.summary()
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 3)] 0 _________________________________________________________________ flexible_dense_3 (FlexibleDe (None, 3) 12 _________________________________________________________________ flexible_dense_4 (FlexibleDe (None, 2) 8 ================================================================= Total params: 20 Trainable params: 20 Non-trainable params: 0 _________________________________________________________________
my_functional_model(tf.constant([[2.0, 2.0, 2.0]]))
<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[8.219393, 4.511119]], dtype=float32)>
Główną różnicą jest to, że kształt wejściowy jest określony z góry jako część funkcjonalnego procesu konstrukcyjnego. Argument input_shape
w tym przypadku nie musi być całkowicie określony; możesz zostawić niektóre wymiary jako None
.
Zapisywanie modeli Keras
Modele Keras mogą mieć punkty kontrolne i będą wyglądać tak samo jak tf.Module
.
Modele Keras można również zapisać za pomocą tf.saved_model.save()
, ponieważ są one modułami. Jednak modele Keras mają wygodne metody i inne funkcje:
my_sequential_model.save("exname_of_file")
INFO:tensorflow:Assets written to: exname_of_file/assets
Równie łatwo można je ponownie załadować:
reconstructed_model = tf.keras.models.load_model("exname_of_file")
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Keras SavedModels
zapisuje również stany metryk, strat i optymalizatorów.
Ten zrekonstruowany model może być użyty i da ten sam wynik, gdy zostanie wywołany na tych samych danych:
reconstructed_model(tf.constant([[2.0, 2.0, 2.0]]))
<tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[5.5604653, 3.3511646]], dtype=float32)>
Jest więcej informacji na temat zapisywania i serializacji modeli Keras, w tym dostarczania metod konfiguracji dla niestandardowych warstw w celu obsługi funkcji. Zapoznaj się z przewodnikiem dotyczącym zapisywania i serializacji .
Co dalej
Jeśli chcesz dowiedzieć się więcej o Keras, możesz zapoznać się z istniejącymi przewodnikami Keras tutaj .
Innym przykładem wysokopoziomowego API zbudowanego na tf.module
jest Sonnet firmy DeepMind, który jest omówiony na ich stronie .