Dzień Społeczności ML jest 9 listopada! Dołącz do nas na aktualizacje z TensorFlow Jax i więcej Dowiedz się więcej

Wprowadzenie do modułów, warstw i modeli

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 tensorów (forward pass)
  • 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ść implementacji wysokiego szczebla warstw i modeli, takich jak Keras lub Sonnet , są zbudowane na tej samej klasy fundamentalnej: tf.Module .

Oto przykład bardzo prostego tf.Module która funkcjonuje na skalarnym tensora:

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ą uczenia głębokiego dla „obiektów”: mają stan wewnętrzny i metody, które go używają.

Nie ma nic szczególnego w __call__ wyjątkiem działać jak Python wymagalne ; 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.

Przez podklasy tf.Module wszelkie tf.Variable lub tf.Module przypadki przypisane do właściwości tego obiektu są automatycznie pobierane. Pozwala to zaoszczędzić i zmienne obciążenia, a także tworzyć zbiory 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-09-22 20:41:24.398693: 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([[0.         0.05766037]], shape=(1, 2), dtype=float32)

tf.Module instancje automatycznie gromadzić, rekurencyjnie wszelkie tf.Variable lub tf.Module przypadki przypisane do niego. To pozwala na zarządzanie zbiorami tf.Module s przy pojedynczym egzemplarzu modelu, a następnie zapisz i wczytaj całych modeli.

print("Submodules:", my_model.submodules)
Submodules: (<__main__.Dense object at 0x7f68843f0610>, <__main__.Dense object at 0x7f693c112990>)
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([[ 1.3168278 , -0.04209378,  1.4812242 ],
       [-1.8976173 , -1.1280936 , -2.57399   ],
       [ 0.43279243,  1.2495825 ,  0.31452706]], 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.70494235,  0.34362802],
       [-1.318382  ,  0.3631226 ],
       [ 0.870611  ,  0.6044129 ]], 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. Jest to więc w zmienna ma znany kształt i może być przyznane.

Odraczając tworzenie zmiennych do pierwszego wywołania modułu z określonym kształtem wejściowym, nie trzeba 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([[0.       4.937734]], shape=(1, 2), dtype=float32)

Ta elastyczność jest dlaczego warstwy TensorFlow często wystarczy, aby określić kształt swoich wyjść, takich jak tf.keras.layers.Dense , zamiast zarówno wielkości wejściowych i wyjściowych.

Oszczędzanie ciężarów

Można zapisać tf.Module zarówno jako punkt kontrolny i 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ą numerowane (np. '00000-of-00001'). W tym przypadku jednak istnieje 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([[0.      , 4.937734]], dtype=float32)>

Funkcje zapisywania

TensorFlow można uruchomić modele bez oryginalnych obiektów Pythona, o czym świadczy TensorFlow Serving i TensorFlow Lite , nawet po pobraniu przeszkoleni modelu z TensorFlow Hub .

TensorFlow musi wiedzieć, jak wykonać obliczenia opisane w Pythonie, ale bez oryginalnego kodu. Aby to zrobić, można zrobić wykres, który jest opisany w Wprowadzenie do wykresów i funkcji przewodnika .

Wykres ten zawiera czynności lub ops, które implementują funkcję.

Można zdefiniować wykres w modelu powyżej dodając @tf.function dekorator, aby wskazać, że ten kod powinien działać w postaci wykresu.

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. Sprawdź Wprowadzenie do wykresów i funkcje przewodnika o szczegóły.

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.57633626 0.8910464 ]], shape=(1, 2), dtype=float32)
tf.Tensor(
[[[0.57633626 0.8910464 ]
  [0.57633626 0.8910464 ]]], 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.]], shape=(1, 2), dtype=float32)

Uruchom TensorBoard, aby wyświetlić wynikowy ślad:

%tensorboard --logdir logs/func

Zrzut ekranu wykresu w TensorBoard

Tworzenie SavedModel

Zalecanym sposobem dzielenia całkowicie wyszkolonych modeli jest użycie SavedModel . SavedModel zawiera zarówno zbiór funkcji i zbiór ciężarami.

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 Sep 22 20:41 assets
-rw-rw-r-- 1 kbuilder kokoro 14702 Sep 22 20:41 saved_model.pb
drwxr-sr-x 2 kbuilder kokoro  4096 Sep 22 20:41 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 Sep 22 20:41 variables.data-00000-of-00001
-rw-rw-r-- 1 kbuilder kokoro 356 Sep 22 20:41 variables.index

saved_model.pb plik jest protokół bufor opisującą 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 , stworzony od ładowania zapisanego modelu, to wewnętrzny obiekt użytkownika TensorFlow bez wiedzy klasy. To 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.57633626 0.8910464 ]], shape=(1, 2), dtype=float32)
tf.Tensor(
[[[0.57633626 0.8910464 ]
  [0.57633626 0.8910464 ]]], shape=(1, 2, 2), dtype=float32)

