Посмотреть на TensorFlow.org | Запустить в Google Colab | Посмотреть на GitHub | Скачать блокнот |
В этом руководстве представлен обзор и примеры оболочки кода моделирования , которую вы можете использовать для использования существующих моделей TF1.x в рабочих процессах TF2, таких как ускоренное выполнение, tf.function
и стратегии распространения, с минимальными изменениями в коде моделирования.
Сфера использования
Прокладка, описанная в этом руководстве, предназначена для моделей TF1.x, которые полагаются на:
-
tf.compat.v1.get_variable
иtf.compat.v1.variable_scope
для управления созданием и повторным использованием переменных, а также - API на основе сбора графов, такие как
tf.compat.v1.global_variables()
,tf.compat.v1.trainable_variables
,tf.compat.v1.losses.get_regularization_losses()
иtf.compat.v1.get_collection()
для отслеживания весов и потерь регуляризации
Это включает в себя большинство моделей, построенных на основе API tf.compat.v1.layer
, tf.contrib.layers
и TensorFlow-Slim .
Прокладка НЕ требуется для следующих моделей TF1.x:
- Автономные модели Keras, которые уже отслеживают все свои обучаемые веса и потери регуляризации через
model.trainable_weights
иmodel.losses
соответственно. -
tf.Module
, которые уже отслеживают все свои обучаемые веса черезmodule.trainable_variables
и создают веса только в том случае, если они еще не созданы.
Эти модели, вероятно, будут работать в TF2 с немедленным выполнением и tf.function
s.
Настраивать
Импортируйте TensorFlow и другие зависимости.
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
Декоратор track_tf1_style_variables
Ключевой прокладкой, описанной в этом руководстве, является tf.compat.v1.keras.utils.track_tf1_style_variables
, декоратор, который вы можете использовать в методах, принадлежащих tf.keras.layers.Layer
и tf.Module
, для отслеживания весов в стиле TF1.x и фиксировать потери регуляризации.
Украшение методов вызова tf.keras.layers.Layer
или tf.Module
с помощью tf.compat.v1.keras.utils.track_tf1_style_variables
позволяет создавать и повторно использовать переменные через tf.compat.v1.get_variable
(и, как расширение, tf.compat.v1.layers
) для правильной работы внутри декорированного метода, а не всегда создавать новую переменную при каждом вызове. Это также заставит слой или модуль неявно отслеживать любые веса, созданные или доступные через get_variable
внутри декорированного метода.
Кроме отслеживания самих весов по стандарту layer.variable
/ module.variable
/etc. properties, если метод принадлежит tf.keras.layers.Layer
, то любые потери регуляризации, указанные с помощью аргументов регуляризатора get_variable
или tf.compat.v1.layers
, будут отслеживаться слоем в соответствии со стандартным свойством layer.losses
.
Этот механизм отслеживания позволяет использовать большие классы кода прямого прохода модели в стиле TF1.x внутри слоев Keras или tf.Module
s в TF2 даже при включенном поведении TF2.
Примеры использования
Приведенные ниже примеры использования демонстрируют моделирующие прокладки, используемые для оформления методов tf.keras.layers.Layer
, но за исключением случаев, когда они специально взаимодействуют с функциями Keras, они также применимы при декорировании методов tf.Module
.
Слой, созданный с помощью tf.compat.v1.get_variable
Представьте, что у вас есть слой, реализованный непосредственно поверх tf.compat.v1.get_variable
следующим образом:
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
Используйте прокладку, чтобы превратить ее в слой и вызывать на входах.
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)>
Получите доступ к отслеживаемым переменным и захваченным потерям регуляризации, как к стандартному слою Keras.
layer.trainable_variables
layer.losses
2021-12-04 02:24:42.941890: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them. [<tf.Tensor: shape=(), dtype=float32, numpy=0.10789324>]
Чтобы увидеть, что веса повторно используются каждый раз, когда вы вызываете слой, установите все веса равными нулю и снова вызовите слой.
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)>
Вы также можете использовать преобразованный слой непосредственно в построении функциональной модели 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>]
Модель построена с помощью tf.compat.v1.layers
Представьте, что у вас есть слой или модель, реализованная непосредственно поверх tf.compat.v1.layers
следующим образом:
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
Используйте прокладку, чтобы превратить ее в слой и вызывать на входах.
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)>
Получите доступ к отслеживаемым переменным и зафиксируйте потери регуляризации, как стандартный слой Keras.
layer.trainable_variables
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.03623246>, <tf.Tensor: shape=(), dtype=float32, numpy=0.14618248>]
Чтобы увидеть, что веса повторно используются каждый раз, когда вы вызываете слой, установите все веса равными нулю и снова вызовите слой.
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)>
Вы также можете использовать преобразованный слой непосредственно в построении функциональной модели 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>]
Захват обновлений пакетной нормализации и аргументов training
модели
В TF1.x вы выполняете пакетную нормализацию следующим образом:
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])
Обратите внимание, что:
- Обновления скользящей средней пакетной нормализации отслеживаются
get_collection
, которая вызывается отдельно от слоя. -
tf.compat.v1.layers.batch_normalization
требует аргументаtraining
(обычно называетсяis_training
при использовании слоев пакетной нормализации TF-Slim)
В TF2, из-за быстрого выполнения и зависимостей автоматического управления, обновления скользящего среднего пакетной нормализации будут выполняться сразу. Нет необходимости отдельно собирать их из коллекции обновлений и добавлять как явные зависимости управления.
Кроме того, если вы дадите методу прямого прохода tf.keras.layers.Layer
training
аргумент, Keras сможет передать ему текущую тренировочную фазу и любые вложенные слои, как и для любого другого слоя. См. документы API для tf.keras.Model
для получения дополнительной информации о том, как Keras обрабатывает training
аргумент.
Если вы декорируете методы tf.Module
, вам необходимо вручную передать все training
аргументы по мере необходимости. Однако обновления скользящего среднего пакетной нормализации по-прежнему будут применяться автоматически без необходимости в явных управляющих зависимостях.
Следующие фрагменты кода демонстрируют, как внедрять слои пакетной нормализации в прокладку и как работает ее использование в модели Keras (применимо к 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)>}
Повторное использование переменных на основе переменных
Любые создания переменных в прямом проходе на основе get_variable
будут поддерживать те же имена переменных и семантику повторного использования, что и области видимости переменных в TF1.x. Это верно до тех пор, пока у вас есть хотя бы одна непустая внешняя область для любого tf.compat.v1.layers
с автоматически сгенерированными именами, как упоминалось выше.
Нетерпеливое исполнение и tf.function
Как видно выше, декорированные методы для tf.keras.layers.Layer
и tf.Module
запускаются внутри нетерпеливого выполнения и также совместимы с tf.function
. Это означает, что вы можете использовать pdb и другие интерактивные инструменты для пошагового выполнения прямого прохода во время его выполнения.
Стратегии распространения
Вызовы get_variable
внутри @track_tf1_style_variables
методы слоя или модуля используют стандартное создание переменных tf.Variable
под капотом. Это означает, что вы можете использовать их с различными стратегиями распространения, доступными в tf.distribute
, такими как MirroredStrategy
и TPUStrategy
.
Вложение tf.Variable
s, tf.Module
s, tf.keras.layers
и tf.keras.models
в декорированные вызовы
Украшение вызова вашего слоя в tf.compat.v1.keras.utils.track_tf1_style_variables
добавит только автоматическое неявное отслеживание переменных, созданных (и повторно используемых) через tf.compat.v1.get_variable
. Он не будет захватывать веса, непосредственно созданные вызовами tf.Variable
, например те, которые используются типичными слоями Keras и большинством tf.Module
s. В этом разделе описывается, как обрабатывать эти вложенные случаи.
(Ранее существовавшие варианты использования) tf.keras.layers
и tf.keras.models
Для уже существующих применений вложенных слоев и моделей Keras используйте tf.compat.v1.keras.utils.get_or_create_layer
. Это рекомендуется только для упрощения миграции существующих вложенных способов использования Keras в TF1.x; новый код должен использовать явную настройку атрибута, как описано ниже для tf.Variables и tf.Modules.
Чтобы использовать tf.compat.v1.keras.utils.get_or_create_layer
, оберните код, создающий вложенную модель, в метод и передайте его методу. Пример:
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)>
Этот метод гарантирует, что эти вложенные слои правильно повторно используются и отслеживаются tensorflow. Обратите внимание, что декоратор @track_tf1_style_variables
по-прежнему требуется для соответствующего метода. Метод построителя модели, переданный в get_or_create_layer
(в данном случае, self.build_model
), не должен принимать аргументов.
Веса отслеживаются:
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)>]
А также потеря регуляризации:
tf.add_n(layer.losses)
<tf.Tensor: shape=(1,), dtype=float32, numpy=array([0.5], dtype=float32)>
Инкрементная миграция: tf.Variables
и tf.Modules
Если вам нужно встроить вызовы tf.Variable
или tf.Module
в декорированные методы (например, если вы выполняете инкрементную миграцию на неустаревшие API TF2, описанные далее в этом руководстве), вам все равно необходимо явно отслеживать их, со следующими требованиями:
- Явно убедитесь, что переменная/модуль/слой создается только один раз
- Явно прикрепите их как атрибуты экземпляра, как если бы вы определяли типичный модуль или слой .
- Явно повторно использовать уже созданный объект в последующих вызовах
Это гарантирует, что веса не создаются новые при каждом вызове и правильно используются повторно. Кроме того, это также гарантирует отслеживание существующих весов и потерь при регуляризации.
Вот пример того, как это может выглядеть:
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)>
Обратите внимание, что требуется явное отслеживание вложенного модуля, даже если он украшен декоратором track_tf1_style_variables
. Это связано с тем, что каждый модуль/уровень с декорированными методами имеет связанное с ним собственное хранилище переменных.
Веса правильно отслеживаются:
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)>]
А также потеря регуляризации:
layer.losses
[<tf.Tensor: shape=(), dtype=float32, numpy=0.058749676>]
Обратите внимание, что если бы NestedLayer
был не- tf.Module
вместо этого, переменные все равно отслеживались бы, но потери регуляризации не отслеживались бы автоматически, поэтому вам пришлось бы явно отслеживать их отдельно.
Руководство по именам переменных
Явные вызовы tf.Variable
и слои Keras используют другой механизм автогенерации имени слоя/переменной, чем тот, к которому вы могли привыкнуть из комбинации get_variable
и variable_scopes
. Хотя прокладка сделает ваши имена переменных совпадающими с переменными, созданными get_variable
, даже при переходе от графиков TF1.x к нетерпеливому выполнению TF2 и tf.function
, она не может гарантировать то же самое для имен переменных, сгенерированных для вызовов tf.Variable
и слоев Keras, которые вы встраиваете в свои декораторы методов. Несколько переменных даже могут иметь одно и то же имя в TF2 с быстрым выполнением и tf.function
.
Вы должны проявлять особую осторожность при выполнении разделов о проверке правильности и отображении контрольных точек TF1.x далее в этом руководстве.
Использование tf.compat.v1.make_template
в декорированном методе
Настоятельно рекомендуется напрямую использовать tf.compat.v1.keras.utils.track_tf1_style_variables
вместо tf.compat.v1.make_template
, так как это более тонкий слой поверх TF2 .
Следуйте инструкциям в этом разделе для предыдущего кода TF1.x, который уже полагался на tf.compat.v1.make_template
.
Поскольку tf.compat.v1.make_template
оборачивает код, который использует get_variable
, декоратор track_tf1_style_variables
позволяет вам использовать эти шаблоны в вызовах слоев и успешно отслеживать веса и потери регуляризации.
Однако обязательно вызовите make_template
только один раз, а затем повторно используйте один и тот же шаблон в каждом вызове слоя. В противном случае каждый раз при вызове слоя будет создаваться новый шаблон с новым набором переменных.
Например,
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)
Инкрементная миграция на Native TF2
Как упоминалось ранее, track_tf1_style_variables
позволяет вам смешивать объектно-ориентированное использование tf.Variable
/ tf.keras.layers.Layer
/ tf.Module
в стиле TF2 с использованием устаревшего tf.compat.v1.get_variable
/ tf.compat.v1.layers
использование внутри одного и того же декорированного модуля/слоя.
Это означает, что после того, как вы сделали свою модель TF1.x полностью совместимой с TF2, вы можете написать все новые компоненты модели с собственными (не tf.compat.v1
) API TF2 и заставить их взаимодействовать с вашим старым кодом.
Однако, если вы продолжаете модифицировать свои старые компоненты модели, вы также можете постепенно переключать использование tf.compat.v1
в устаревшем стиле на чисто родные объектно-ориентированные API, которые рекомендуются для вновь написанного кода TF2.
Использование tf.compat.v1.get_variable
можно заменить либо self.add_weight
, если вы украшаете слой/модель Keras, либо вызовами tf.Variable
, если вы украшаете объекты Keras или tf.Module
s.
Как функциональный, так и объектно-ориентированный tf.compat.v1.layers
обычно можно заменить эквивалентным слоем tf.keras.layers
без необходимости изменения аргументов.
Вы также можете рассматривать части вашей модели или общие шаблоны в отдельных слоях/модулях во время постепенного перехода к чисто нативным API, которые сами могут использовать track_tf1_style_variables
.
Примечание о Slim и contrib.layers
Большое количество старого кода TF 1.x использует библиотеку Slim , которая была упакована с TF 1.x как tf.contrib.layers
. Преобразование кода с использованием Slim в собственный TF 2 требует больше усилий, чем преобразование v1.layers
. На самом деле, может иметь смысл сначала преобразовать код Slim в v1.layers
, а затем преобразовать в Keras. Ниже приведены некоторые общие рекомендации по преобразованию кода Slim.
- Убедитесь, что все аргументы являются явными. Удалите
arg_scopes
, если это возможно. Если вам все еще нужно их использовать, разделитеnormalizer_fn
иactivation_fn
на отдельные слои. - Разделяемые конверсионные слои сопоставляются с одним или несколькими различными слоями Keras (по глубине, точечными и разделяемыми слоями Keras).
- Slim и
v1.layers
имеют разные имена аргументов и значения по умолчанию. - Обратите внимание, что некоторые аргументы имеют разную шкалу.
Миграция на Native TF2 с игнорированием совместимости контрольных точек
В следующем образце кода демонстрируется поэтапный перенос модели на чисто собственные API-интерфейсы без учета совместимости контрольных точек.
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
Затем постепенно замените API-интерфейсы compat.v1
их собственными объектно-ориентированными эквивалентами. Начните с переключения слоя свертки на объект Keras, созданный в конструкторе слоев.
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
Используйте класс v1.keras.utils.DeterministicRandomTestTool
, чтобы убедиться, что это добавочное изменение оставляет модель с тем же поведением, что и раньше.
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())
Теперь вы заменили все отдельные compat.v1.layers
собственными слоями Keras.
class NearlyFullyNativeModel(tf.keras.layers.Layer):
def __init__(self, units, *args, **kwargs):
super().__init__(*args, **kwargs)
self.units = units
self.conv_layer = tf.keras.layers.Conv2D(
3, 3,
kernel_regularizer="l2")
self.flatten_layer = tf.keras.layers.Flatten()
self.dense_layer = tf.keras.layers.Dense(
self.units,
kernel_regularizer="l2")
@tf.compat.v1.keras.utils.track_tf1_style_variables
def call(self, inputs):
with tf.compat.v1.variable_scope('model'):
out = self.conv_layer(inputs)
out = self.flatten_layer(out)
out = self.dense_layer(out)
return out
random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
layer = NearlyFullyNativeModel(10)
inputs = tf.random.normal(shape=(10, 5, 5, 5))
migrated_output = layer(inputs)
# Grab the regularization loss as well
migrated_regularization_loss = tf.math.add_n(layer.losses)
print(migrated_regularization_loss)
tf.Tensor(0.17953834, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
np.testing.assert_allclose(original_regularization_loss.numpy(), migrated_regularization_loss.numpy())
np.testing.assert_allclose(original_output.numpy(), migrated_output.numpy())
Наконец, удалите оставшееся (больше не нужное) использование variable_scope
и сам декоратор track_tf1_style_variables
.
Теперь у вас осталась версия модели, в которой используются полностью собственные API.
class FullyNativeModel(tf.keras.layers.Layer):
def __init__(self, units, *args, **kwargs):
super().__init__(*args, **kwargs)
self.units = units
self.conv_layer = tf.keras.layers.Conv2D(
3, 3,
kernel_regularizer="l2")
self.flatten_layer = tf.keras.layers.Flatten()
self.dense_layer = tf.keras.layers.Dense(
self.units,
kernel_regularizer="l2")
def call(self, inputs):
out = self.conv_layer(inputs)
out = self.flatten_layer(out)
out = self.dense_layer(out)
return out
random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
layer = FullyNativeModel(10)
inputs = tf.random.normal(shape=(10, 5, 5, 5))
migrated_output = layer(inputs)
# Grab the regularization loss as well
migrated_regularization_loss = tf.math.add_n(layer.losses)
print(migrated_regularization_loss)
tf.Tensor(0.17953834, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
np.testing.assert_allclose(original_regularization_loss.numpy(), migrated_regularization_loss.numpy())
np.testing.assert_allclose(original_output.numpy(), migrated_output.numpy())
Поддержание совместимости контрольных точек при переходе на Native TF2
Описанный выше процесс миграции на собственные API-интерфейсы TF2 изменил как имена переменных (поскольку API-интерфейсы Keras создают очень разные имена весов), так и объектно-ориентированные пути, указывающие на разные веса в модели. Последствия этих изменений заключаются в том, что они нарушат как существующие контрольные точки на основе имен в стиле TF1, так и объектно-ориентированные контрольные точки в стиле TF2.
Однако в некоторых случаях вы можете взять исходную контрольную точку на основе имени и найти сопоставление переменных с их новыми именами с помощью подходов, подобных описанному в руководстве по повторному использованию контрольных точек TF1.x.
Вот несколько советов, как сделать это возможным:
- Все переменные по-прежнему имеют аргумент
name
, который вы можете установить. - Модели Keras также принимают аргумент
name
, который они устанавливают в качестве префикса для своих переменных. -
v1.name_scope
можно использовать для установки префиксов имен переменных. Это сильно отличается отtf.variable_scope
. Он влияет только на имена и не отслеживает переменные и повторное использование.
С учетом приведенных выше указаний следующие примеры кода демонстрируют рабочий процесс, который вы можете адаптировать к своему коду для постепенного обновления части модели при одновременном обновлении контрольных точек.
- Начните с переключения функциональных
tf.compat.v1.layers
на их объектно-ориентированные версии.
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']
- Затем назначьте объекты compat.v1.layer и любые переменные, созданные
compat.v1.get_variable
, как свойства объектаtf.keras.layers.Layer
/tf.Module
, чей метод украшенtrack_tf1_style_variables
(обратите внимание, что любой объектно-ориентированный TF2 контрольные точки стиля теперь будут сохранять как путь по имени переменной, так и новый объектно-ориентированный путь).
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']
- Пересохраните загруженную контрольную точку в этой точке, чтобы сохранить пути как по имени переменной (для compat.v1.layers), так и по объектно-ориентированному графу объектов.
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
- Теперь вы можете заменить объектно-ориентированные
compat.v1.layers
на нативные слои Keras, сохраняя при этом возможность загрузки недавно сохраненной контрольной точки. Убедитесь, что вы сохранили имена переменных для остальныхcompat.v1.layers
, по-прежнему записывая автоматически сгенерированныеvariable_scopes
замененных слоев. Эти переключаемые слои/переменные теперь будут использовать только путь атрибута объекта к переменным в контрольной точке вместо пути имени переменной.
Как правило, вы можете заменить использование compat.v1.get_variable
в переменных, прикрепленных к свойствам, следующим образом:
- Переключение их на использование
tf.Variable
, ИЛИ - Обновление их с помощью
tf.keras.layers.Layer.add_weight
. Обратите внимание, что если вы не переключаете все слои за один раз, это может изменить автоматически сгенерированное имя слоя/переменной для остальныхcompat.v1.layers
, в которых отсутствует аргументname
. Если это так, вы должны сохранить имена переменных для оставшихсяcompat.v1.layers
же, вручную открывая и закрываяvariable_scope
, соответствующий сгенерированному имени области удаленногоcompat.v1.layer
. В противном случае пути от существующих чекпоинтов могут конфликтовать, и загрузка чекпоинтов будет вестись некорректно.
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']
Сохранение контрольной точки на этом шаге после создания переменных приведет к тому, что она будет содержать только доступные на данный момент пути к объектам.
Убедитесь, что вы записали области удаленных compat.v1.layers
, чтобы сохранить автоматически сгенерированные имена весов для оставшихся 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
- Повторяйте описанные выше шаги до тех пор, пока не замените все
compat.v1.layers
иcompat.v1.get_variable
в вашей модели полностью собственными эквивалентами.
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']
Не забудьте проверить, чтобы убедиться, что недавно обновленная контрольная точка по-прежнему ведет себя так, как вы ожидаете. Применяйте методы, описанные в руководстве по проверке числовой правильности, на каждом шаге этого процесса, чтобы обеспечить правильную работу перенесенного кода.
Обработка изменений поведения TF1.x в TF2, не охватываемых прокладками моделирования
Моделирующие прокладки, описанные в этом руководстве, могут гарантировать, что переменные, слои и потери регуляризации, созданные с get_variable
, tf.compat.v1.layers
и variable_scope
, будут продолжать работать как раньше при использовании активного выполнения и tf.function
, без необходимости полагаться на коллекции.
Это не охватывает всю специфичную для TF1.x семантику, на которую может полагаться ваша модель. В некоторых случаях прокладок может быть недостаточно, чтобы заставить вашу модель работать в TF2 самостоятельно. Прочтите руководство по поведению TF1.x и TF2, чтобы узнать больше о различиях в поведении между TF1.x и TF2.