Visualizza su TensorFlow.org | Esegui in Google Colab | Visualizza su GitHub | Scarica quaderno |
Questa guida fornisce una panoramica ed esempi di uno shim di codice di modellazione che puoi utilizzare per utilizzare i tuoi modelli TF1.x esistenti nei flussi di lavoro TF2 come l'esecuzione ansiosa, tf.function
e le strategie di distribuzione con modifiche minime al codice di modellazione.
Ambito di utilizzo
Lo spessore descritto in questa guida è progettato per i modelli TF1.x che si basano su:
-
tf.compat.v1.get_variable
etf.compat.v1.variable_scope
per controllare la creazione e il riutilizzo delle variabili e - API basate sulla raccolta di grafici come
tf.compat.v1.global_variables()
,tf.compat.v1.trainable_variables
,tf.compat.v1.losses.get_regularization_losses()
etf.compat.v1.get_collection()
per tenerne traccia di pesi e perdite di regolarizzazione
Ciò include la maggior parte dei modelli basati sulle API tf.compat.v1.layer
, tf.contrib.layers
e TensorFlow-Slim .
Lo spessore NON è necessario per i seguenti modelli TF1.x:
- Modelli Keras autonomi che tracciano già tutti i loro pesi allenabili e le perdite di regolarizzazione rispettivamente tramite
model.trainable_weights
emodel.losses
. -
tf.Module
s che tracciano già tutti i loro pesi addestrabili tramitemodule.trainable_variables
e creano pesi solo se non sono già stati creati.
È probabile che questi modelli funzionino in TF2 con un'esecuzione ansiosa e tf.function
out-of-the-box.
Impostare
Importa TensorFlow e altre dipendenze.
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
Il decoratore track_tf1_style_variables
Lo shim chiave descritto in questa guida è tf.compat.v1.keras.utils.track_tf1_style_variables
, un decoratore che puoi utilizzare all'interno dei metodi appartenenti a tf.keras.layers.Layer
e tf.Module
per tenere traccia dei pesi e catturare le perdite di regolarizzazione.
La decorazione di un metodo di chiamata tf.keras.layers.Layer
o tf.Module
con tf.compat.v1.keras.utils.track_tf1_style_variables
consente la creazione e il riutilizzo di variabili tramite tf.compat.v1.get_variable
(e per estensione tf.compat.v1.layers
) per funzionare correttamente all'interno del metodo decorato anziché creare sempre una nuova variabile ad ogni chiamata. Farà inoltre sì che il livello o il modulo tenga traccia implicitamente di eventuali pesi creati o a cui si accede tramite get_variable
all'interno del metodo decorato.
Oltre a tenere traccia dei pesi stessi sotto lo standard layer.variable
/ module.variable
/etc. properties, se il metodo appartiene a un tf.keras.layers.Layer
, tutte le perdite di regolarizzazione specificate tramite gli get_variable
o tf.compat.v1.layers
regularizer verranno tracciate dal livello sotto la proprietà layer.losses
standard.
Questo meccanismo di tracciamento consente l'utilizzo di classi di grandi dimensioni di codice model-forward-pass in stile TF1.x all'interno dei livelli Keras o tf.Module
s in TF2 anche con i comportamenti TF2 abilitati.
Esempi di utilizzo
Gli esempi di utilizzo seguenti mostrano gli spessori di modellazione usati per decorare i metodi tf.keras.layers.Layer
, ma tranne quando interagiscono specificamente con le caratteristiche di Keras sono applicabili anche quando si decorano i metodi tf.Module
.
Livello creato con tf.compat.v1.get_variable
Immagina di avere un livello implementato direttamente sopra tf.compat.v1.get_variable
come segue:
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
Usa lo spessore per trasformarlo in un livello e chiamalo sugli input.
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)>
Accedi alle variabili tracciate e alle perdite di regolarizzazione acquisite come un livello Keras standard.
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>]
Per vedere che i pesi vengono riutilizzati ogni volta che chiami il livello, imposta tutti i pesi su zero e chiama di nuovo il livello.
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)>
Puoi anche usare il livello convertito direttamente nella costruzione del modello funzionale 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>]
Modello realizzato con tf.compat.v1.layers
Immagina di avere un livello o un modello implementato direttamente sopra tf.compat.v1.layers
come segue:
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
Usa lo spessore per trasformarlo in un livello e chiamalo sugli input.
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)>
Accedi alle variabili tracciate e cattura le perdite di regolarizzazione come un livello Keras standard.
layer.trainable_variables
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.03623246>, <tf.Tensor: shape=(), dtype=float32, numpy=0.14618248>]
Per vedere che i pesi vengono riutilizzati ogni volta che chiami il livello, imposta tutti i pesi su zero e chiama di nuovo il livello.
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)>
Puoi anche usare il livello convertito direttamente nella costruzione del modello funzionale 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>]
Acquisisci aggiornamenti della normalizzazione batch e argomenti training
del modello
In TF1.x, esegui la normalizzazione batch in questo modo:
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])
Notare che:
- Gli aggiornamenti della media mobile della normalizzazione batch vengono tracciati da
get_collection
che è stato chiamato separatamente dal livello -
tf.compat.v1.layers.batch_normalization
richiede un argomento ditraining
(generalmente chiamatois_training
quando si utilizzano i livelli di normalizzazione batch TF-Slim)
In TF2, a causa dell'esecuzione ansiosa e delle dipendenze di controllo automatico, gli aggiornamenti della media mobile di normalizzazione batch verranno eseguiti immediatamente. Non è necessario raccoglierli separatamente dalla raccolta degli aggiornamenti e aggiungerli come dipendenze di controllo esplicite.
Inoltre, se assegni un argomento di training
al metodo di passaggio in avanti di tf.keras.layers.Layer
, Keras sarà in grado di passare la fase di addestramento corrente e tutti i livelli nidificati ad essa proprio come fa per qualsiasi altro livello. Consulta i documenti API per tf.keras.Model
per ulteriori informazioni su come Keras gestisce l'argomento di training
.
Se stai decorando i metodi tf.Module
, devi assicurarti di passare manualmente tutti gli argomenti di training
secondo necessità. Tuttavia, gli aggiornamenti della media mobile della normalizzazione batch verranno comunque applicati automaticamente senza la necessità di dipendenze di controllo esplicite.
I frammenti di codice seguenti mostrano come incorporare i livelli di normalizzazione batch nello shim e come funziona il suo utilizzo in un modello Keras (applicabile 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)>}
Riutilizzo di variabili basate su ambito variabile
Qualsiasi creazione di variabili nel passaggio in avanti basato su get_variable
manterrà la stessa denominazione delle variabili e riutilizzerà la semantica degli ambiti delle variabili in TF1.x. Questo è vero finché hai almeno un ambito esterno non vuoto per qualsiasi tf.compat.v1.layers
con nomi generati automaticamente, come menzionato sopra.
Esecuzione desiderosa e tf.function
Come visto sopra, i metodi decorati per tf.keras.layers.Layer
e tf.Module
eseguiti all'interno dell'esecuzione desiderosa e sono anche compatibili con tf.function
. Ciò significa che puoi utilizzare pdb e altri strumenti interattivi per scorrere il tuo passaggio in avanti mentre è in esecuzione.
Strategie distributive
Le chiamate a get_variable
all'interno di @track_tf1_style_variables
-i metodi di livello o modulo decorati utilizzano le creazioni di variabili tf.Variable
standard sotto il cofano. Ciò significa che puoi usarli con le varie strategie di distribuzione disponibili con tf.distribute
come MirroredStrategy
e TPUStrategy
.
Nidificazione di tf.Variable
s, tf.Module
s, tf.keras.layers
e tf.keras.models
in chiamate decorate
La decorazione della chiamata di livello in tf.compat.v1.keras.utils.track_tf1_style_variables
aggiungerà solo il tracciamento implicito automatico delle variabili create (e riutilizzate) tramite tf.compat.v1.get_variable
. Non catturerà i pesi creati direttamente dalle chiamate tf.Variable
, come quelle utilizzate dai livelli Keras tipici e dalla maggior parte dei tf.Module
s. Questa sezione descrive come gestire questi casi nidificati.
(Usi preesistenti) tf.keras.layers
e tf.keras.models
Per utilizzi preesistenti di livelli e modelli Keras nidificati, utilizzare tf.compat.v1.keras.utils.get_or_create_layer
. Questo è consigliato solo per facilitare la migrazione degli utilizzi Keras nidificati TF1.x esistenti; il nuovo codice dovrebbe utilizzare l'impostazione esplicita dell'attributo come descritto di seguito per tf.Variables e tf.Modules.
Per utilizzare tf.compat.v1.keras.utils.get_or_create_layer
, avvolgi il codice che costruisce il tuo modello nidificato in un metodo e passalo al metodo. Esempio:
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)>
Questo metodo garantisce che questi livelli nidificati vengano correttamente riutilizzati e monitorati da tensorflow. Nota che il decoratore @track_tf1_style_variables
è ancora richiesto sul metodo appropriato. Il metodo del generatore di modelli passato a get_or_create_layer
(in questo caso, self.build_model
), non dovrebbe accettare argomenti.
I pesi sono tracciati:
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 anche la perdita di regolarizzazione:
tf.add_n(layer.losses)
<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.5], dtype=float32)>
Migrazione incrementale: tf.Variables
e tf.Modules
Se devi incorporare le chiamate tf.Variable
o tf.Module
s nei tuoi metodi decorati (ad esempio, se stai seguendo la migrazione incrementale alle API TF2 non legacy descritte più avanti in questa guida), devi comunque tenerne traccia in modo esplicito, con i seguenti requisiti:
- Assicurati esplicitamente che la variabile/modulo/livello venga creata una sola volta
- Allegali esplicitamente come attributi di istanza proprio come faresti quando definisci un modulo o un livello tipico
- Riutilizza in modo esplicito l'oggetto già creato nelle chiamate successive
Ciò garantisce che i pesi non vengano creati nuovi ad ogni chiamata e vengano riutilizzati correttamente. Inoltre, ciò garantisce anche il monitoraggio dei pesi esistenti e delle perdite di regolarizzazione.
Ecco un esempio di come potrebbe apparire:
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)>
Si noti che è necessario un tracciamento esplicito del modulo annidato anche se è decorato con il decoratore track_tf1_style_variables
. Questo perché ogni modulo/strato con metodi decorati ha il proprio archivio di variabili ad esso associato.
I pesi sono correttamente tracciati:
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)>]
Oltre alla perdita di regolarizzazione:
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.058749676>]
Nota che se NestedLayer
fosse invece un tf.Module
non Keras, le variabili verrebbero comunque tracciate ma le perdite di regolarizzazione non verrebbero tracciate automaticamente, quindi dovresti tracciarle esplicitamente separatamente.
Guida ai nomi delle variabili
Le chiamate esplicite tf.Variable
e i livelli Keras utilizzano un meccanismo di generazione automatica del nome del livello/nome della variabile diverso da quello a cui potresti essere abituato dalla combinazione di get_variable
e variable_scopes
. Sebbene lo shim farà corrispondere i nomi delle variabili alle variabili create da get_variable
anche quando si passa dai grafici TF1.x all'esecuzione desiderosa di TF2 & tf.function
, non può garantire lo stesso per i nomi delle variabili generati per le chiamate tf.Variable
e i livelli Keras che incorpori nei tuoi decoratori di metodi. È anche possibile che più variabili condividano lo stesso nome nell'esecuzione desiderosa di TF2 e tf.function
.
Dovresti prestare particolare attenzione a questo quando segui le sezioni sulla convalida della correttezza e sulla mappatura dei checkpoint TF1.x più avanti in questa guida.
Usando tf.compat.v1.make_template
nel metodo decorato
Si consiglia vivamente di utilizzare direttamente tf.compat.v1.keras.utils.track_tf1_style_variables
invece di utilizzare tf.compat.v1.make_template
, poiché è uno strato più sottile sopra TF2 .
Segui le indicazioni in questa sezione per il codice TF1.x precedente che si basava già su tf.compat.v1.make_template
.
Poiché tf.compat.v1.make_template
wrapping del codice che utilizza get_variable
, il decoratore track_tf1_style_variables
consente di utilizzare questi modelli nelle chiamate di livello e di monitorare correttamente i pesi e le perdite di regolarizzazione.
Tuttavia, assicurati di chiamare make_template
solo una volta e quindi riutilizza lo stesso modello in ogni chiamata di livello. In caso contrario, verrà creato un nuovo modello ogni volta che chiami il livello insieme a un nuovo insieme di variabili.
Per esempio,
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)
Migrazione incrementale a Native TF2
Come accennato in precedenza, track_tf1_style_variables
consente di combinare l'utilizzo orientato agli oggetti in stile TF2 tf.Variable
/ tf.keras.layers.Layer
/ tf.Module
con l'utilizzo legacy tf.compat.v1.get_variable
/ tf.compat.v1.layers
utilizzo all'interno dello stesso modulo/strato decorato.
Ciò significa che dopo aver reso il modello TF1.x completamente compatibile con TF2, è possibile scrivere tutti i nuovi componenti del modello con API TF2 native (non tf.compat.v1
) e farle interagire con il codice precedente.
Tuttavia, se si continua a modificare i componenti del modello precedente, è anche possibile scegliere di passare in modo incrementale l'utilizzo di tf.compat.v1
in stile legacy alle API orientate agli oggetti puramente native consigliate per il codice TF2 appena scritto.
L'utilizzo di tf.compat.v1.get_variable
può essere sostituito con chiamate self.add_weight
se stai decorando un livello/modello Keras, o con chiamate tf.Variable
se stai decorando oggetti Keras o tf.Module
s.
Sia tf.compat.v1.layers
in stile funzionale che orientato agli oggetti possono generalmente essere sostituiti con il livello tf.keras.layers
equivalente senza che siano necessarie modifiche agli argomenti.
Puoi anche considerare parti del tuo modello o modelli comuni in singoli livelli/moduli durante il tuo passaggio incrementale alle API puramente native, che a loro volta potrebbero utilizzare track_tf1_style_variables
.
Una nota su Slim e contrib.layers
Una grande quantità di codice TF 1.x precedente utilizza la libreria Slim , che è stata inclusa in TF 1.x come tf.contrib.layers
. La conversione del codice utilizzando Slim in TF 2 nativo è più complicata della conversione di v1.layers
. In effetti, potrebbe avere senso convertire prima il codice Slim in v1.layers
, quindi convertirlo in Keras. Di seguito sono riportate alcune indicazioni generali per la conversione del codice Slim.
- Assicurati che tutti gli argomenti siano espliciti. Rimuovi
arg_scopes
se possibile. Se hai ancora bisogno di usarli, dividinormalizer_fn
eactivation_fn
nei loro livelli. - I livelli di conv separabili vengono mappati su uno o più livelli Keras diversi (livelli Keras in profondità, a punti e separabili).
- Slim e
v1.layers
hanno nomi di argomenti e valori predefiniti diversi. - Si noti che alcuni argomenti hanno scale diverse.
Migrazione a Native TF2 ignorando la compatibilità del checkpoint
Nell'esempio di codice seguente viene illustrato uno spostamento incrementale di un modello in API puramente native senza considerare la compatibilità del checkpoint.
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
Quindi, sostituisci le API compat.v1
con i loro equivalenti orientati agli oggetti nativi in modo a tratti. Inizia cambiando il livello di convoluzione in un oggetto Keras creato nel costruttore del livello.
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
Utilizzare la classe v1.keras.utils.DeterministicRandomTestTool
per verificare che questa modifica incrementale lasci il modello con lo stesso comportamento di prima.
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())
Ora hai sostituito tutti i singoli compat.v1.layers
con livelli Keras nativi.
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())
Infine, rimuovi sia l'eventuale utilizzo rimanente (non più necessario) di variable_scope
che il decoratore track_tf1_style_variables
stesso.
Ora ti rimane una versione del modello che utilizza API interamente native.
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())
Mantenimento della compatibilità dei checkpoint durante la migrazione a Native TF2
Il processo di migrazione precedente alle API TF2 native ha modificato sia i nomi delle variabili (poiché le API Keras producono nomi di peso molto diversi) sia i percorsi orientati agli oggetti che puntano a pesi diversi nel modello. L'impatto di queste modifiche è che avranno rotto sia i checkpoint basati sul nome in stile TF1 esistenti che i checkpoint orientati agli oggetti in stile TF2.
Tuttavia, in alcuni casi, potresti essere in grado di prendere il tuo checkpoint originale basato sul nome e trovare una mappatura delle variabili sui loro nuovi nomi con approcci come quello dettagliato nella guida ai checkpoint di riutilizzo di TF1.x .
Alcuni suggerimenti per renderlo fattibile sono i seguenti:
- Le variabili hanno ancora tutte un argomento del
name
che puoi impostare. - I modelli Keras prendono anche un argomento del
name
come prefisso per le loro variabili. - La funzione
v1.name_scope
può essere utilizzata per impostare i prefissi dei nomi delle variabili. Questo è molto diverso datf.variable_scope
. Influisce solo sui nomi e non tiene traccia delle variabili e del riutilizzo.
Tenendo presente i suggerimenti precedenti, gli esempi di codice seguenti mostrano un flusso di lavoro che puoi adattare al tuo codice per aggiornare in modo incrementale parte di un modello aggiornando contemporaneamente i checkpoint.
- Inizia passando da
tf.compat.v1.layers
in stile funzionale alle loro versioni orientate agli oggetti.
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']
- Quindi, assegna gli oggetti compat.v1.layer e qualsiasi variabile creata da
compat.v1.get_variable
come proprietà dell'oggettotf.keras.layers.Layer
/tf.Module
il cui metodo è decorato contrack_tf1_style_variables
(nota che qualsiasi TF2 orientato agli oggetti i checkpoint di stile ora salveranno sia un percorso in base al nome della variabile che il nuovo percorso orientato agli oggetti).
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']
- Salva nuovamente un checkpoint caricato a questo punto per salvare i percorsi sia in base al nome della variabile (per compat.v1.layers), sia in base al grafico dell'oggetto orientato agli oggetti.
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
- Ora puoi sostituire i
compat.v1.layers
orientati agli oggetti con i livelli Keras nativi mentre sei ancora in grado di caricare il checkpoint salvato di recente. Assicurati di preservare i nomi delle variabili per i restanticompat.v1.layers
continuando a registrare glivariable_scopes
generati automaticamente dei livelli sostituiti. Questi livelli/variabili commutati ora utilizzeranno solo il percorso dell'attributo dell'oggetto per le variabili nel checkpoint invece del percorso del nome della variabile.
In generale, puoi sostituire l'utilizzo di compat.v1.get_variable
nelle variabili allegate alle proprietà con:
- Passandoli all'utilizzo di
tf.Variable
, OR - Aggiornandoli utilizzando
tf.keras.layers.Layer.add_weight
. Nota che se non stai cambiando tutti i livelli in una volta, questo potrebbe cambiare la denominazione di livello/variabile generata automaticamente per i restanticompat.v1.layers
cui manca un argomento delname
. In tal caso, è necessario mantenere invariati i nomi delle variabili percompat.v1.layers
rimanenti aprendo e chiudendo manualmente unvariable_scope
corrispondente al nome dell'ambito generato dacompat.v1.layer
rimosso. In caso contrario, i percorsi dei checkpoint esistenti potrebbero entrare in conflitto e il caricamento dei checkpoint si comporterà in modo errato.
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']
Il salvataggio di un checkpoint in questo passaggio dopo aver costruito le variabili lo farà contenere solo i percorsi degli oggetti attualmente disponibili.
Assicurati di registrare gli ambiti dei compat.v1.layers
rimossi per preservare i nomi dei pesi generati automaticamente per i restanti 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
- Ripeti i passaggi precedenti finché non hai sostituito tutti i
compat.v1.layers
ecompat.v1.get_variable
nel tuo modello con equivalenti completamente nativi.
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']
Ricorda di testare per assicurarti che il checkpoint appena aggiornato si comporti ancora come previsto. Applicare le tecniche descritte nella guida alla convalida della correttezza numerica in ogni fase incrementale di questo processo per garantire che il codice migrato venga eseguito correttamente.
Gestione delle modifiche al comportamento da TF1.x a TF2 non coperte dagli spessori di modellazione
Gli spessori di modellazione descritti in questa guida possono garantire che le variabili, i livelli e le perdite di regolarizzazione create con get_variable
, tf.compat.v1.layers
e la semantica di variable_scope
continuino a funzionare come prima quando si utilizza l'esecuzione desiderosa e tf.function
, senza dover affidarsi alle collezioni.
Questo non copre tutta la semantica specifica di TF1.x su cui il tuo modello passa in avanti potrebbe fare affidamento. In alcuni casi, gli spessori potrebbero non essere sufficienti per far funzionare il tuo modello in avanti in TF2 da solo. Leggi la guida ai comportamenti TF1.x vs TF2 per saperne di più sulle differenze comportamentali tra TF1.x e TF2.