Gunakan model TF1.x dalam alur kerja TF2

Lihat di TensorFlow.org Jalankan di Google Colab Lihat di GitHub Unduh buku catatan

Panduan ini memberikan gambaran umum dan contoh shim kode pemodelan yang dapat Anda terapkan untuk menggunakan model TF1.x yang ada dalam alur kerja TF2 seperti eksekusi bersemangat, tf.function , dan strategi distribusi dengan sedikit perubahan pada kode pemodelan Anda.

Lingkup penggunaan

Shim yang dijelaskan dalam panduan ini dirancang untuk model TF1.x yang mengandalkan:

  1. tf.compat.v1.get_variable dan tf.compat.v1.variable_scope untuk mengontrol pembuatan dan penggunaan kembali variabel, dan
  2. API berbasis koleksi grafik seperti tf.compat.v1.global_variables() , tf.compat.v1.trainable_variables , tf.compat.v1.losses.get_regularization_losses() , dan tf.compat.v1.get_collection() untuk melacak bobot dan kerugian regularisasi

Ini mencakup sebagian besar model yang dibuat di atas tf.compat.v1.layer , tf.contrib.layers API, dan TensorFlow-Slim .

Shim TIDAK diperlukan untuk model TF1.x berikut:

  1. Model Keras mandiri yang telah melacak semua bobot yang dapat dilatih dan kehilangan regularisasinya masing-masing melalui model.trainable_weights dan model.losses .
  2. tf.Module s yang telah melacak semua bobot yang dapat dilatih melalui module.trainable_variables , dan hanya membuat bobot jika belum dibuat.

Model-model ini cenderung bekerja di TF2 dengan eksekusi yang bersemangat dan tf.function s out-of-the-box.

Mempersiapkan

Impor TensorFlow dan dependensi lainnya.

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

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

from contextlib import contextmanager

Dekorator track_tf1_style_variables

Shim kunci yang dijelaskan dalam panduan ini adalah tf.compat.v1.keras.utils.track_tf1_style_variables , dekorator yang dapat Anda gunakan dalam metode milik tf.keras.layers.Layer dan tf.Module untuk melacak bobot gaya TF1.x dan menangkap kerugian regularisasi.

Mendekorasi metode panggilan tf.keras.layers.Layer atau tf.Module dengan tf.compat.v1.keras.utils.track_tf1_style_variables memungkinkan pembuatan dan penggunaan kembali variabel melalui tf.compat.v1.get_variable (dan dengan ekstensi tf.compat.v1.layers ) untuk bekerja dengan benar di dalam metode yang didekorasi daripada selalu membuat variabel baru pada setiap panggilan. Ini juga akan menyebabkan lapisan atau modul secara implisit melacak bobot yang dibuat atau diakses melalui get_variable di dalam metode yang didekorasi.

Selain melacak bobot itu sendiri di bawah standar layer.variable / module.variable /etc. properties, jika metode tersebut milik tf.keras.layers.Layer , maka kehilangan regularisasi apa pun yang ditentukan melalui argumen pengatur get_variable atau tf.compat.v1.layers akan dilacak oleh lapisan di bawah properti layer.losses standar.

Mekanisme pelacakan ini memungkinkan penggunaan kelas besar kode model-forward-pass gaya TF1.x di dalam lapisan Keras atau tf.Module s di TF2 bahkan dengan perilaku TF2 diaktifkan.

Contoh penggunaan

Contoh penggunaan di bawah ini menunjukkan shim pemodelan yang digunakan untuk mendekorasi metode tf.keras.layers.Layer , tetapi kecuali jika mereka secara khusus berinteraksi dengan fitur Keras, shim tersebut juga berlaku saat mendekorasi metode tf.Module .

Lapisan dibangun dengan tf.compat.v1.get_variable

Bayangkan Anda memiliki lapisan yang diimplementasikan langsung di atas tf.compat.v1.get_variable sebagai berikut:

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

Gunakan shim untuk mengubahnya menjadi layer dan menyebutnya pada 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)>

Akses variabel yang dilacak dan kehilangan regularisasi yang ditangkap seperti lapisan Keras standar.

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>]

