Używaj modeli TF1.x w przepływach pracy TF2

Zobacz na TensorFlow.org Uruchom w Google Colab Zobacz na GitHub Pobierz notatnik

Ten przewodnik zawiera omówienie i przykłady podkładek kodu modelowania , których można użyć do korzystania z istniejących modeli TF1.x w przepływach pracy TF2, takich jak szybkie wykonywanie, tf.function i strategie dystrybucji przy minimalnych zmianach w kodzie modelowania.

Zakres użytkowania

Podkładka opisana w tym przewodniku jest przeznaczona dla modeli TF1.x, które opierają się na:

  1. tf.compat.v1.get_variable i tf.compat.v1.variable_scope do sterowania tworzeniem i ponownym użyciem zmiennych oraz
  2. Interfejsy API oparte na kolekcji wykresów, takie jak tf.compat.v1.global_variables() , tf.compat.v1.trainable_variables , tf.compat.v1.losses.get_regularization_losses() i tf.compat.v1.get_collection() do śledzenia wag i ubytków regularyzacyjnych

Obejmuje to większość modeli zbudowanych na interfejsach API tf.compat.v1.layer , tf.contrib.layers i TensorFlow-Slim .

Podkładka NIE jest potrzebna w następujących modelach TF1.x:

  1. Samodzielne modele Keras, które już śledzą wszystkie możliwe do trenowania wagi i straty w regularyzacji odpowiednio za pomocą model.trainable_weights i model.losses .
  2. tf.Module s, które już śledzą wszystkie wagi, które można wyszkolić za pomocą module.trainable_variables , i tworzą wagi tylko wtedy, gdy nie zostały jeszcze utworzone.

Modele te prawdopodobnie będą działać w TF2 z gorliwym wykonaniem i gotowym do tf.function .

Ustawiać

Importuj TensorFlow i inne zależności.

pip uninstall -y -q tensorflow
# Install tf-nightly as the DeterministicRandomTestTool is available only in
# Tensorflow 2.8

pip install -q tf-nightly
import tensorflow as tf
import tensorflow.compat.v1 as v1
import sys
import numpy as np

from contextlib import contextmanager

Dekorator track_tf1_style_variables

Kluczową podkładką opisaną w tym przewodniku jest tf.compat.v1.keras.utils.track_tf1_style_variables , dekorator, którego można używać w ramach metod należących do tf.keras.layers.Layer i tf.Module do śledzenia wag w stylu TF1.x i wychwytywanie strat związanych z regularyzacją.

Udekorowanie metod wywołania tf.keras.layers.Layer lub tf.Module za pomocą tf.compat.v1.keras.utils.track_tf1_style_variables umożliwia tworzenie zmiennych i ich ponowne użycie za pośrednictwem tf.compat.v1.get_variable (i przez rozszerzenie tf.compat.v1.layers ), aby działał poprawnie wewnątrz zdobionej metody, zamiast zawsze tworzyć nową zmienną przy każdym wywołaniu. Spowoduje to również, że warstwa lub moduł będzie domyślnie śledzić wszelkie wagi utworzone lub dostępne za pośrednictwem get_variable wewnątrz zdobionej metody.

Oprócz śledzenia samych wag w ramach standardowej layer.variable / module.variable /etc. właściwości, jeśli metoda należy do tf.keras.layers.Layer , wówczas wszelkie straty regularyzacji określone za pomocą argumentów regularyzatora get_variable lub tf.compat.v1.layers będą śledzone przez warstwę pod standardową właściwością layer.losses .

Ten mechanizm śledzenia umożliwia używanie dużych klas kodu przekazywania modelu w stylu TF1.x wewnątrz warstw Keras lub tf.Module s w TF2, nawet przy włączonych zachowaniach TF2.

Przykłady użycia

Poniższe przykłady użycia demonstrują podkładki modelujące używane do dekorowania metod tf.keras.layers.Layer , ale z wyjątkiem sytuacji, gdy wchodzą one w interakcję z funkcjami Keras, mają one również zastosowanie podczas dekorowania metod tf.Module .

Warstwa zbudowana za pomocą tf.compat.v1.get_variable

Wyobraź sobie, że masz warstwę zaimplementowaną bezpośrednio nad tf.compat.v1.get_variable w następujący sposób:

def dense(self, inputs, units):
  out = inputs
  with tf.compat.v1.variable_scope("dense"):
    # The weights are created with a `regularizer`,
    kernel = tf.compat.v1.get_variable(
        shape=[out.shape[-1], units],
        regularizer=tf.keras.regularizers.L2(),
        initializer=tf.compat.v1.initializers.glorot_normal,
        name="kernel")
    bias = tf.compat.v1.get_variable(
        shape=[units,],
        initializer=tf.compat.v1.initializers.zeros,
        name="bias")
    out = tf.linalg.matmul(out, kernel)
    out = tf.compat.v1.nn.bias_add(out, bias)
  return out

Użyj podkładki, aby przekształcić ją w warstwę i wywołać na wejściach.

