Veja no TensorFlow.org | Executar no Google Colab | Ver no GitHub | Baixar caderno |
Este guia fornece uma visão geral e exemplos de um shim de código de modelagem que você pode empregar para usar seus modelos TF1.x existentes em fluxos de trabalho do TF2, como execução antecipada, tf.function
e estratégias de distribuição com alterações mínimas em seu código de modelagem.
Escopo de uso
O calço descrito neste guia é projetado para modelos TF1.x que dependem de:
-
tf.compat.v1.get_variable
etf.compat.v1.variable_scope
para controlar a criação e reutilização de variáveis e - APIs baseadas em coleção de gráficos, como
tf.compat.v1.global_variables()
,tf.compat.v1.trainable_variables
,tf.compat.v1.losses.get_regularization_losses()
etf.compat.v1.get_collection()
para acompanhar de pesos e perdas de regularização
Isso inclui a maioria dos modelos criados com base nas APIs tf.compat.v1.layer
, tf.contrib.layers
e TensorFlow-Slim .
O calço NÃO é necessário para os seguintes modelos TF1.x:
- Modelos Keras autônomos que já rastreiam todos os seus pesos treináveis e perdas de regularização via
model.trainable_weights
emodel.losses
respectivamente. -
tf.Module
s que já rastreiam todos os seus pesos treináveis viamodule.trainable_variables
e só criam pesos se ainda não tiverem sido criados.
Esses modelos provavelmente funcionarão no TF2 com execução rápida e tf.function
s prontos para uso.
Configurar
Importe o TensorFlow e outras dependências.
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
O decorador track_tf1_style_variables
O shim chave descrito neste guia é tf.compat.v1.keras.utils.track_tf1_style_variables
, um decorador que você pode usar dentro de métodos pertencentes a tf.keras.layers.Layer
e tf.Module
para rastrear pesos de estilo TF1.x e capturar perdas de regularização.
Decorar os métodos de chamada de um tf.keras.layers.Layer
ou tf.Module
com tf.compat.v1.keras.utils.track_tf1_style_variables
permite a criação e reutilização de variáveis via tf.compat.v1.get_variable
(e por extensão tf.compat.v1.layers
) para funcionar corretamente dentro do método decorado ao invés de sempre criar uma nova variável em cada chamada. Isso também fará com que a camada ou módulo rastreie implicitamente quaisquer pesos criados ou acessados via get_variable
dentro do método decorado.
Além de rastrear os próprios pesos sob o padrão layer.variable
/ module.variable
/etc. properties, se o método pertencer a um tf.keras.layers.Layer
, então quaisquer perdas de regularização especificadas por meio dos argumentos do regularizador get_variable
ou tf.compat.v1.layers
serão rastreadas pela camada sob a propriedade layer.losses
padrão.
Esse mecanismo de rastreamento permite o uso de grandes classes de código de passagem de modelo no estilo TF1.x dentro de camadas Keras ou tf.Module
s no TF2, mesmo com comportamentos TF2 ativados.
Exemplos de uso
Os exemplos de uso abaixo demonstram os shims de modelagem usados para decorar métodos tf.keras.layers.Layer
, mas exceto onde eles interagem especificamente com recursos Keras, eles também são aplicáveis ao decorar métodos tf.Module
.
Camada criada com tf.compat.v1.get_variable
Imagine que você tenha uma camada implementada diretamente em cima de tf.compat.v1.get_variable
da seguinte forma:
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
Use o shim para transformá-lo em uma camada e chamá-lo nas entradas.
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)>
Acesse as variáveis rastreadas e as perdas de regularização capturadas como uma camada Keras padrão.
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>]
Para ver que os pesos são reutilizados cada vez que você chama a camada, defina todos os pesos como zero e chame a camada novamente.
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)>
Você também pode usar a camada convertida diretamente na construção do modelo funcional 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>]
Modelo construído com tf.compat.v1.layers
Imagine que você tenha uma camada ou modelo implementado diretamente em cima de tf.compat.v1.layers
da seguinte forma:
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
Use o shim para transformá-lo em uma camada e chamá-lo nas entradas.
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)>
Acesse as variáveis rastreadas e as perdas de regularização capturadas como uma camada Keras padrão.
layer.trainable_variables
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.03623246>, <tf.Tensor: shape=(), dtype=float32, numpy=0.14618248>]
Para ver que os pesos são reutilizados cada vez que você chama a camada, defina todos os pesos como zero e chame a camada novamente.
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)>
Você também pode usar a camada convertida diretamente na construção do modelo funcional 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>]
Capture atualizações de normalização em lote e argumentos training
de modelo
No TF1.x, você executa a normalização em lote assim:
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])
Observe que:
- As atualizações médias móveis de normalização de lote são rastreadas por
get_collection
que foi chamado separadamente da camada -
tf.compat.v1.layers.batch_normalization
requer um argumento detraining
(geralmente chamadois_training
ao usar camadas de normalização em lote TF-Slim)
No TF2, devido à execução rápida e dependências de controle automático, as atualizações de média móvel de normalização de lote serão executadas imediatamente. Não há necessidade de coletá-los separadamente da coleção de atualizações e adicioná-los como dependências de controle explícitas.
Além disso, se você der um argumento de training
para o método forward pass do tf.keras.layers.Layer
, o Keras poderá passar a fase de treinamento atual e quaisquer camadas aninhadas para ele da mesma forma que faz para qualquer outra camada. Consulte os documentos da API para tf.keras.Model
para obter mais informações sobre como o Keras trata o argumento de training
.
Se você estiver decorando métodos tf.Module
, você precisa ter certeza de passar manualmente todos os argumentos de training
conforme necessário. No entanto, as atualizações de média móvel de normalização em lote ainda serão aplicadas automaticamente sem a necessidade de dependências de controle explícitas.
Os trechos de código a seguir demonstram como incorporar camadas de normalização em lote no shim e como funciona o uso em um modelo Keras (aplicável a 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)>}
Reutilização de variável baseada em escopo variável
Qualquer criação de variável na passagem de encaminhamento baseada em get_variable
manterá a mesma nomenclatura de variável e semântica de reutilização que os escopos de variável têm no TF1.x. Isso é verdade desde que você tenha pelo menos um escopo externo não vazio para qualquer tf.compat.v1.layers
com nomes gerados automaticamente, conforme mencionado acima.
Execução ansiosa e tf.function
Como visto acima, métodos decorados para tf.keras.layers.Layer
e tf.Module
são executados dentro da execução antecipada e também são compatíveis com tf.function
. Isso significa que você pode usar o pdb e outras ferramentas interativas para percorrer sua passagem de avanço enquanto ela está em execução.
Estratégias de distribuição
Chamadas para get_variable
dentro de @track_tf1_style_variables
-decorated layer ou module métodos usam criações de variáveis tf.Variable
padrão sob o capô. Isso significa que você pode usá-los com as várias estratégias de distribuição disponíveis com tf.distribute
, como MirroredStrategy
e TPUStrategy
.
Aninhamento tf.Variable
s, tf.Module
s, tf.keras.layers
& tf.keras.models
em chamadas decoradas
Decorar sua chamada de camada em tf.compat.v1.keras.utils.track_tf1_style_variables
apenas adicionará rastreamento implícito automático de variáveis criadas (e reutilizadas) via tf.compat.v1.get_variable
. Ele não capturará pesos criados diretamente por chamadas tf.Variable
, como aquelas usadas por camadas Keras típicas e a maioria dos tf.Module
s. Esta seção descreve como lidar com esses casos aninhados.
(Usos pré-existentes) tf.keras.layers
e tf.keras.models
Para usos pré-existentes de camadas e modelos Keras aninhados, use tf.compat.v1.keras.utils.get_or_create_layer
. Isso é recomendado apenas para facilitar a migração de usos Keras aninhados do TF1.x existentes; o novo código deve usar a configuração de atributo explícita conforme descrito abaixo para tf.Variables e tf.Modules.
Para usar tf.compat.v1.keras.utils.get_or_create_layer
, envolva o código que constrói seu modelo aninhado em um método e passe-o para o método. Exemplo:
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)>
Esse método garante que essas camadas aninhadas sejam reutilizadas e rastreadas corretamente pelo tensorflow. Observe que o decorador @track_tf1_style_variables
ainda é necessário no método apropriado. O método do construtor de modelo passado para get_or_create_layer
(neste caso, self.build_model
), não deve receber argumentos.
Os pesos são rastreados:
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)>]
E perda de regularização também:
tf.add_n(layer.losses)
<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.5], dtype=float32)>
Migração incremental: tf.Variables
e tf.Modules
Se você precisar incorporar chamadas tf.Variable
ou tf.Module
s em seus métodos decorados (por exemplo, se você estiver seguindo a migração incremental para APIs TF2 não legadas descritas posteriormente neste guia), você ainda precisará rastreá-los explicitamente, com os seguintes requisitos:
- Certifique-se explicitamente de que a variável/módulo/camada seja criada apenas uma vez
- Anexe-os explicitamente como atributos de instância, assim como você faria ao definir um módulo ou camada típico
- Reutilize explicitamente o objeto já criado em chamadas subseqüentes
Isso garante que os pesos não sejam criados a cada chamada e sejam reutilizados corretamente. Além disso, isso também garante que os pesos existentes e as perdas de regularização sejam rastreados.
Aqui está um exemplo de como isso pode ficar:
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)>
Observe que o rastreamento explícito do módulo aninhado é necessário mesmo que ele seja decorado com o decorador track_tf1_style_variables
. Isso ocorre porque cada módulo/camada com métodos decorados tem seu próprio armazenamento de variáveis associado a ele.
Os pesos são rastreados corretamente:
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)>]
Assim como a perda de regularização:
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.058749676>]
Observe que se o NestedLayer
fosse um tf.Module
não Keras, as variáveis ainda seriam rastreadas, mas as perdas de regularização não seriam rastreadas automaticamente, então você teria que rastreá-las explicitamente separadamente.
Orientação sobre nomes de variáveis
Chamadas tf.Variable
explícitas e camadas Keras usam um mecanismo de autogeração de nome de camada/nome de variável diferente do qual você pode estar acostumado a partir da combinação de get_variable
e variable_scopes
. Embora o shim faça com que seus nomes de variáveis correspondam às variáveis criadas por get_variable
mesmo ao passar de gráficos TF1.x para execução antecipada do TF2 & tf.function
, ele não pode garantir o mesmo para os nomes de variáveis gerados para chamadas tf.Variable
e camadas Keras que você incorpora em seus decoradores de método. É até possível que várias variáveis compartilhem o mesmo nome na execução antecipada do TF2 e tf.function
.
Você deve ter um cuidado especial com isso ao seguir as seções sobre validação de correção e mapeamento de pontos de verificação TF1.x posteriormente neste guia.
Usando tf.compat.v1.make_template
no método decorado
É altamente recomendado que você use diretamente tf.compat.v1.keras.utils.track_tf1_style_variables
em vez de usar tf.compat.v1.make_template
, pois é uma camada mais fina em cima do TF2 .
Siga as orientações nesta seção para o código TF1.x anterior que já dependia de tf.compat.v1.make_template
.
Como tf.compat.v1.make_template
envolve o código que usa get_variable
, o decorador track_tf1_style_variables
permite que você use esses modelos em chamadas de camada e rastreie com êxito os pesos e as perdas de regularização.
No entanto, certifique-se de chamar make_template
apenas uma vez e, em seguida, reutilize o mesmo modelo em cada chamada de camada. Caso contrário, um novo modelo será criado toda vez que você chamar a camada junto com um novo conjunto de variáveis.
Por exemplo,
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)
Migração incremental para o TF2 nativo
Como mencionado anteriormente, track_tf1_style_variables
permite que você misture tf.Variable
/ tf.keras.layers.Layer
/ tf.Module
orientado a objeto no estilo TF2 com o estilo legado tf.compat.v1.get_variable
/ tf.compat.v1.layers
uso dentro do mesmo módulo/camada decorado.
Isso significa que depois de tornar seu modelo TF1.x totalmente compatível com o TF2, você pode escrever todos os novos componentes do modelo com APIs TF2 nativas (não tf.compat.v1
) e fazer com que eles interoperem com seu código antigo.
No entanto, se você continuar a modificar seus componentes de modelo mais antigos, você também pode optar por alternar incrementalmente seu uso tf.compat.v1
de estilo legado para as APIs orientadas a objetos puramente nativas que são recomendadas para código TF2 recém-escrito.
O uso tf.compat.v1.get_variable
pode ser substituído por chamadas self.add_weight
se você estiver decorando uma camada/modelo Keras, ou por chamadas tf.Variable
se estiver decorando objetos Keras ou tf.Module
s.
Tanto as tf.compat.v1.layers
de estilo funcional quanto as orientadas a objetos geralmente podem ser substituídas pela camada tf.keras.layers
equivalente sem a necessidade de alterações de argumento.
Você também pode considerar partes de seu modelo ou padrões comuns em camadas/módulos individuais durante sua mudança incremental para APIs puramente nativas, que podem usar track_tf1_style_variables
.
Uma nota sobre Slim e contrib.layers
Uma grande quantidade de código TF 1.x mais antigo usa a biblioteca Slim , que foi empacotada com o TF 1.x como tf.contrib.layers
. Converter código usando Slim para TF 2 nativo é mais complicado do que converter v1.layers
. Na verdade, pode fazer sentido converter seu código Slim primeiro para v1.layers
e depois converter para Keras. Abaixo estão algumas orientações gerais para converter o código Slim.
- Certifique-se de que todos os argumentos sejam explícitos. Remova
arg_scopes
se possível. Se você ainda precisar usá-los, dividanormalizer_fn
eactivation_fn
em suas próprias camadas. - As camadas de conversão separáveis são mapeadas para uma ou mais camadas Keras diferentes (camadas Keras em profundidade, em pontos e separáveis).
- Slim e
v1.layers
têm nomes de argumentos e valores padrão diferentes. - Observe que alguns argumentos têm escalas diferentes.
Migração para o TF2 nativo ignorando a compatibilidade do ponto de verificação
O exemplo de código a seguir demonstra uma movimentação incremental de um modelo para APIs puramente nativas sem considerar a compatibilidade do ponto de verificação.
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
Em seguida, substitua as APIs compat.v1
por seus equivalentes nativos orientados a objetos de maneira fragmentada. Comece alternando a camada de convolução para um objeto Keras criado no construtor de camada.
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
Use a classe v1.keras.utils.DeterministicRandomTestTool
para verificar se essa alteração incremental deixa o modelo com o mesmo comportamento de antes.
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())
Agora você substituiu todas as compat.v1.layers
individuais por camadas Keras nativas.
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())
Por fim, remova qualquer uso de variable_scope
restante (não mais necessário) e o próprio decorador track_tf1_style_variables
.
Agora você tem uma versão do modelo que usa APIs totalmente nativas.
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())
Mantendo a compatibilidade do ponto de verificação durante a migração para o TF2 nativo
O processo de migração acima para APIs nativas do TF2 alterou os nomes das variáveis (já que as APIs Keras produzem nomes de peso muito diferentes) e os caminhos orientados a objetos que apontam para pesos diferentes no modelo. O impacto dessas mudanças é que elas quebrarão qualquer ponto de verificação baseado em nome no estilo TF1 existente ou pontos de verificação orientados a objeto no estilo TF2.
No entanto, em alguns casos, você pode pegar seu ponto de verificação baseado em nome original e encontrar um mapeamento das variáveis para seus novos nomes com abordagens como a detalhada no guia Reutilizando pontos de verificação do TF1.x .
Algumas dicas para tornar isso viável são as seguintes:
- As variáveis ainda têm um argumento de
name
que você pode definir. - Os modelos Keras também recebem um argumento de
name
como o prefixo de suas variáveis. - A função
v1.name_scope
pode ser usada para definir prefixos de nomes de variáveis. Isso é muito diferente detf.variable_scope
. Ele afeta apenas nomes e não rastreia variáveis e reutilização.
Com os ponteiros acima em mente, os exemplos de código a seguir demonstram um fluxo de trabalho que você pode adaptar ao seu código para atualizar incrementalmente parte de um modelo enquanto atualiza simultaneamente os pontos de verificação.
- Comece trocando
tf.compat.v1.layers
de estilo funcional para suas versões orientadas a objetos.
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']
- Em seguida, atribua os objetos compat.v1.layer e quaisquer variáveis criadas por
compat.v1.get_variable
como propriedades do objetotf.keras.layers.Layer
/tf.Module
cujo método é decorado comtrack_tf1_style_variables
(observe que qualquer TF2 orientado a objetos os pontos de verificação de estilo agora salvarão um caminho por nome de variável e o novo caminho orientado a objeto).
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']
- Salve novamente um ponto de verificação carregado neste ponto para salvar caminhos pelo nome da variável (para compat.v1.layers) ou pelo gráfico de objeto orientado a objeto.
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
- Agora você pode trocar o
compat.v1.layers
orientado a objeto por camadas Keras nativas enquanto ainda é capaz de carregar o ponto de verificação salvo recentemente. Certifique-se de preservar os nomes das variáveis para ascompat.v1.layers
restantes, ainda gravando osvariable_scopes
gerados automaticamente das camadas substituídas. Essas camadas/variáveis comutadas agora usarão apenas o caminho do atributo do objeto para as variáveis no ponto de verificação em vez do caminho do nome da variável.
Em geral, você pode substituir o uso de compat.v1.get_variable
em variáveis anexadas a propriedades por:
- Mudando-os para usar
tf.Variable
, OR - Atualizando-os usando
tf.keras.layers.Layer.add_weight
. Observe que, se você não estiver alternando todas as camadas de uma só vez, isso pode alterar a nomenclatura de camada/variável gerada automaticamente para ascompat.v1.layers
restantes que não possuem um argumento dename
. Se for esse o caso, você deve manter os nomes de variáveis paracompat.v1.layers
restantes os mesmos abrindo e fechando manualmente umvariable_scope
correspondente ao nome de escopo gerado docompat.v1.layer
removido. Caso contrário, os caminhos dos pontos de verificação existentes podem entrar em conflito e o carregamento do ponto de verificação se comportará incorretamente.
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']
Salvar um checkpoint nesta etapa depois de construir as variáveis fará com que ele contenha apenas os caminhos de objeto disponíveis no momento.
Certifique-se de registrar os escopos dos compat.v1.layers
removidos para preservar os nomes de peso gerados automaticamente para os compat.v1.layers
restantes.
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
- Repita as etapas acima até ter substituído todos os
compat.v1.layers
ecompat.v1.get_variable
s em seu modelo por equivalentes totalmente nativos.
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']
Lembre-se de testar para garantir que o checkpoint recém-atualizado ainda se comporte conforme o esperado. Aplique as técnicas descritas no guia de validação numérica em cada etapa incremental deste processo para garantir que seu código migrado seja executado corretamente.
Manipulação de alterações de comportamento de TF1.x para TF2 não cobertas pelos calços de modelagem
Os calços de modelagem descritos neste guia podem garantir que variáveis, camadas e perdas de regularização criadas com as get_variable
, tf.compat.v1.layers
e variable_scope
continuem a funcionar como antes ao usar execução antecipada e tf.function
, sem ter que confiar em coleções.
Isso não cobre todas as semânticas específicas do TF1.x que seu modelo pode confiar. Em alguns casos, os calços podem ser insuficientes para fazer o seu modelo avançar no TF2 por conta própria. Leia o guia de comportamentos TF1.x vs TF2 para saber mais sobre as diferenças comportamentais entre TF1.xe TF2.