Untuk melihat bahwa bobot digunakan kembali setiap kali Anda memanggil layer, atur semua bobot ke nol dan panggil layer lagi.

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)>

Anda juga dapat menggunakan lapisan yang dikonversi secara langsung dalam konstruksi model fungsional Keras.

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

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

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

Model dibuat dengan tf.compat.v1.layers

Bayangkan Anda memiliki lapisan atau model yang diimplementasikan langsung di atas tf.compat.v1.layers sebagai berikut:

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

Gunakan shim untuk mengubahnya menjadi layer dan menyebutnya pada 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)>

Akses variabel yang dilacak dan kehilangan regularisasi yang ditangkap seperti lapisan Keras standar.

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

Untuk melihat bahwa bobot digunakan kembali setiap kali Anda memanggil layer, atur semua bobot ke nol dan panggil layer lagi.

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)>

Anda juga dapat menggunakan lapisan yang dikonversi secara langsung dalam konstruksi model fungsional 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>]

Tangkap pembaruan normalisasi batch dan argumen training model

Di TF1.x, Anda melakukan normalisasi batch seperti ini:

  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])

Perhatikan bahwa:

  1. Pembaruan rata-rata bergerak normalisasi batch dilacak oleh get_collection yang dipanggil secara terpisah dari lapisan
  2. tf.compat.v1.layers.batch_normalization memerlukan argumen training (umumnya disebut is_training saat menggunakan lapisan normalisasi batch TF-Slim)

Di TF2, karena eksekusi yang bersemangat dan dependensi kontrol otomatis, pembaruan rata-rata bergerak normalisasi batch akan segera dieksekusi. Tidak perlu mengumpulkannya secara terpisah dari koleksi pembaruan dan menambahkannya sebagai dependensi kontrol eksplisit.

Selain itu, jika Anda memberikan argumen training metode forward pass tf.keras.layers.Layer Anda, Keras akan dapat melewati fase pelatihan saat ini dan semua lapisan bersarang ke sana seperti halnya untuk lapisan lainnya. Lihat dokumen API untuk tf.keras.Model untuk informasi selengkapnya tentang cara Keras menangani argumen training .

Jika Anda mendekorasi metode tf.Module , Anda perlu memastikan untuk secara manual melewati semua argumen training sesuai kebutuhan. Namun, pembaruan rata-rata bergerak normalisasi batch akan tetap diterapkan secara otomatis tanpa memerlukan dependensi kontrol eksplisit.

Cuplikan kode berikut menunjukkan cara menyematkan lapisan normalisasi batch di shim dan cara menggunakannya dalam model Keras bekerja (berlaku untuk 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)>}

Penggunaan kembali variabel berbasis cakupan variabel

Setiap pembuatan variabel di penerusan berdasarkan get_variable akan mempertahankan penamaan variabel yang sama dan menggunakan kembali semantik yang dimiliki cakupan variabel di TF1.x. Ini benar selama Anda memiliki setidaknya satu cakupan luar yang tidak kosong untuk setiap tf.compat.v1.layers dengan nama yang dibuat secara otomatis, seperti yang disebutkan di atas.

Eksekusi bersemangat & tf.function

Seperti yang terlihat di atas, metode yang didekorasi untuk tf.keras.layers.Layer dan tf.Module berjalan di dalam eksekusi yang bersemangat dan juga kompatibel dengan tf.function . Ini berarti Anda dapat menggunakan pdb dan alat interaktif lainnya untuk melangkah melalui umpan maju Anda saat sedang berjalan.

Strategi distribusi

Panggilan ke get_variable di dalam @track_tf1_style_variables layer atau metode modul menggunakan pembuatan variabel tf.Variable standar di bawah tenda. Ini berarti Anda dapat menggunakannya dengan berbagai strategi distribusi yang tersedia dengan tf.distribute seperti MirroredStrategy dan TPUStrategy .

Bersarang tf.Variable s, tf.Module s, tf.keras.layers & tf.keras.models dalam panggilan yang didekorasi