class DenseLayer(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    out = inputs
    with tf.compat.v1.variable_scope("dense"):
      # The weights are created with a `regularizer`,
      # so the layer should track their regularization losses
      kernel = tf.compat.v1.get_variable(
          shape=[out.shape[-1], self.units],
          regularizer=tf.keras.regularizers.L2(),
          initializer=tf.compat.v1.initializers.glorot_normal,
          name="kernel")
      bias = tf.compat.v1.get_variable(
          shape=[self.units,],
          initializer=tf.compat.v1.initializers.zeros,
          name="bias")
      out = tf.linalg.matmul(out, kernel)
      out = tf.compat.v1.nn.bias_add(out, bias)
    return out

layer = DenseLayer(10)
x = tf.random.normal(shape=(8, 20))
layer(x)
WARNING:tensorflow:From /tmp/ipykernel_27038/795621215.py:7: The name tf.keras.utils.track_tf1_style_variables is deprecated. Please use tf.compat.v1.keras.utils.track_tf1_style_variables instead.
<tf.Tensor: shape=(8, 10), dtype=float32, numpy=
array([[-0.51018804, -0.58145535,  0.25050664, -0.09880018,  0.71741414,
        -0.08512568,  0.33404148,  0.50894034,  0.19362557,  0.03945067],
       [-0.66160053,  0.43442816, -0.6187523 ,  0.00753711,  1.3946855 ,
         0.22528797,  0.55661404, -1.6155301 ,  1.5854199 , -0.4165327 ],
       [ 0.15855707,  0.43848652,  0.04762229,  0.22020248,  0.88300526,
         0.31525093, -0.10912375,  0.03332198,  1.3462385 , -0.37986106],
       [ 0.02546233, -0.01084138,  0.0417656 ,  1.1082407 ,  0.926408  ,
         0.46938205,  1.0183189 ,  1.2039868 , -0.09619217, -0.50863194],
       [-1.6222394 ,  0.17156005, -0.07482994,  0.646423  ,  1.0284312 ,
         2.3619173 ,  0.6322627 ,  0.5350776 , -2.2700598 , -0.8211552 ],
       [-1.1044651 ,  0.7303245 ,  1.0183476 ,  1.2858934 ,  0.4575533 ,
         0.93400717,  0.5323913 , -0.01242167,  0.8308919 ,  0.03202473],
       [ 0.3880633 , -1.2345276 ,  0.7713047 , -0.33720714,  1.0418141 ,
        -1.055242  , -1.6942265 ,  1.705035  ,  0.8671215 ,  0.8162696 ],
       [ 0.02216246, -0.5235669 ,  0.01065174, -1.1682817 ,  0.44079733,
         0.25890222, -1.0779501 ,  0.37716752, -0.27636313, -0.6359312 ]],
      dtype=float32)>

Uzyskaj dostęp do śledzonych zmiennych i przechwyconych strat regularyzacji jak do standardowej warstwy Keras.

layer.trainable_variables
layer.losses
2021-12-04 02:24:42.941890: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
[<tf.Tensor: shape=(), dtype=float32, numpy=0.10789324>]

Aby zobaczyć, że wagi są ponownie używane za każdym razem, gdy wywołujesz warstwę, ustaw wszystkie wagi na zero i ponownie wywołaj warstwę.

print("Resetting variables to zero:", [var.name for var in layer.trainable_variables])

for var in layer.trainable_variables:
  var.assign(var * 0.0)

# Note: layer.losses is not a live view and
# will get reset only at each layer call
print("layer.losses:", layer.losses)
print("calling layer again.")
out = layer(x)
print("layer.losses: ", layer.losses)
out
Resetting variables to zero: ['dense/bias:0', 'dense/kernel:0']
layer.losses: [<tf.Tensor: shape=(), dtype=float32, numpy=0.0>]
calling layer again.
layer.losses:  [<tf.Tensor: shape=(), dtype=float32, numpy=0.0>]
<tf.Tensor: shape=(8, 10), dtype=float32, numpy=
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>

Przekonwertowanej warstwy można użyć również bezpośrednio w konstrukcji modelu funkcjonalnego Keras.

inputs = tf.keras.Input(shape=(20))
outputs = DenseLayer(10)(inputs)
model = tf.keras.Model(inputs=inputs, outputs=outputs)

x = tf.random.normal(shape=(8, 20))
model(x)

# Access the model variables and regularization losses
model.weights
model.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.1345337>]

Model zbudowany za pomocą tf.compat.v1.layers

Wyobraź sobie, że masz warstwę lub model zaimplementowany bezpośrednio na tf.compat.v1.layers w następujący sposób:

def model(self, inputs, units):
  with tf.compat.v1.variable_scope('model'):
    out = tf.compat.v1.layers.conv2d(
        inputs, 3, 3,
        kernel_regularizer="l2")
    out = tf.compat.v1.layers.flatten(out)
    out = tf.compat.v1.layers.dense(
        out, units,
        kernel_regularizer="l2")
    return out

Użyj podkładki, aby przekształcić ją w warstwę i wywołać na wejściach.

class CompatV1LayerModel(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    with tf.compat.v1.variable_scope('model'):
      out = tf.compat.v1.layers.conv2d(
          inputs, 3, 3,
          kernel_regularizer="l2")
      out = tf.compat.v1.layers.flatten(out)
      out = tf.compat.v1.layers.dense(
          out, self.units,
          kernel_regularizer="l2")
      return out

layer = CompatV1LayerModel(10)
x = tf.random.normal(shape=(8, 5, 5, 5))
layer(x)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:12: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  if sys.path[0] == '':
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/convolutional.py:575: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:13: UserWarning: `tf.layers.flatten` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Flatten` instead.
  del sys.path[0]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/core.py:541: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:16: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  app.launch_new_instance()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/core.py:261: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs)
<tf.Tensor: shape=(8, 10), dtype=float32, numpy=
array([[ 2.4439096 , -0.2912227 ,  1.5531251 ,  1.284059  ,  0.10077369,
        -0.4231838 ,  1.0458903 , -0.01530766,  0.07358164, -0.6108157 ],
       [-0.4576063 ,  0.34942552,  2.3044965 ,  1.1483003 , -1.2211238 ,
         0.5634397 ,  0.73821646, -0.07581732,  0.5747937 , -0.66470885],
       [-2.2948585 , -2.709268  ,  1.7494816 , -0.9808065 , -2.9099958 ,
         0.5067346 , -1.011502  ,  2.559535  , -3.0888772 ,  0.3522656 ],
       [ 1.7788265 ,  0.8846102 ,  0.45562026,  0.01498583, -0.12482446,
        -0.32868862, -0.7743829 ,  2.3106992 , -0.0997327 , -0.7715093 ],
       [ 0.40295708,  0.04771695, -0.21336336, -0.13069987,  2.279875  ,
         2.7284563 ,  0.6444641 , -1.1919906 ,  0.96321577,  1.0182515 ],
       [ 0.47900966,  0.04906505,  1.1335449 ,  0.2907704 ,  0.7732022 ,
         0.68217   ,  0.51932573, -0.45156685,  2.081223  ,  1.068861  ],
       [ 0.10084352,  1.6456002 ,  0.63820475,  1.5959243 ,  0.22463399,
         0.07713126,  0.7467398 , -1.5435244 ,  1.2494736 , -0.07683721],
       [ 2.1396816 ,  1.5613532 , -1.1726325 , -0.88917583,  1.6447946 ,
        -1.0071977 , -1.8496083 ,  1.1887017 ,  2.1971662 ,  2.1175954 ]],
      dtype=float32)>

Uzyskaj dostęp do śledzonych zmiennych i uchwyconych strat regularyzacji, jak w przypadku standardowej warstwy Keras.

layer.trainable_variables
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.03623246>,
 <tf.Tensor: shape=(), dtype=float32, numpy=0.14618248>]

Aby zobaczyć, że wagi są ponownie używane za każdym razem, gdy wywołujesz warstwę, ustaw wszystkie wagi na zero i ponownie wywołaj warstwę.

print("Resetting variables to zero:", [var.name for var in layer.trainable_variables])

for var in layer.trainable_variables:
  var.assign(var * 0.0)

out = layer(x)
print("layer.losses: ", layer.losses)
out
Resetting variables to zero: ['model/conv2d/bias:0', 'model/conv2d/kernel:0', 'model/dense/bias:0', 'model/dense/kernel:0']
layer.losses:  [<tf.Tensor: shape=(), dtype=float32, numpy=0.0>, <tf.Tensor: shape=(), dtype=float32, numpy=0.0>]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:12: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  if sys.path[0] == '':
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:13: UserWarning: `tf.layers.flatten` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Flatten` instead.
  del sys.path[0]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:16: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  app.launch_new_instance()
<tf.Tensor: shape=(8, 10), dtype=float32, numpy=
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>

Przekonwertowanej warstwy można użyć również bezpośrednio w konstrukcji modelu funkcjonalnego Keras.

inputs = tf.keras.Input(shape=(5, 5, 5))
outputs = CompatV1LayerModel(10)(inputs)
model = tf.keras.Model(inputs=inputs, outputs=outputs)

x = tf.random.normal(shape=(8, 5, 5, 5))
model(x)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:12: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  if sys.path[0] == '':
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/base.py:573: UserWarning: `layer.updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically.
  _add_elements_to_collection(self.updates, tf.compat.v1.GraphKeys.UPDATE_OPS)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:13: UserWarning: `tf.layers.flatten` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Flatten` instead.
  del sys.path[0]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:16: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  app.launch_new_instance()
<tf.Tensor: shape=(8, 10), dtype=float32, numpy=
array([[ 0.19487001,  0.54727787,  1.1044168 , -0.6613899 , -0.26437742,
        -1.1580509 , -0.24707682,  0.97752655,  0.59436107,  0.13125825],
       [ 0.48974586, -1.3510125 ,  0.7186962 , -0.8996632 , -0.60448873,
         0.06332532,  0.31494308,  0.23021704, -1.9166642 ,  0.3890404 ],
       [-0.06499191, -0.21485235,  0.01158494,  1.4407377 , -0.0488929 ,
        -0.37594396, -0.4386894 , -0.08751169,  1.0905663 , -1.5450519 ],
       [-2.2749739 , -2.4603422 , -1.3834419 , -2.8800466 ,  0.8954872 ,
        -3.0429187 , -0.7885461 ,  1.6037437 , -3.1845028 , -1.0725503 ],
       [ 0.98735195, -0.45159122,  0.892656  ,  0.477053  ,  0.31193537,
        -0.44723228, -0.01815075, -0.47465172, -1.665448  , -2.105824  ],
       [-2.5408387 , -1.7552321 , -1.924145  , -0.6395873 ,  0.4081779 ,
        -0.48731515, -3.2637763 , -1.4409767 , -2.032539  ,  0.10204412],
       [ 2.1583526 ,  0.78955674, -0.07266375,  0.06652926,  2.1300716 ,
        -1.6256162 ,  0.56154627, -0.76179224,  2.2985756 , -1.5504618 ],
       [ 2.062847  ,  0.971378  , -1.0830508 ,  1.8224751 , -0.3542943 ,
         0.74113446, -0.6204865 ,  1.4503044 , -0.4979878 , -0.4383126 ]],
      dtype=float32)>
# Access the model variables and regularization losses
model.weights
model.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.03079858>,
 <tf.Tensor: shape=(), dtype=float32, numpy=0.12991619>]