Zatem stosując SavedModel , jesteś w stanie zaoszczędzić masę TensorFlow i wykresów 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 budować własną wysokiego poziomu API na szczycie tf.Module , a ludzie mają.

W tej sekcji można zbadać, jak Keras wykorzystuje tf.Module . Pełna instrukcja do modeli Keras można znaleźć w przewodniku Keras .

Warstwy Keras

tf.keras.layers.Layer jest klasa podstawa wszystkich warstw Keras i dziedziczy tf.Module .

Można konwertować moduł do warstwy Keras prostu wymieniając rodzica, a następnie zmieniając __call__ do 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ą własne __call__ który wykonuje pewne księgowości opisaną 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([[4.441887  , 0.00987494, 1.2474661 ]], dtype=float32)>

build krok

Jak wspomniano, w wielu przypadkach wygodnie jest poczekać z utworzeniem zmiennych, dopóki nie będziesz pewien kształtu wejściowego.

Warstwy Keras mają dodatkowy etap cyklu życia, który zapewnia większą elastyczność w definiowaniu warstw. Jest to określone w build funkcji.

build nazywa się dokładnie raz, i to się nazywa w kształcie wejściu. Zwykle służy do tworzenia zmiennych (wag).

Można przepisać MyDense warstwę powyżej, aby być elastyczne, aby wielkość jej wejść:

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(
[[ 0.26693314 -4.5170803  -2.1615696 ]
 [ 0.40039957 -6.7756205  -3.2423544 ]], shape=(2, 3), dtype=float32)
flexible_dense.variables
[<tf.Variable 'flexible_dense/w:0' shape=(3, 3) dtype=float32, numpy=
 array([[ 0.69237417, -1.9560792 , -0.8260334 ],
        [-0.5022879 , -1.314342  ,  0.6498058 ],
        [-0.05661968,  1.0118811 , -0.90455717]], dtype=float32)>,
 <tf.Variable 'flexible_dense/b:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>]

Ponieważ build jest wywoływana tylko raz, wejścia zostaną odrzucone, jeżeli kształt wejściowy nie jest zgodny ze zmiennymi warstwy za:

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
  • Wbudowane wsparcie dla opcjonalnego training argumentu odróżnić szkolenia i wykorzystania wnioskowania
  • get_config i from_config metody, które pozwalają precyzyjnie zapisać konfiguracje modelu, aby umożliwić klonowanie w Pythonie

Przeczytaj o nich w pełnym przewodnikiem do niestandardowych warstw i modeli.

Modele Keras

Możesz zdefiniować swój model jako zagnieżdżone warstwy Keras.

Jednak Keras zapewnia również w pełni funkcjonalny model klasy o nazwie tf.keras.Model . Dziedziczy z tf.keras.layers.Layer , więc model Keras można stosować zagnieżdżone, a zapisywane 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żna zdefiniować SequentialModule z góry z prawie identycznym kodem ponownie przekształcając __call__ do call() i zmiany 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([[13.535655 -1.076561]], 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.45659065,  0.38672394, -1.4717953 ],
        [-0.60538477, -0.7337349 , -1.9595573 ],
        [ 0.56756437, -0.7816317 , -0.43361524]], 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.66438836, -0.6865299 ],
        [-0.5305109 , -0.68015355],
        [-1.6681372 ,  0.2635035 ]], 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 0x7f691bf92d10>,
 <__main__.FlexibleDense at 0x7f691ad189d0>)

Nadrzędnymi tf.keras.Model jest bardzo pythonowy podejście do budowania modeli TensorFlow. Jeśli migrujesz modele z innych frameworków, może to być bardzo proste.

Jeśli konstruowaniu modeli, które są proste zespoły istniejących warstw i wejść, można zaoszczędzić czas i przestrzeń za pomocą API funkcjonalną , która pochodzi z dodatkowymi funkcjami wokół modelu rekonstrukcji i architektury.

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([[ 0.5620958, -0.3502033]], dtype=float32)>

Główną różnicą jest to, że kształt wejściowy jest określony z góry jako część funkcjonalnego procesu konstrukcyjnego. input_shape argumentem w tym przypadku nie musi być całkowicie określona; można pozostawić pewne wymiary jak None .

Zapisywanie modeli Keras

Modele Keras można checkpointed, i że będzie wyglądać tak samo jak tf.Module .

Modele Keras mogą być również zapisywane tf.saved_model.save() , ponieważ są moduły. 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 również zapisać stanów metryczny, strat i Optimizer.

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([[13.535655, -1.076561]], 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. Sprawdź przewodnik oszczędności i serializacji .

Co dalej

Jeśli chcesz dowiedzieć się więcej szczegółów na temat Keras można śledzić istniejące prowadnice Keras tutaj .

Innym przykładem API wysokiego poziomu zbudowany na tf.module jest Sonnet z DeepMind, która jest pokryta na ich stronie .