Mendekorasi panggilan lapisan Anda di tf.compat.v1.keras.utils.track_tf1_style_variables hanya akan menambahkan pelacakan implisit otomatis dari variabel yang dibuat (dan digunakan kembali) melalui tf.compat.v1.get_variable . Itu tidak akan menangkap bobot yang dibuat secara langsung oleh panggilan tf.Variable , seperti yang digunakan oleh lapisan Keras biasa dan sebagian besar tf.Module s. Bagian ini menjelaskan cara menangani kasus bersarang ini.

(Penggunaan yang sudah ada sebelumnya) tf.keras.layers dan tf.keras.models

Untuk penggunaan lapisan dan model Keras bersarang yang sudah ada sebelumnya, gunakan tf.compat.v1.keras.utils.get_or_create_layer . Ini hanya disarankan untuk memudahkan migrasi penggunaan Keras bersarang TF1.x yang ada; kode baru harus menggunakan pengaturan atribut eksplisit seperti yang dijelaskan di bawah ini untuk tf.Variables dan tf.Modules.

Untuk menggunakan tf.compat.v1.keras.utils.get_or_create_layer , bungkus kode yang menyusun model bersarang Anda ke dalam sebuah metode, dan teruskan ke dalam metode. Contoh:

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)>

Metode ini memastikan bahwa lapisan bersarang ini digunakan kembali dengan benar dan dilacak oleh tensorflow. Perhatikan bahwa dekorator @track_tf1_style_variables masih diperlukan pada metode yang sesuai. Metode pembuat model yang diteruskan ke get_or_create_layer (dalam hal ini, self.build_model ), tidak boleh menggunakan argumen.

Berat dilacak:

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)>]

Dan kerugian regularisasi juga:

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

Migrasi tambahan: tf.Variables dan tf.Modules

Jika Anda perlu menyematkan panggilan tf.Variable atau tf.Module s dalam metode yang didekorasi (misalnya, jika Anda mengikuti migrasi inkremental ke API TF2 non-legacy yang dijelaskan nanti dalam panduan ini), Anda masih perlu melacaknya secara eksplisit, dengan persyaratan sebagai berikut:

  • Pastikan secara eksplisit bahwa variabel/modul/lapisan hanya dibuat sekali
  • Lampirkan secara eksplisit sebagai atribut instan seperti yang Anda lakukan saat mendefinisikan modul atau lapisan biasa
  • Gunakan kembali objek yang sudah dibuat secara eksplisit dalam panggilan lanjutan

Ini memastikan bahwa bobot tidak dibuat baru setiap panggilan dan digunakan kembali dengan benar. Selain itu, ini juga memastikan bahwa bobot yang ada dan kerugian regularisasi dapat dilacak.

Berikut adalah contoh bagaimana ini bisa terlihat:

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)>

Perhatikan bahwa pelacakan eksplisit dari modul bersarang diperlukan meskipun didekorasi dengan dekorator track_tf1_style_variables . Ini karena setiap modul/lapisan dengan metode yang didekorasi memiliki penyimpanan variabel sendiri yang terkait dengannya.

Bobot dilacak dengan benar:

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)>]

Serta kehilangan regularisasi:

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

Perhatikan bahwa jika NestedLayer adalah tf.Module non-Keras, variabel akan tetap dilacak tetapi kehilangan regularisasi tidak akan dilacak secara otomatis, jadi Anda harus melacaknya secara eksplisit secara terpisah.

Panduan tentang nama variabel

Panggilan tf.Variable eksplisit dan lapisan Keras menggunakan nama lapisan / mekanisme autogenerasi nama variabel yang berbeda dari yang biasa Anda gunakan dari kombinasi get_variable dan variable_scopes . Meskipun shim akan membuat nama variabel Anda cocok dengan variabel yang dibuat oleh get_variable bahkan ketika beralih dari grafik TF1.x ke TF2 bersemangat eksekusi & tf.function , itu tidak dapat menjamin hal yang sama untuk nama variabel yang dihasilkan untuk panggilan tf.Variable dan lapisan Keras yang Anda menanamkan dalam dekorator metode Anda. Bahkan dimungkinkan untuk beberapa variabel untuk berbagi nama yang sama di TF2 bersemangat eksekusi dan tf.function .