Przechwytuj aktualizacje normalizacji wsadowej i argumenty training modelu

W TF1.x wykonujesz normalizację wsadową w następujący sposób:

  x_norm = tf.compat.v1.layers.batch_normalization(x, training=training)

  # ...

  update_ops = tf.compat.v1.get_collection(tf.GraphKeys.UPDATE_OPS)
  train_op = optimizer.minimize(loss)
  train_op = tf.group([train_op, update_ops])

Zwróć uwagę, że:

  1. Aktualizacje średniej ruchomej normalizacji partii są śledzone przez get_collection , które zostało wywołane niezależnie od warstwy
  2. tf.compat.v1.layers.batch_normalization wymaga argumentu training (zwykle nazywanego is_training w przypadku korzystania z warstw normalizacji wsadowej TF-Slim)

W TF2 ze względu na gorliwe wykonywanie i zależności automatycznego sterowania, aktualizacje średniej ruchomej normalizacji partii zostaną wykonane natychmiast. Nie ma potrzeby oddzielnego zbierania ich z kolekcji aktualizacji i dodawania ich jako jawnych zależności kontrolnych.

Dodatkowo, jeśli podasz metodzie przekazywania do przodu tf.keras.layers.Layer argument training , Keras będzie mógł przekazać do niej bieżącą fazę szkolenia i wszelkie zagnieżdżone warstwy, tak jak w przypadku każdej innej warstwy. Zobacz dokumentację interfejsu API dla tf.keras.Model , aby uzyskać więcej informacji o tym, jak Keras obsługuje argument training .

Jeśli dekorujesz metody tf.Module , musisz upewnić się, że ręcznie przekażesz wszystkie argumenty training w razie potrzeby. Jednak aktualizacje średniej ruchomej normalizacji partii będą nadal stosowane automatycznie, bez potrzeby wyraźnych zależności kontroli.

Poniższe fragmenty kodu pokazują, jak osadzić warstwy normalizacji wsadowej w podkładce i jak działa jej użycie w modelu Keras (dotyczy tf.keras.layers.Layer ).

class CompatV1BatchNorm(tf.keras.layers.Layer):

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    print("Forward pass called with `training` =", training)
    with v1.variable_scope('batch_norm_layer'):
      return v1.layers.batch_normalization(x, training=training)
print("Constructing model")
inputs = tf.keras.Input(shape=(5, 5, 5))
outputs = CompatV1BatchNorm()(inputs)
model = tf.keras.Model(inputs=inputs, outputs=outputs)

print("Calling model in inference mode")
x = tf.random.normal(shape=(8, 5, 5, 5))
model(x, training=False)

print("Moving average variables before training: ",
      {var.name: var.read_value() for var in model.non_trainable_variables})

# Notice that when running TF2 and eager execution, the batchnorm layer directly
# updates the moving averages while training without needing any extra control
# dependencies
print("calling model in training mode")
model(x, training=True)

print("Moving average variables after training: ",
      {var.name: var.read_value() for var in model.non_trainable_variables})
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:7: UserWarning: `tf.layers.batch_normalization` is deprecated and will be removed in a future version. Please use `tf.keras.layers.BatchNormalization` instead. In particular, `tf.control_dependencies(tf.GraphKeys.UPDATE_OPS)` should not be used (consult the `tf.keras.layers.BatchNormalization` documentation).
  import sys
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/normalization.py:463: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs, training=training)
Constructing model
Forward pass called with `training` = None
Calling model in inference mode
Forward pass called with `training` = False
Moving average variables before training:  {'batch_norm_layer/batch_normalization/moving_mean:0': <tf.Tensor: shape=(5,), dtype=float32, numpy=array([0., 0., 0., 0., 0.], dtype=float32)>, 'batch_norm_layer/batch_normalization/moving_variance:0': <tf.Tensor: shape=(5,), dtype=float32, numpy=array([1., 1., 1., 1., 1.], dtype=float32)>}
calling model in training mode
Forward pass called with `training` = True
Moving average variables after training:  {'batch_norm_layer/batch_normalization/moving_mean:0': <tf.Tensor: shape=(5,), dtype=float32, numpy=
array([-0.00177554, -0.00036542, -0.00099426, -0.00112544,  0.0008541 ],
      dtype=float32)>, 'batch_norm_layer/batch_normalization/moving_variance:0': <tf.Tensor: shape=(5,), dtype=float32, numpy=
array([1.0005339, 1.0003369, 0.9976748, 1.0001523, 1.0009514],
      dtype=float32)>}

