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:
-
tf.compat.v1.get_variable
itf.compat.v1.variable_scope
do sterowania tworzeniem i ponownym użyciem zmiennych oraz - 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()
itf.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:
- Samodzielne modele Keras, które już śledzą wszystkie możliwe do trenowania wagi i straty w regularyzacji odpowiednio za pomocą
model.trainable_weights
imodel.losses
. -
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:
- Aktualizacje średniej ruchomej normalizacji partii są śledzone przez
get_collection
, które zostało wywołane niezależnie od warstwy -
tf.compat.v1.layers.batch_normalization
wymaga argumentutraining
(zwykle nazywanegois_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ć, podzielnormalizer_fn
iactivation_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ę odtf.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.
- 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']
- Następnie przypisz obiekty compat.v1.layer i wszelkie zmienne utworzone przez
compat.v1.get_variable
jako właściwości obiektutf.keras.layers.Layer
/tf.Module
, którego metoda jest ozdobionatrack_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']
- 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
- 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łychcompat.v1.layers
, nadal rejestrując automatycznie generowanevariable_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łychcompat.v1.layers
, w których brakuje argumentuname
. W takim przypadku należy zachować takie same nazwy zmiennych dla pozostałychcompat.v1.layers
, ręcznie otwierając i zamykającvariable_scope
odpowiadający wygenerowanej nazwie zakresu usuniętejcompat.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
- Powtarzaj powyższe kroki, aż zastąpisz wszystkie
compat.v1.layers
icompat.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.