Anda harus berhati-hati dengan hal ini saat mengikuti bagian tentang validasi kebenaran dan pemetaan pos pemeriksaan TF1.x nanti dalam panduan ini.

Menggunakan tf.compat.v1.make_template dalam metode yang didekorasi

Sangat disarankan Anda langsung menggunakan tf.compat.v1.keras.utils.track_tf1_style_variables daripada menggunakan tf.compat.v1.make_template , karena ini adalah lapisan yang lebih tipis di atas TF2 .

Ikuti panduan di bagian ini untuk kode TF1.x sebelumnya yang sudah mengandalkan tf.compat.v1.make_template .

Karena tf.compat.v1.make_template membungkus kode yang menggunakan get_variable , dekorator track_tf1_style_variables memungkinkan Anda untuk menggunakan template ini dalam panggilan lapisan dan berhasil melacak bobot dan kerugian regularisasi.

Namun, pastikan untuk memanggil make_template hanya sekali dan kemudian menggunakan kembali template yang sama di setiap panggilan lapisan. Jika tidak, template baru akan dibuat setiap kali Anda memanggil layer bersama dengan set variabel baru.

Sebagai contoh,

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)

Migrasi tambahan ke TF2 Asli

Seperti disebutkan sebelumnya, track_tf1_style_variables memungkinkan Anda untuk mencampur penggunaan tf.Variable / tf.keras.layers.Layer / tf.Module berorientasi objek gaya TF2 dengan tf.Variable / tf.Module tf.compat.v1.get_variable tf.compat.v1.layers penggunaan di dalam modul/lapisan yang didekorasi sama.

Ini berarti bahwa setelah Anda membuat model TF1.x Anda sepenuhnya kompatibel dengan TF2, Anda dapat menulis semua komponen model baru dengan API TF2 asli (non tf.compat.v1 ) dan membuatnya beroperasi dengan kode lama Anda.

Namun, jika Anda terus memodifikasi komponen model lama, Anda juga dapat memilih untuk secara bertahap mengalihkan penggunaan tf.compat.v1 gaya lama Anda ke API berorientasi objek murni asli yang direkomendasikan untuk kode TF2 yang baru ditulis.

Penggunaan tf.compat.v1.get_variable dapat diganti dengan panggilan self.add_weight jika Anda mendekorasi layer/model Keras, atau dengan panggilan tf.Variable jika Anda mendekorasi objek Keras atau tf.Module s.

Baik tf.compat.v1.layers gaya fungsional dan berorientasi objek umumnya dapat diganti dengan lapisan tf.keras.layers yang setara tanpa perubahan argumen yang diperlukan.

Anda juga dapat mempertimbangkan potongan bagian dari model atau pola umum Anda ke dalam lapisan/modul individual selama perpindahan inkremental Anda ke API asli murni, yang mungkin sendiri menggunakan track_tf1_style_variables .

Catatan tentang Slim dan contrib.layers

Sejumlah besar kode TF 1.x lama menggunakan pustaka Slim , yang dikemas dengan TF 1.x sebagai tf.contrib.layers . Mengonversi kode menggunakan Slim ke TF 2 asli lebih terlibat daripada mengonversi v1.layers . Bahkan, mungkin masuk akal untuk mengonversi kode Slim Anda ke v1.layers terlebih dahulu, lalu mengonversi ke Keras. Di bawah ini adalah beberapa panduan umum untuk mengonversi kode Slim.

  • Pastikan semua argumen eksplisit. Hapus arg_scopes jika memungkinkan. Jika Anda masih perlu menggunakannya, pisahkan normalizer_fn dan activation_fn ke dalam lapisannya sendiri.
  • Lapisan konv yang dapat dipisahkan dipetakan ke satu atau lebih lapisan Keras yang berbeda (lapisan Keras yang mendalam, terarah, dan dapat dipisahkan).
  • Slim dan v1.layers memiliki nama argumen dan nilai default yang berbeda.
  • Perhatikan bahwa beberapa argumen memiliki skala yang berbeda.