Ponowne wykorzystanie zmiennych o zmiennym zakresie

Wszelkie kreacje zmiennych w przejściu do przodu w oparciu o get_variable zachowają te same nazwy zmiennych i semantykę ponownego wykorzystania, które mają zakresy zmiennych w TF1.x. Jest to prawdą, o ile masz co najmniej jeden niepusty zakres zewnętrzny dla dowolnego tf.compat.v1.layers z automatycznie wygenerowanymi nazwami, jak wspomniano powyżej.

tf.function wykonanie i funkcja tf.

Jak widać powyżej, zdobione metody dla tf.keras.layers.Layer i tf.Module działają wewnątrz gorliwego wykonania i są również kompatybilne z tf.function . Oznacza to, że możesz użyć pdb i innych interaktywnych narzędzi, aby przejść przez przebieg do przodu podczas jego działania.

Strategie dystrybucji

Wywołania get_variable wewnątrz @track_tf1_style_variables warstwy lub metody modułu wykorzystują standardowe kreacje zmiennych tf.Variable pod maską. Oznacza to, że możesz ich używać z różnymi strategiami dystrybucji dostępnymi w tf.distribute , takimi jak MirroredStrategy i TPUStrategy .

Zagnieżdżanie tf.Variable s, tf.Module s, tf.keras.layers i tf.keras.models w zdobionych połączeniach

Udekorowanie wywołania warstwy w tf.compat.v1.keras.utils.track_tf1_style_variables doda tylko automatyczne niejawne śledzenie zmiennych utworzonych (i ponownie użytych) za pośrednictwem tf.compat.v1.get_variable . Nie przechwytuje wag bezpośrednio utworzonych przez wywołania tf.Variable , takich jak te używane przez typowe warstwy Keras i większość tf.Module s. W tej sekcji opisano, jak obsługiwać te zagnieżdżone przypadki.

(Wcześniej istniejące zastosowania) tf.keras.layers i tf.keras.models

W przypadku wcześniejszych zastosowań zagnieżdżonych warstw i modeli Keras użyj tf.compat.v1.keras.utils.get_or_create_layer . Jest to zalecane tylko w celu ułatwienia migracji istniejących zastosowań Keras zagnieżdżonych w TF1.x; nowy kod powinien używać jawnego ustawienia atrybutu, jak opisano poniżej dla tf.Variables i tf.Modules.

Aby użyć tf.compat.v1.keras.utils.get_or_create_layer , zapakuj kod konstruujący model zagnieżdżony w metodę i przekaż go do metody. Przykład:

class NestedModel(tf.keras.Model):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units

  def build_model(self):
    inp = tf.keras.Input(shape=(5, 5))
    dense_layer = tf.keras.layers.Dense(
        10, name="dense", kernel_regularizer="l2",
        kernel_initializer=tf.compat.v1.ones_initializer())
    model = tf.keras.Model(inputs=inp, outputs=dense_layer(inp))
    return model

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    # Get or create a nested model without assigning it as an explicit property
    model = tf.compat.v1.keras.utils.get_or_create_layer(
        "dense_model", self.build_model)
    return model(inputs)

layer = NestedModel(10)
layer(tf.ones(shape=(5,5)))
<tf.Tensor: shape=(5, 10), dtype=float32, numpy=
array([[5., 5., 5., 5., 5., 5., 5., 5., 5., 5.],
       [5., 5., 5., 5., 5., 5., 5., 5., 5., 5.],
       [5., 5., 5., 5., 5., 5., 5., 5., 5., 5.],
       [5., 5., 5., 5., 5., 5., 5., 5., 5., 5.],
       [5., 5., 5., 5., 5., 5., 5., 5., 5., 5.]], dtype=float32)>

Ta metoda zapewnia, że ​​te zagnieżdżone warstwy są poprawnie ponownie wykorzystywane i śledzone przez tensorflow. Zauważ, że dekorator @track_tf1_style_variables jest nadal wymagany w odpowiedniej metodzie. Metoda konstruktora modelu przekazana do get_or_create_layer (w tym przypadku self.build_model ) nie powinna przyjmować żadnych argumentów.

Wagi są śledzone:

assert len(layer.weights) == 2
weights = {x.name: x for x in layer.variables}

assert set(weights.keys()) == {"dense/bias:0", "dense/kernel:0"}

layer.weights
[<tf.Variable 'dense/kernel:0' shape=(5, 10) dtype=float32, numpy=
 array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32)>,
 <tf.Variable 'dense/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>]

A także utrata regularyzacji:

tf.add_n(layer.losses)
<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.5], dtype=float32)>

Migracja przyrostowa: tf.Variables i tf.Modules

Jeśli musisz osadzić wywołania tf.Variable lub tf.Module s w zdobionych metodach (na przykład, jeśli śledzisz przyrostową migrację do niestarszych interfejsów API TF2 opisaną w dalszej części tego przewodnika), nadal musisz je jawnie śledzić, z następującymi wymaganiami:

  • Jawnie upewnij się, że zmienna/moduł/warstwa jest tworzona tylko raz
  • Jawnie dołącz je jako atrybuty instancji, tak jak podczas definiowania typowego modułu lub warstwy
  • Jawne ponowne użycie już utworzonego obiektu w kolejnych wywołaniach

Gwarantuje to, że wagi nie są tworzone od nowa przy każdym wywołaniu i są prawidłowo ponownie wykorzystywane. Dodatkowo zapewnia to również śledzenie istniejących wag i strat związanych z regularyzacją.

Oto przykład, jak to może wyglądać:

class NestedLayer(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def __call__(self, inputs):
    out = inputs
    with tf.compat.v1.variable_scope("inner_dense"):
      # The weights are created with a `regularizer`,
      # so the layer should track their regularization losses
      kernel = tf.compat.v1.get_variable(
          shape=[out.shape[-1], self.units],
          regularizer=tf.keras.regularizers.L2(),
          initializer=tf.compat.v1.initializers.glorot_normal,
          name="kernel")
      bias = tf.compat.v1.get_variable(
          shape=[self.units,],
          initializer=tf.compat.v1.initializers.zeros,
          name="bias")
      out = tf.linalg.matmul(out, kernel)
      out = tf.compat.v1.nn.bias_add(out, bias)
    return out

class WrappedDenseLayer(tf.keras.layers.Layer):

  def __init__(self, units, **kwargs):
    super().__init__(**kwargs)
    self.units = units
    # Only create the nested tf.variable/module/layer/model
    # once, and then reuse it each time!
    self._dense_layer = NestedLayer(self.units)

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    with tf.compat.v1.variable_scope('outer'):
      outputs = tf.compat.v1.layers.dense(inputs, 3)
      outputs = tf.compat.v1.layers.dense(inputs, 4)
      return self._dense_layer(outputs)

layer = WrappedDenseLayer(10)

layer(tf.ones(shape=(5, 5)))
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:38: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:39: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
<tf.Tensor: shape=(5, 10), dtype=float32, numpy=
array([[-0.4987283 ,  0.06630042, -0.09875254,  0.20954818,  0.03599668,
         0.3980474 ,  0.11181635,  0.6891558 , -0.33903462,  0.15674731],
       [-0.4987283 ,  0.06630042, -0.09875254,  0.20954818,  0.03599668,
         0.3980474 ,  0.11181635,  0.6891558 , -0.33903462,  0.15674731],
       [-0.4987283 ,  0.06630042, -0.09875254,  0.20954818,  0.03599668,
         0.3980474 ,  0.11181635,  0.6891558 , -0.33903462,  0.15674731],
       [-0.4987283 ,  0.06630042, -0.09875254,  0.20954818,  0.03599668,
         0.3980474 ,  0.11181635,  0.6891558 , -0.33903462,  0.15674731],
       [-0.4987283 ,  0.06630042, -0.09875254,  0.20954818,  0.03599668,
         0.3980474 ,  0.11181635,  0.6891558 , -0.33903462,  0.15674731]],
      dtype=float32)>

Należy zauważyć, że wymagane jest jawne śledzenie modułu zagnieżdżonego, mimo że jest on wyposażony w dekorator track_tf1_style_variables . Dzieje się tak dlatego, że każdy moduł/warstwa z dekorowanymi metodami ma swój własny, powiązany z nim magazyn zmiennych.

Wagi są prawidłowo śledzone:

assert len(layer.weights) == 6
weights = {x.name: x for x in layer.variables}

assert set(weights.keys()) == {"outer/inner_dense/bias:0",
                               "outer/inner_dense/kernel:0",
                               "outer/dense/bias:0",
                               "outer/dense/kernel:0",
                               "outer/dense_1/bias:0",
                               "outer/dense_1/kernel:0"}

layer.trainable_weights
[<tf.Variable 'outer/inner_dense/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>,
 <tf.Variable 'outer/inner_dense/kernel:0' shape=(4, 10) dtype=float32, numpy=
 array([[-0.20786692,  0.14702448, -0.2577947 ,  0.1885891 ,  0.28935957,
          0.02086618, -0.20579144, -0.7509229 , -0.23490003,  0.00370591],
        [ 0.09247629, -0.37428686, -0.6002815 , -0.2702465 ,  0.20350575,
          0.34964404, -0.32633537,  0.50722903, -0.0419833 , -0.61815673],
        [ 0.24821116,  0.15504731, -0.12409697, -0.2506969 ,  0.22316858,
         -0.44847375, -0.08295754, -0.8262154 ,  0.7674222 , -0.40613693],
        [-0.7447006 ,  0.2992331 , -0.45639235,  0.0669547 ,  0.39443025,
          0.3182467 ,  0.10884362,  0.5395837 ,  0.32210502, -0.30076835]],
       dtype=float32)>,
 <tf.Variable 'outer/dense/bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>,
 <tf.Variable 'outer/dense/kernel:0' shape=(5, 3) dtype=float32, numpy=
 array([[ 0.6283595 , -0.80413634, -0.5471641 ],
        [ 0.25296038, -0.7657203 ,  0.5884425 ],
        [-0.7180575 , -0.29509914,  0.44014376],
        [ 0.81024987,  0.39888996,  0.80002993],
        [-0.32921118, -0.7010279 ,  0.820375  ]], dtype=float32)>,
 <tf.Variable 'outer/dense_1/bias:0' shape=(4,) dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>,
 <tf.Variable 'outer/dense_1/kernel:0' shape=(5, 4) dtype=float32, numpy=
 array([[ 0.7941524 , -0.58552563,  0.46828055, -0.44095916],
        [-0.16019303,  0.27973688, -0.60373306, -0.20117629],
        [ 0.6345844 ,  0.30732214,  0.18921828,  0.37930095],
        [-0.50815696, -0.2471816 , -0.10282421,  0.21441567],
        [-0.71987414,  0.18304104, -0.5701992 ,  0.4926386 ]],
       dtype=float32)>]

Jak również utrata regularyzacji:

layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.058749676>]

Zwróć uwagę, że gdyby NestedLayer był modułem tf.Module innym niż Keras, zmienne nadal byłyby śledzone, ale straty w regularyzacji nie byłyby śledzone automatycznie, więc musiałbyś jawnie śledzić je osobno.

Wskazówki dotyczące nazw zmiennych

Jawne wywołania tf.Variable i warstwy Keras używają innego mechanizmu autogenerowania nazwy warstwy/nazwy zmiennej niż ten, do którego możesz być przyzwyczajony z kombinacji get_variable i variable_scopes . Chociaż podkładka sprawi, że nazwy zmiennych będą zgodne ze zmiennymi utworzonymi przez get_variable nawet podczas przechodzenia z wykresów TF1.x do szybkiego wykonania TF2 i tf.function , nie może zagwarantować tego samego dla nazw zmiennych generowanych dla wywołań tf.Variable i warstw Keras, które osadzasz w swoich dekoratorach metod. Możliwe jest nawet, aby wiele zmiennych współdzieliło tę samą nazwę w szybkim wykonaniu TF2 i tf.function .

Należy zachować szczególną ostrożność podczas wykonywania sekcji dotyczących sprawdzania poprawności i mapowania punktów kontrolnych TF1.x w dalszej części tego przewodnika.

Korzystanie z tf.compat.v1.make_template w metodzie dekorowanej

Zdecydowanie zaleca się bezpośrednie użycie tf.compat.v1.keras.utils.track_tf1_style_variables zamiast tf.compat.v1.make_template , ponieważ jest to cieńsza warstwa na wierzchu TF2 .

Postępuj zgodnie ze wskazówkami w tej sekcji dla wcześniejszego kodu TF1.x, który już opierał się na tf.compat.v1.make_template .

Ponieważ tf.compat.v1.make_template zawija kod, który używa get_variable , dekorator track_tf1_style_variables umożliwia używanie tych szablonów w wywołaniach warstw i skuteczne śledzenie wag i strat związanych z regularyzacją.

Pamiętaj jednak, aby wywołać make_template tylko raz, a następnie ponownie użyć tego samego szablonu w każdym wywołaniu warstwy. W przeciwnym razie za każdym razem, gdy wywołasz warstwę, zostanie utworzony nowy szablon wraz z nowym zestawem zmiennych.

Na przykład,

class CompatV1TemplateScaleByY(tf.keras.layers.Layer):

  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    def my_op(x, scalar_name):
      var1 = tf.compat.v1.get_variable(scalar_name,
                            shape=[],
                            regularizer=tf.compat.v1.keras.regularizers.L2(),
                            initializer=tf.compat.v1.constant_initializer(1.5))
      return x * var1
    self.scale_by_y = tf.compat.v1.make_template('scale_by_y', my_op, scalar_name='y')

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    with tf.compat.v1.variable_scope('layer'):
      # Using a scope ensures the `scale_by_y` name will not be incremented
      # for each instantiation of the layer.
      return self.scale_by_y(inputs)

layer = CompatV1TemplateScaleByY()

out = layer(tf.ones(shape=(2, 3)))
print("weights:", layer.weights)
print("regularization loss:", layer.losses)
print("output:", out)
weights: [<tf.Variable 'layer/scale_by_y/y:0' shape=() dtype=float32, numpy=1.5>]
regularization loss: [<tf.Tensor: shape=(), dtype=float32, numpy=0.022499999>]
output: tf.Tensor(
[[1.5 1.5 1.5]
 [1.5 1.5 1.5]], shape=(2, 3), dtype=float32)

Przyrostowa migracja do Native TF2

Jak wspomniano wcześniej, track_tf1_style_variables umożliwia mieszanie zorientowanego obiektowo stylu TF2 tf.Variable / tf.keras.layers.Layer / tf.Module ze starszym tf.compat.v1.get_variable / tf.compat.v1.layers zastosowanie wewnątrz tego samego dekorowanego modułu/warstwy.

Oznacza to, że po utworzeniu modelu TF1.x w pełni zgodnego z TF2 można napisać wszystkie nowe komponenty modelu za pomocą natywnych (nie tf.compat.v1 ) API TF2 i umożliwić im współdziałanie ze starszym kodem.

Jeśli jednak będziesz nadal modyfikować komponenty starszego modelu, możesz również stopniowo przełączyć użycie tf.compat.v1 w starszym stylu na całkowicie natywne, obiektowe interfejsy API, które są zalecane dla nowo pisanego kodu TF2.

Użycie tf.compat.v1.get_variable można zastąpić wywołaniami self.add_weight , jeśli dekorujesz warstwę/model Keras, lub wywołaniami tf.Variable , jeśli dekorujesz obiekty Keras lub tf.Module s.

Zarówno funkcjonalną, jak i obiektową tf.compat.v1.layers można zasadniczo zastąpić równoważną warstwą tf.keras.layers bez konieczności zmiany argumentów.

Możesz również rozważyć fragmenty części swojego modelu lub wspólne wzorce na poszczególne warstwy/moduły podczas stopniowego przejścia do czysto natywnych interfejsów API, które same mogą używać track_tf1_style_variables .

Uwaga na temat Slim i contrib.layers

Duża ilość starszego kodu TF 1.x korzysta z biblioteki Slim , która została spakowana z TF 1.x jako tf.contrib.layers . Konwersja kodu przy użyciu Slim do natywnego TF 2 jest bardziej skomplikowana niż konwersja v1.layers . W rzeczywistości może mieć sens najpierw przekonwertować kod Slim na v1.layers , a następnie przekonwertować na Keras. Poniżej znajdują się ogólne wskazówki dotyczące konwersji kodu Slim.

  • Upewnij się, że wszystkie argumenty są jawne. Jeśli to możliwe, usuń arg_scopes . Jeśli nadal potrzebujesz ich używać, podziel normalizer_fn i activation_fn na osobne warstwy.
  • Oddzielne warstwy konw. są mapowane na jedną lub więcej różnych warstw Keras (warstwy wgłębne, punktowe i rozdzielne Keras).
  • Slim i v1.layers mają różne nazwy argumentów i wartości domyślne.
  • Zauważ, że niektóre argumenty mają różne skale.

Migracja do Native TF2 z pominięciem zgodności w punktach kontrolnych

Poniższy przykład kodu demonstruje przyrostowe przenoszenie modelu do czysto natywnych interfejsów API bez uwzględniania zgodności z punktem kontrolnym.