Migrasi ke TF2 Asli mengabaikan kompatibilitas pos pemeriksaan

Contoh kode berikut menunjukkan perpindahan bertahap model ke API asli murni tanpa mempertimbangkan kompatibilitas pos pemeriksaan.

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

Selanjutnya, ganti API compat.v1 dengan ekuivalen berorientasi objek asli secara bertahap. Mulailah dengan mengalihkan lapisan konvolusi ke objek Keras yang dibuat di konstruktor lapisan.

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

Gunakan kelas v1.keras.utils.DeterministicRandomTestTool untuk memverifikasi bahwa perubahan inkremental ini meninggalkan model dengan perilaku yang sama seperti sebelumnya.

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())

Anda sekarang telah mengganti semua compat.v1.layers individu dengan lapisan Keras asli.

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())

Terakhir, hapus penggunaan variable_scope yang tersisa (tidak lagi diperlukan) dan dekorator track_tf1_style_variables itu sendiri.

Sekarang Anda memiliki versi model yang sepenuhnya menggunakan API asli.

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())

Mempertahankan kompatibilitas pos pemeriksaan selama migrasi ke Native TF2

Proses migrasi di atas ke API TF2 asli mengubah nama variabel (karena Keras API menghasilkan nama bobot yang sangat berbeda), dan jalur berorientasi objek yang mengarah ke bobot berbeda dalam model. Dampak dari perubahan ini adalah bahwa mereka akan merusak pos pemeriksaan berbasis nama gaya TF1 yang ada atau pos pemeriksaan berorientasi objek gaya TF2.

Namun, dalam beberapa kasus, Anda mungkin dapat mengambil pos pemeriksaan berbasis nama asli Anda dan menemukan pemetaan variabel ke nama barunya dengan pendekatan seperti yang dirinci dalam panduan pos pemeriksaan Menggunakan Kembali TF1.x .

Beberapa tips untuk membuat ini layak adalah sebagai berikut:

  • Semua variabel masih memiliki argumen name yang dapat Anda atur.
  • Model Keras juga mengambil argumen name yang mereka tetapkan sebagai awalan untuk variabel mereka.
  • Fungsi v1.name_scope dapat digunakan untuk mengatur awalan nama variabel. Ini sangat berbeda dari tf.variable_scope . Itu hanya memengaruhi nama, dan tidak melacak variabel dan menggunakan kembali.

Dengan mengingat petunjuk di atas, contoh kode berikut menunjukkan alur kerja yang dapat Anda sesuaikan dengan kode Anda untuk memperbarui bagian model secara bertahap sekaligus memperbarui pos pemeriksaan.

  1. Mulailah dengan mengalihkan gaya fungsional tf.compat.v1.layers ke versi berorientasi objeknya.
class FunctionalStyleCompatModel(tf.keras.layers.Layer):

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

layer = FunctionalStyleCompatModel()
layer(tf.ones(shape=(10, 10, 10, 10)))
[v.name for v in layer.weights]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:8: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:11: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
  # This is added back by InteractiveShellApp.init_path()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
['model/conv2d/bias:0',
 'model/conv2d/kernel:0',
 'model/conv2d_1/bias:0',
 'model/conv2d_1/kernel:0',
 'model/conv2d_2/bias:0',
 'model/conv2d_2/kernel:0']
  1. Selanjutnya, tetapkan objek compat.v1.layer dan variabel apa pun yang dibuat oleh compat.v1.get_variable sebagai properti objek tf.keras.layers.Layer / tf.Module yang metodenya didekorasi dengan track_tf1_style_variables (perhatikan bahwa semua TF2 berorientasi objek pos pemeriksaan gaya sekarang akan menyimpan jalur dengan nama variabel dan jalur berorientasi objek baru).
class OOStyleCompatModel(tf.keras.layers.Layer):

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

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

layer = OOStyleCompatModel()
layer(tf.ones(shape=(10, 10, 10, 10)))
[v.name for v in layer.weights]
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:19: UserWarning: `tf.layers.conv2d` is deprecated and will be removed in a future version. Please Use `tf.keras.layers.Conv2D` instead.
['model/conv2d/kernel:0',
 'model/conv2d/bias:0',
 'model/conv2d_1/kernel:0',
 'model/conv2d_1/bias:0',
 'model/conv2d_2/bias:0',
 'model/conv2d_2/kernel:0']
  1. Simpan pos pemeriksaan yang dimuat pada titik ini untuk menyimpan jalur baik dengan nama variabel (untuk compat.v1.layers), atau dengan grafik objek berorientasi objek.
weights = {v.name: v for v in layer.weights}
assert weights['model/conv2d/kernel:0'] is layer.conv_1.kernel
assert weights['model/conv2d_1/bias:0'] is layer.conv_2.bias
  1. Anda sekarang dapat menukar compat.v1.layers berorientasi objek untuk lapisan Keras asli sambil tetap dapat memuat pos pemeriksaan yang baru saja disimpan. Pastikan Anda mempertahankan nama variabel untuk compat.v1.layers yang tersisa dengan tetap merekam variable_scopes yang dibuat secara otomatis dari lapisan yang diganti. Lapisan/variabel yang dialihkan ini sekarang hanya akan menggunakan jalur atribut objek ke variabel di pos pemeriksaan alih-alih jalur nama variabel.

Secara umum, Anda dapat mengganti penggunaan compat.v1.get_variable dalam variabel yang dilampirkan ke properti dengan:

  • Mengalihkannya ke menggunakan tf.Variable , ATAU
  • Memperbarui mereka dengan menggunakan tf.keras.layers.Layer.add_weight . Perhatikan bahwa jika Anda tidak mengganti semua lapisan sekaligus, ini dapat mengubah penamaan lapisan/variabel yang dibuat secara otomatis untuk compat.v1.layers yang tersisa yang tidak memiliki argumen name . Jika demikian, Anda harus menjaga nama variabel untuk compat.v1.layers yang tersisa tetap sama dengan membuka dan menutup variable_scope secara manual sesuai dengan nama lingkup yang dihasilkan compat.v1.layer yang dihapus. Jika tidak, jalur dari pos pemeriksaan yang ada mungkin bertentangan dan pemuatan pos pemeriksaan akan berperilaku tidak benar.
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']

Menyimpan checkpoint pada langkah ini setelah membuat variabel akan membuatnya hanya berisi jalur objek yang tersedia saat ini.

Pastikan Anda mencatat cakupan compat.v1.layers yang dihapus untuk mempertahankan nama bobot yang dibuat secara otomatis untuk compat.v1.layers yang tersisa.

weights = set(v.name for v in layer.weights)
assert 'model/conv2d_2/kernel:0' in weights
assert 'model/conv2d_2/bias:0' in weights
  1. Ulangi langkah di atas hingga Anda mengganti semua compat.v1.layers dan compat.v1.get_variable s dalam model Anda dengan padanan yang sepenuhnya asli.
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']

Ingatlah untuk menguji untuk memastikan pos pemeriksaan yang baru diperbarui masih berfungsi seperti yang Anda harapkan. Terapkan teknik yang dijelaskan dalam panduan kebenaran numerik validasi pada setiap langkah tambahan dari proses ini untuk memastikan kode yang dimigrasikan berjalan dengan benar.

Menangani perubahan perilaku TF1.x ke TF2 yang tidak tercakup oleh shim pemodelan

Shim pemodelan yang dijelaskan dalam panduan ini dapat memastikan bahwa variabel, lapisan, dan kerugian regularisasi yang dibuat dengan get_variable , tf.compat.v1.layers , dan variable_scope semantik terus bekerja seperti sebelumnya saat menggunakan eksekusi bersemangat dan tf.function , tanpa harus mengandalkan koleksi.

Ini tidak mencakup semua semantik khusus TF1.x yang dapat diandalkan oleh model forward pass Anda. Dalam beberapa kasus, shim mungkin tidak cukup untuk menjalankan model forward pass Anda di TF2 sendiri. Baca panduan perilaku TF1.x vs TF2 untuk mempelajari lebih lanjut tentang perbedaan perilaku antara TF1.x dan TF2.