class CompatModel(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    with tf.compat.v1.variable_scope('model'):
      out = tf.compat.v1.layers.conv2d(
          inputs, 3, 3,
          kernel_regularizer="l2")
      out = tf.compat.v1.layers.flatten(out)
      out = tf.compat.v1.layers.dropout(out, training=training)
      out = tf.compat.v1.layers.dense(
          out, self.units,
          kernel_regularizer="l2")
      return out

Następnie zastąp po kawałku interfejsy API compat.v1 ich natywnymi odpowiednikami zorientowanymi obiektowo. Zacznij od przełączenia warstwy splotu na obiekt Keras utworzony w konstruktorze warstwy.

class PartiallyMigratedModel(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units
    self.conv_layer = tf.keras.layers.Conv2D(
      3, 3,
      kernel_regularizer="l2")

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    with tf.compat.v1.variable_scope('model'):
      out = self.conv_layer(inputs)
      out = tf.compat.v1.layers.flatten(out)
      out = tf.compat.v1.layers.dropout(out, training=training)
      out = tf.compat.v1.layers.dense(
          out, self.units,
          kernel_regularizer="l2")
      return out

Użyj klasy v1.keras.utils.DeterministicRandomTestTool , aby sprawdzić, czy ta przyrostowa zmiana pozostawia model z takim samym zachowaniem jak wcześniej.

random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
  layer = CompatModel(10)

  inputs = tf.random.normal(shape=(10, 5, 5, 5))
  original_output = layer(inputs)

  # Grab the regularization loss as well
  original_regularization_loss = tf.math.add_n(layer.losses)

print(original_regularization_loss)
tf.Tensor(0.17953834, shape=(), dtype=float32)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:12: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  if sys.path[0] == '':
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:13: UserWarning: `tf.layers.flatten` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Flatten` instead.
  del sys.path[0]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: `tf.layers.dropout` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dropout` instead.
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/core.py:413: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs, training=training)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:17: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
  layer = PartiallyMigratedModel(10)

  inputs = tf.random.normal(shape=(10, 5, 5, 5))
  migrated_output = layer(inputs)

  # Grab the regularization loss as well
  migrated_regularization_loss = tf.math.add_n(layer.losses)

print(migrated_regularization_loss)
tf.Tensor(0.17953834, shape=(), dtype=float32)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: `tf.layers.flatten` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Flatten` instead.
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: `tf.layers.dropout` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dropout` instead.
  from ipykernel import kernelapp as app
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:18: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
# Verify that the regularization loss and output both match
np.testing.assert_allclose(original_regularization_loss.numpy(), migrated_regularization_loss.numpy())
np.testing.assert_allclose(original_output.numpy(), migrated_output.numpy())

Zamieniłeś teraz wszystkie poszczególne compat.v1.layers na natywne warstwy Keras.

class NearlyFullyNativeModel(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units
    self.conv_layer = tf.keras.layers.Conv2D(
      3, 3,
      kernel_regularizer="l2")
    self.flatten_layer = tf.keras.layers.Flatten()
    self.dense_layer = tf.keras.layers.Dense(
      self.units,
      kernel_regularizer="l2")

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    with tf.compat.v1.variable_scope('model'):
      out = self.conv_layer(inputs)
      out = self.flatten_layer(out)
      out = self.dense_layer(out)
      return out
random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
  layer = NearlyFullyNativeModel(10)

  inputs = tf.random.normal(shape=(10, 5, 5, 5))
  migrated_output = layer(inputs)

  # Grab the regularization loss as well
  migrated_regularization_loss = tf.math.add_n(layer.losses)

print(migrated_regularization_loss)
tf.Tensor(0.17953834, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
np.testing.assert_allclose(original_regularization_loss.numpy(), migrated_regularization_loss.numpy())
np.testing.assert_allclose(original_output.numpy(), migrated_output.numpy())

Na koniec usuń zarówno wszelkie pozostałe (już nie potrzebne) użycie variable_scope , jak i sam dekorator track_tf1_style_variables .

Masz teraz wersję modelu, która wykorzystuje całkowicie natywne interfejsy API.

class FullyNativeModel(tf.keras.layers.Layer):

  def __init__(self, units, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.units = units
    self.conv_layer = tf.keras.layers.Conv2D(
      3, 3,
      kernel_regularizer="l2")
    self.flatten_layer = tf.keras.layers.Flatten()
    self.dense_layer = tf.keras.layers.Dense(
      self.units,
      kernel_regularizer="l2")

  def call(self, inputs):
    out = self.conv_layer(inputs)
    out = self.flatten_layer(out)
    out = self.dense_layer(out)
    return out
random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
  layer = FullyNativeModel(10)

  inputs = tf.random.normal(shape=(10, 5, 5, 5))
  migrated_output = layer(inputs)

  # Grab the regularization loss as well
  migrated_regularization_loss = tf.math.add_n(layer.losses)

print(migrated_regularization_loss)
tf.Tensor(0.17953834, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
np.testing.assert_allclose(original_regularization_loss.numpy(), migrated_regularization_loss.numpy())
np.testing.assert_allclose(original_output.numpy(), migrated_output.numpy())

Utrzymanie zgodności punktów kontrolnych podczas migracji do Native TF2

Powyższy proces migracji do natywnych interfejsów API TF2 zmienił zarówno nazwy zmiennych (ponieważ interfejsy API Keras generują bardzo różne nazwy wag), jak i ścieżki zorientowane obiektowo, które wskazują różne wagi w modelu. Wpływ tych zmian polega na tym, że zniszczą zarówno istniejące punkty kontrolne oparte na nazwach w stylu TF1, jak i punkty kontrolne zorientowane obiektowo w stylu TF2.

Jednak w niektórych przypadkach możesz być w stanie wziąć swój pierwotny punkt kontrolny oparty na nazwie i znaleźć mapowanie zmiennych na ich nowe nazwy, stosując metody takie jak opisane w przewodniku Ponowne używanie punktów kontrolnych TF1.x .

Oto kilka wskazówek, jak to zrobić:

  • Wszystkie zmienne nadal mają argument name , który można ustawić.
  • Modele Keras przyjmują również argument name , który ustawiają jako przedrostek dla swoich zmiennych.
  • Funkcja v1.name_scope może służyć do ustawiania przedrostków nazw zmiennych. To bardzo różni się od tf.variable_scope . Wpływa tylko na nazwy i nie śledzi zmiennych ani ponownego użycia.

Mając na uwadze powyższe wskaźniki, poniższe przykłady kodu demonstrują przepływ pracy, który można dostosować do kodu, aby przyrostowo aktualizować część modelu, jednocześnie aktualizując punkty kontrolne.

  1. Zacznij od przełączenia funkcjonalnego stylu tf.compat.v1.layers na ich wersje zorientowane obiektowo.
class FunctionalStyleCompatModel(tf.keras.layers.Layer):

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    with tf.compat.v1.variable_scope('model'):
      out = tf.compat.v1.layers.conv2d(
          inputs, 3, 3,
          kernel_regularizer="l2")
      out = tf.compat.v1.layers.conv2d(
          out, 4, 4,
          kernel_regularizer="l2")
      out = tf.compat.v1.layers.conv2d(
          out, 5, 5,
          kernel_regularizer="l2")
      return out

layer = FunctionalStyleCompatModel()
layer(tf.ones(shape=(10, 10, 10, 10)))
[v.name for v in layer.weights]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:8: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:11: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  # This is added back by InteractiveShellApp.init_path()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
['model/conv2d/bias:0',
 'model/conv2d/kernel:0',
 'model/conv2d_1/bias:0',
 'model/conv2d_1/kernel:0',
 'model/conv2d_2/bias:0',
 'model/conv2d_2/kernel:0']
  1. Następnie przypisz obiekty compat.v1.layer i wszelkie zmienne utworzone przez compat.v1.get_variable jako właściwości obiektu tf.keras.layers.Layer / tf.Module , którego metoda jest ozdobiona track_tf1_style_variables (zauważ, że każdy obiekt TF2 punkty kontrolne stylu będą teraz zapisywać zarówno ścieżkę według nazwy zmiennej, jak i nową ścieżkę zorientowaną obiektowo).
class OOStyleCompatModel(tf.keras.layers.Layer):

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.conv_1 = tf.compat.v1.layers.Conv2D(
          3, 3,
          kernel_regularizer="l2")
    self.conv_2 = tf.compat.v1.layers.Conv2D(
          4, 4,
          kernel_regularizer="l2")

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    with tf.compat.v1.variable_scope('model'):
      out = self.conv_1(inputs)
      out = self.conv_2(out)
      out = tf.compat.v1.layers.conv2d(
          out, 5, 5,
          kernel_regularizer="l2")
      return out

layer = OOStyleCompatModel()
layer(tf.ones(shape=(10, 10, 10, 10)))
[v.name for v in layer.weights]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:19: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
['model/conv2d/kernel:0',
 'model/conv2d/bias:0',
 'model/conv2d_1/kernel:0',
 'model/conv2d_1/bias:0',
 'model/conv2d_2/bias:0',
 'model/conv2d_2/kernel:0']
  1. Ponownie zapisz załadowany punkt kontrolny w tym momencie, aby zapisać ścieżki zarówno przez nazwę zmiennej (dla compat.v1.layers), jak i przez obiektowy graf obiektowy.
weights = {v.name: v for v in layer.weights}
assert weights['model/conv2d/kernel:0'] is layer.conv_1.kernel
assert weights['model/conv2d_1/bias:0'] is layer.conv_2.bias
  1. Możesz teraz zamienić zorientowane obiektowo compat.v1.layers na natywne warstwy Keras, zachowując jednocześnie możliwość wczytania ostatnio zapisanego punktu kontrolnego. Upewnij się, że zachowujesz nazwy zmiennych dla pozostałych compat.v1.layers , nadal rejestrując automatycznie generowane variable_scopes zastępowanych warstw. Te przełączane warstwy/zmienne będą teraz używać tylko ścieżki atrybutu obiektu do zmiennych w punkcie kontrolnym zamiast ścieżki nazwy zmiennej.

Ogólnie rzecz biorąc, można zastąpić użycie compat.v1.get_variable w zmiennych dołączonych do właściwości przez:

  • Przełączanie ich na używanie tf.Variable , OR
  • Aktualizowanie ich za pomocą tf.keras.layers.Layer.add_weight . Zauważ, że jeśli nie przełączasz wszystkich warstw za jednym razem, może to zmienić automatycznie generowane nazewnictwo warstw/zmiennych dla pozostałych compat.v1.layers , w których brakuje argumentu name . W takim przypadku należy zachować takie same nazwy zmiennych dla pozostałych compat.v1.layers , ręcznie otwierając i zamykając variable_scope odpowiadający wygenerowanej nazwie zakresu usuniętej compat.v1.layer . W przeciwnym razie ścieżki z istniejących punktów kontrolnych mogą kolidować i ładowanie punktów kontrolnych będzie zachowywać się niepoprawnie.
def record_scope(scope_name):
  """Record a variable_scope to make sure future ones get incremented."""
  with tf.compat.v1.variable_scope(scope_name):
    pass

class PartiallyNativeKerasLayersModel(tf.keras.layers.Layer):

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.conv_1 = tf.keras.layers.Conv2D(
          3, 3,
          kernel_regularizer="l2")
    self.conv_2 = tf.keras.layers.Conv2D(
          4, 4,
          kernel_regularizer="l2")

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    with tf.compat.v1.variable_scope('model'):
      out = self.conv_1(inputs)
      record_scope('conv2d') # Only needed if follow-on compat.v1.layers do not pass a `name` arg
      out = self.conv_2(out)
      record_scope('conv2d_1') # Only needed if follow-on compat.v1.layers do not pass a `name` arg
      out = tf.compat.v1.layers.conv2d(
          out, 5, 5,
          kernel_regularizer="l2")
      return out

layer = PartiallyNativeKerasLayersModel()
layer(tf.ones(shape=(10, 10, 10, 10)))
[v.name for v in layer.weights]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:26: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
['partially_native_keras_layers_model/model/conv2d_13/kernel:0',
 'partially_native_keras_layers_model/model/conv2d_13/bias:0',
 'partially_native_keras_layers_model/model/conv2d_14/kernel:0',
 'partially_native_keras_layers_model/model/conv2d_14/bias:0',
 'model/conv2d_2/bias:0',
 'model/conv2d_2/kernel:0']

Zapisanie punktu kontrolnego na tym etapie po skonstruowaniu zmiennych spowoduje, że będzie on zawierał tylko aktualnie dostępne ścieżki obiektów.

Upewnij się, że rejestrujesz zakresy usuniętych compat.v1.layers , aby zachować automatycznie wygenerowane nazwy wag dla pozostałych compat.v1.layers .

weights = set(v.name for v in layer.weights)
assert 'model/conv2d_2/kernel:0' in weights
assert 'model/conv2d_2/bias:0' in weights
  1. Powtarzaj powyższe kroki, aż zastąpisz wszystkie compat.v1.layers i compat.v1.get_variable w swoim modelu w pełni natywnymi odpowiednikami.
class FullyNativeKerasLayersModel(tf.keras.layers.Layer):

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.conv_1 = tf.keras.layers.Conv2D(
          3, 3,
          kernel_regularizer="l2")
    self.conv_2 = tf.keras.layers.Conv2D(
          4, 4,
          kernel_regularizer="l2")
    self.conv_3 = tf.keras.layers.Conv2D(
          5, 5,
          kernel_regularizer="l2")


  def call(self, inputs, training=None):
    with tf.compat.v1.variable_scope('model'):
      out = self.conv_1(inputs)
      out = self.conv_2(out)
      out = self.conv_3(out)
      return out

layer = FullyNativeKerasLayersModel()
layer(tf.ones(shape=(10, 10, 10, 10)))
[v.name for v in layer.weights]
['fully_native_keras_layers_model/model/conv2d_16/kernel:0',
 'fully_native_keras_layers_model/model/conv2d_16/bias:0',
 'fully_native_keras_layers_model/model/conv2d_17/kernel:0',
 'fully_native_keras_layers_model/model/conv2d_17/bias:0',
 'fully_native_keras_layers_model/model/conv2d_18/kernel:0',
 'fully_native_keras_layers_model/model/conv2d_18/bias:0']

Pamiętaj, aby przetestować, aby upewnić się, że nowo zaktualizowany punkt kontrolny nadal zachowuje się zgodnie z oczekiwaniami. Zastosuj techniki opisane w przewodniku dotyczącym sprawdzania poprawności liczbowej na każdym kolejnym kroku tego procesu, aby zapewnić prawidłowe działanie zmigrowanego kodu.

Obsługa zmian zachowania TF1.x do TF2 nieobjętych podkładkami modelującymi

Podkładki modelujące opisane w tym przewodniku mogą zapewnić, że zmienne, warstwy i straty w regularyzacji utworzone za pomocą semantyki get_variable , tf.compat.v1.layers i variable_scope będą nadal działać tak jak wcześniej podczas korzystania z szybkiego wykonywania i tf.function , bez konieczności polegać na kolekcjach.

Nie obejmuje to wszystkich semantyk specyficznych dla TF1.x, na których może polegać Twój model. W niektórych przypadkach podkładki mogą być niewystarczające do samodzielnego uruchomienia modelu w TF2. Przeczytaj przewodnik po zachowaniach TF1.x i TF2, aby dowiedzieć się więcej o różnicach behawioralnych między TF1.x i TF2.