روز جامعه ML 9 نوامبر است! برای به روز رسانی از TensorFlow، JAX به ما بپیوندید، و بیشتر بیشتر بدانید

تأیید صحت و معادل عددی

مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده در GitHub دانلود دفترچه یادداشت

هنگام انتقال کد TensorFlow خود از TF1.x به TF2 ، این یک عمل خوب است که اطمینان حاصل کنید که کد مهاجرت شده شما در TF2 مانند TF1.x رفتار می کند.

این راهنما شامل مهاجرت نمونه کد با tf.compat.v1.keras.utils.track_tf1_style_variables مدل سازی شیم به کار گرفته tf.keras.layers.Layer روش. دفعات بازدید: راهنمای نقشه برداری مدل برای پیدا کردن اطلاعات بیشتر در مورد TF2 مدل سازی شیم.

این راهنما جزئیات رویکردهایی را که می توانید برای موارد زیر استفاده کنید ، توضیح می دهد:

  • صحت نتایج بدست آمده از مدلهای آموزشی را با استفاده از کد مهاجرت شده تأیید کنید
  • اعتبار عددی کد خود را در نسخه های TensorFlow تأیید کنید

برپایی

pip uninstall -y -q tensorflow
# Install tf-nightly as the model mapping shim is available only in
# TensorFlow 2.7
pip install -q tf-nightly
pip install -q tf_slim
import tensorflow as tf
import tensorflow.compat.v1 as v1

import numpy as np
import tf_slim as slim
import sys

from unittest import mock

from contextlib import contextmanager
!git clone --depth=1 https://github.com/tensorflow/models.git
import models.research.slim.nets.inception_resnet_v2 as inception
Cloning into 'models'...
remote: Enumerating objects: 2921, done.[K
remote: Counting objects: 100% (2921/2921), done.[K
remote: Compressing objects: 100% (2452/2452), done.[K
remote: Total 2921 (delta 742), reused 1280 (delta 431), pack-reused 0[K
Receiving objects: 100% (2921/2921), 32.97 MiB | 16.84 MiB/s, done.
Resolving deltas: 100% (742/742), done.

اگر شما یک تکه غیر محتوا از کد عبور جلو را در شیم قرار می دهید ، می خواهید بدانید که رفتار مشابهی با TF1.x دارد. به عنوان مثال ، سعی کنید یک مدل TF-Slim Inception-Resnet-v2 را به صورت زیر قرار دهید:

# TF1 Inception resnet v2 forward pass based on slim layers
def inception_resnet_v2(inputs, num_classes, is_training):
  with slim.arg_scope(
    inception.inception_resnet_v2_arg_scope(batch_norm_scale=True)):
    return inception.inception_resnet_v2(inputs, num_classes, is_training=is_training)
class InceptionResnetV2(tf.keras.layers.Layer):
  """Slim InceptionResnetV2 forward pass as a Keras layer"""

  def __init__(self, num_classes, **kwargs):
    super().__init__(**kwargs)
    self.num_classes = num_classes

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs, training=None):
    is_training = training or False 

    # Slim does not accept `None` as a value for is_training,
    # Keras will still pass `None` to layers to construct functional models
    # without forcing the layer to always be in training or in inference.
    # However, `None` is generally considered to run layers in inference.

    with slim.arg_scope(
        inception.inception_resnet_v2_arg_scope(batch_norm_scale=True)):
      return inception.inception_resnet_v2(
          inputs, self.num_classes, is_training=is_training)
WARNING:tensorflow:From /tmp/ipykernel_13532/2131234657.py:8: The name tf.keras.utils.track_tf1_style_variables is deprecated. Please use tf.compat.v1.keras.utils.track_tf1_style_variables instead.

همانطور که اتفاق می افتد ، این لایه در واقع کاملاً عالی از جعبه کار می کند (با ردیابی دقیق ضرر منظم).

با این حال ، این چیزی نیست که شما بخواهید آن را بدیهی بدانید. مراحل زیر را دنبال کنید تا مطمئن شوید که در واقع مانند TF1.x رفتار می کند ، تا رعایت معادلات عددی کامل. این مراحل همچنین می تواند به شما کمک کند تا سه قسمتی از پاس رو به جلو که باعث واگرایی از TF1.x می شود را مشخص کنید (تشخیص دهید که آیا واگرایی در مدل رو به جلو در مقابل قسمت دیگری از مدل ایجاد شده است).

مرحله 1: تأیید کنید که متغیرها فقط یک بار ایجاد می شوند

اولین چیزی که باید تأیید کنید این است که شما به درستی مدل را به گونه ای ساخته اید که از متغیرها در هر فراخوانی استفاده مجدد کند تا اینکه هر بار به طور تصادفی متغیرهای جدیدی ایجاد و استفاده کنید. برای مثال، اگر مدل خود را یک لایه جدید ایجاد Keras و یا تماس های tf.Variable در هر تماس پاس رو به جلو و سپس آن را به احتمال زیاد به دلیل عدم متغیرهای ضبط و ایجاد امکانات جدید در هر زمان.

در زیر دو حوزه مدیریت زمینه وجود دارد که می توانید برای تشخیص اینکه مدل شما در حال ایجاد متغیرهای جدید است و اشکال زدایی کدام قسمت از مدل در حال انجام است استفاده کنید.

@contextmanager
def assert_no_variable_creations():
  """Assert no variables are created in this context manager scope."""
  def invalid_variable_creator(next_creator, **kwargs):
    raise ValueError("Attempted to create a new variable instead of reusing an existing one. Args: {}".format(kwargs))

  with tf.variable_creator_scope(invalid_variable_creator):
    yield

@contextmanager
def catch_and_raise_created_variables():
  """Raise all variables created within this context manager scope (if any)."""
  created_vars = []
  def variable_catcher(next_creator, **kwargs):
    var = next_creator(**kwargs)
    created_vars.append(var)
    return var

  with tf.variable_creator_scope(variable_catcher):
    yield
  if created_vars:
    raise ValueError("Created vars:", created_vars)

دامنه اول ( assert_no_variable_creations() ) یک خطا بلافاصله افزایش یک بار شما سعی می کنید یک متغیر در محدوده. این به شما امکان می دهد stacktrace را بررسی کنید (و از اشکال زدایی تعاملی استفاده کنید) تا بفهمید دقیقاً کدام خط کد به جای استفاده مجدد از متغیر موجود ، یک متغیر را ایجاد کرده است.

دامنه دوم ( catch_and_raise_created_variables() ) خواهد یک استثنا در پایان از دامنه بالا می برد اگر هر متغیر به پایان رسید تا ایجاد شده است. این استثنا شامل لیست همه متغیرهای ایجاد شده در محدوده می شود. این برای درک اینکه مجموعه وزنهایی که مدل شما ایجاد می کند در صورتی که بتوانید الگوهای کلی را تشخیص دهید ، مفید است. با این حال ، برای شناسایی خطوط دقیق کد که در آن متغیرها ایجاد شده اند ، کمتر مفید است.

برای اطمینان از اینکه لایه InceptionResnetV2 مبتنی بر shim هیچ متغیر جدیدی را پس از اولین تماس ایجاد نمی کند (احتمالاً استفاده مجدد از آنها) از هر دو دامنه زیر استفاده کنید.

model = InceptionResnetV2(1000)
height, width = 299, 299
num_classes = 1000

inputs = tf.ones( (1, height, width, 3))
# Create all weights on the first call
model(inputs)

# Verify that no new weights are created in followup calls
with assert_no_variable_creations():
  model(inputs)
with catch_and_raise_created_variables():
  model(inputs)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer.py:2212: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  warnings.warn('`layer.apply` is deprecated and '
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/legacy_tf_layers/core.py:336: UserWarning: `tf.layers.flatten` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Flatten` instead.
  warnings.warn('`tf.layers.flatten` is deprecated and '

در مثال زیر ، نحوه عملکرد این دکوراتورها را بر روی لایه ای که هر بار به جای استفاده مجدد از وزنه های موجود ، وزنه جدیدی ایجاد می کند ، مشاهده کنید.

class BrokenScalingLayer(tf.keras.layers.Layer):
  """Scaling layer that incorrectly creates new weights each time:"""

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    var = tf.Variable(initial_value=2.0)
    bias = tf.Variable(initial_value=2.0, name='bias')
    return inputs * var + bias
model = BrokenScalingLayer()
inputs = tf.ones( (1, height, width, 3))
model(inputs)

try:
  with assert_no_variable_creations():
    model(inputs)
except ValueError as err:
  import traceback
  traceback.print_exc()
Traceback (most recent call last):
  File "/tmp/ipykernel_13532/1128777590.py", line 7, in <module>
    model(inputs)
  File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/utils/traceback_utils.py", line 67, in error_handler
    raise e.with_traceback(filtered_tb) from None
  File "/tmp/ipykernel_13532/3224979076.py", line 6, in call
    var = tf.Variable(initial_value=2.0)
  File "/tmp/ipykernel_13532/1829430118.py", line 5, in invalid_variable_creator
    raise ValueError("Attempted to create a new variable instead of reusing an existing one. Args: {}".format(kwargs))
ValueError: Exception encountered when calling layer "broken_scaling_layer" (type BrokenScalingLayer).

Attempted to create a new variable instead of reusing an existing one. Args: {'initial_value': 2.0, 'trainable': None, 'validate_shape': True, 'caching_device': None, 'name': None, 'variable_def': None, 'dtype': None, 'import_scope': None, 'constraint': None, 'synchronization': <VariableSynchronization.AUTO: 0>, 'aggregation': <VariableAggregation.NONE: 0>, 'shape': None}

Call arguments received:
  • inputs=tf.Tensor(shape=(1, 299, 299, 3), dtype=float32)
model = BrokenScalingLayer()
inputs = tf.ones( (1, height, width, 3))
model(inputs)

try:
  with catch_and_raise_created_variables():
    model(inputs)
except ValueError as err:
  print(err)
('Created vars:', [<tf.Variable 'broken_scaling_layer_1/Variable:0' shape=() dtype=float32, numpy=2.0>, <tf.Variable 'broken_scaling_layer_1/bias:0' shape=() dtype=float32, numpy=2.0>])

می توانید لایه را با اطمینان از اینکه فقط یک بار وزنه ایجاد می کند و سپس هر بار مجدداً از آنها استفاده می کنید ، برطرف کنید.

class FixedScalingLayer(tf.keras.layers.Layer):
  """Scaling layer that incorrectly creates new weights each time:"""
  def __init__(self):
    super().__init__()
    self.var = None
    self.bias = None

  @tf.compat.v1.keras.utils.track_tf1_style_variables
  def call(self, inputs):
    if self.var is None:
      self.var = tf.Variable(initial_value=2.0)
      self.bias = tf.Variable(initial_value=2.0, name='bias')
    return inputs * self.var + self.bias

model = FixedScalingLayer()
inputs = tf.ones( (1, height, width, 3))
model(inputs)

with assert_no_variable_creations():
  model(inputs)
with catch_and_raise_created_variables():
  model(inputs)

عیب یابی

در اینجا چند دلیل رایج وجود دارد که چرا مدل شما به طور تصادفی به جای استفاده مجدد از وزنه های موجود ، وزنه های جدیدی ایجاد می کند:

  1. آن استفاده می کند صریح tf.Variable پاسخ بدون استفاده مجدد از در حال حاضر ایجاد شده tf.Variables . ابتدا با بررسی اینکه آیا ایجاد نشده است ، سپس با استفاده مجدد از موارد موجود ، این مشکل را برطرف کنید.
  2. آن را ایجاد یک لایه Keras یا مدل به طور مستقیم در هر بار که رو به جلو منتقل می کند (به عنوان مخالف tf.compat.v1.layers ). ابتدا با بررسی اینکه آیا ایجاد نشده است ، سپس با استفاده مجدد از موارد موجود ، این مشکل را برطرف کنید.
  3. این است که در بالای ساخته شده tf.compat.v1.layers اما نتواند به اختصاص تمام compat.v1.layers نام صریح و یا به بسته بندی خود را compat.v1.layer در داخل استفاده از یک نام variable_scope ، باعث نام لایه autogenerated به افزایش در هر مدل تماس این مشکل را با قرار دادن یک نام tf.compat.v1.variable_scope داخل روش شیم تزئین خود را که کاری ادامه داده اند همه را tf.compat.v1.layers استفاده.

مرحله 2: بررسی کنید که تعداد متغیرها ، نام ها و شکل ها با هم مطابقت دارند

مرحله دوم این است که مطمئن شوید لایه شما که در TF2 اجرا می شود همان تعداد وزنه را با اشکال مشابه ایجاد می کند ، همانطور که کد مربوطه در TF1.x انجام می دهد.

می توانید ترکیبی از بررسی دستی آنها برای مشاهده مطابقت آنها و انجام برنامه ای بررسی ها به صورت برنامه ای در یک واحد آزمون مانند تصویر زیر را انجام دهید.

# Build the forward pass inside a TF1.x graph, and 
# get the counts, shapes, and names of the variables
graph = tf.Graph()
with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
  height, width = 299, 299
  num_classes = 1000
  inputs = tf.ones( (1, height, width, 3))

  out, endpoints = inception_resnet_v2(inputs, num_classes, is_training=False)

  tf1_variable_names_and_shapes = {
      var.name: (var.trainable, var.shape) for var in tf.compat.v1.global_variables()}
  num_tf1_variables = len(tf.compat.v1.global_variables())
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer_v1.py:1694: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  warnings.warn('`layer.apply` is deprecated and '

در مرحله بعد ، همین کار را برای لایه پیچیده شده در TF2 انجام دهید. توجه داشته باشید که مدل قبل از گرفتن وزنه چندین بار نیز فراخوانی می شود. این کار برای آزمایش م forثر استفاده مجدد متغیرها انجام می شود.

height, width = 299, 299
num_classes = 1000

model = InceptionResnetV2(num_classes)
# The weights will not be created until you call the model

inputs = tf.ones( (1, height, width, 3))
# Call the model multiple times before checking the weights, to verify variables
# get reused rather than accidentally creating additional variables
out, endpoints = model(inputs, training=False)
out, endpoints = model(inputs, training=False)

# Grab the name: shape mapping and the total number of variables separately,
# because in TF2 variables can be created with the same name
num_tf2_variables = len(model.variables)
tf2_variable_names_and_shapes = {
    var.name: (var.trainable, var.shape) for var in model.variables}
2021-09-22 22:18:46.944968: 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.
# Verify that the variable counts, names, and shapes all match:
assert num_tf1_variables == num_tf2_variables
assert tf1_variable_names_and_shapes == tf2_variable_names_and_shapes

لایه InceptionResnetV2 مبتنی بر shim این آزمایش را پشت سر می گذارد. با این حال ، در مواردی که آنها مطابقت ندارند ، می توانید آن را از طریق یک تفاوت (متنی یا دیگری) اجرا کنید تا ببینید تفاوت ها در کجاست.

این می تواند سرنخی در مورد اینکه کدام قسمت از مدل مطابق انتظار رفتار نمی کند ، ارائه دهد. با اجرای مشتاقانه می توانید از pdb ، اشکال زدایی تعاملی و نقاط شکست برای کاوش در قسمت هایی از مدل که مشکوک به نظر می رسند ، استفاده کنید و اشکال را در عمق بیشتری اشکال زدایی کنید.

عیب یابی

  • توجه نزدیک به نام هر متغیر به طور مستقیم توسط صریح ایجاد tf.Variable تماس و لایه Keras / مدل به عنوان نام متغیر نسل معناشناسی آنها ممکن است کمی بین نمودار TF1.x و قابلیت TF2 مانند اعدام مشتاق و متفاوت tf.function حتی اگر همه چیز دیگری درست کار می کند اگر این مورد در مورد شما صادق است ، آزمون خود را با توجه به معانی نامگذاری کمی متفاوت تنظیم کنید.

  • شما ممکن است گاهی اوقات می یابند که tf.Variabletf.keras.layers.Layer ، یا tf.keras.Model در پاس رو به جلو حلقه آموزش خود را ایجاد می کند از لیست متغیرهای TF2 خود را از دست رفته حتی اگر آنها توسط مجموعه متغیرهای دستگیر شدند در TF1.x. با تخصیص متغیرها/لایه ها/مدل هایی که فوروارد پاس شما ایجاد می کند ، این ویژگی را برطرف کنید. مشاهده اینجا برای اطلاعات بیشتر.

مرحله 3: همه متغیرها را بازنشانی کنید ، معادل عددی را با همه موارد غیر فعال بررسی کنید

گام بعدی این است که وقتی عدد را طوری اصلاح می کنید که تولید عدد تصادفی در کار نباشد (مانند استنتاج) ، معادل عددی برای خروجی های واقعی و ردیابی ضرر منظم را تأیید کنید.

روش دقیق انجام این کار ممکن است به مدل خاص شما بستگی داشته باشد ، اما در اکثر مدلها (مانند این) ، می توانید این کار را به روش زیر انجام دهید:

  1. شروع وزن ها به همان مقدار بدون تصادفی. این را می توان با تنظیم مجدد آنها به مقدار ثابت پس از ایجاد آنها انجام داد.
  2. اجرای مدل در حالت استنتاج برای جلوگیری از ایجاد هرگونه لایه رها شده که می تواند منابع تصادفی باشد.

کد زیر نشان می دهد که چگونه می توانید نتایج TF1.x و TF2 را از این طریق مقایسه کنید.

graph = tf.Graph()
with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
  height, width = 299, 299
  num_classes = 1000
  inputs = tf.ones( (1, height, width, 3))

  out, endpoints = inception_resnet_v2(inputs, num_classes, is_training=False)

  # Rather than running the global variable initializers,
  # reset all variables to a constant value
  var_reset = tf.group([var.assign(tf.ones_like(var) * 0.001) for var in tf.compat.v1.global_variables()])
  sess.run(var_reset)

  # Grab the outputs & regularization loss
  reg_losses = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
  tf1_regularization_loss = sess.run(tf.math.add_n(reg_losses))
  tf1_output = sess.run(out)

print("Regularization loss:", tf1_regularization_loss)
tf1_output[0][:5]
Regularization loss: 0.001182976
array([0.00299837, 0.00299837, 0.00299837, 0.00299837, 0.00299837],
      dtype=float32)

نتایج TF2 را بدست آورید.

height, width = 299, 299
num_classes = 1000

model = InceptionResnetV2(num_classes)

inputs = tf.ones((1, height, width, 3))
# Call the model once to create the weights
out, endpoints = model(inputs, training=False)

# Reset all variables to the same fixed value as above, with no randomness
for var in model.variables:
  var.assign(tf.ones_like(var) * 0.001)
tf2_output, endpoints = model(inputs, training=False)

# Get the regularization loss
tf2_regularization_loss = tf.math.add_n(model.losses)

print("Regularization loss:", tf2_regularization_loss)
tf2_output[0][:5]
Regularization loss: tf.Tensor(0.0011829757, shape=(), dtype=float32)
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.00299837, 0.00299837, 0.00299837, 0.00299837, 0.00299837],
      dtype=float32)>
# Create a dict of tolerance values
tol_dict={'rtol':1e-06, 'atol':1e-05}
# Verify that the regularization loss and output both match
# when we fix the weights and avoid randomness by running inference:
np.testing.assert_allclose(tf1_regularization_loss, tf2_regularization_loss.numpy(), **tol_dict)
np.testing.assert_allclose(tf1_output, tf2_output.numpy(), **tol_dict)

اعداد بین TF1.x و TF2 مطابقت زمانی که شما حذف منابع تصادفی، و TF2 سازگار InceptionResnetV2 لایه آزمون عبور می کند.

اگر مشاهده می کنید که نتایج برای مدل های خود متفاوت است ، می توانید از چاپ یا pdb و اشکال زدایی تعاملی برای تشخیص اینکه کجا و چرا نتایج شروع به واگرایی می کند ، استفاده کنید. اجرای مشتاقانه می تواند این کار را به میزان قابل توجهی آسان کند. همچنین می توانید از یک روش تخریب برای اجرای بخشهای کوچکی از مدل بر روی ورودیهای متوسط ​​ثابت و جداسازی در مواردی که واگرایی اتفاق می افتد استفاده کنید.

به راحتی ، بسیاری از شبکه های باریک (و مدل های دیگر) نقاط انتهایی متوسطی را که می توانید بررسی کنید ، نشان می دهند.

مرحله 4: تولید اعداد تصادفی را تراز کنید ، معادلات عددی را هم در آموزش و هم در نتیجه گیری بررسی کنید

گام نهایی این است که تأیید کنید که مدل TF2 از لحاظ عددی با مدل TF1.x مطابقت دارد ، حتی زمانی که برای ایجاد اعداد تصادفی در راه اندازی متغیر و در خود گذر جلو (مانند لایه های ترک تحصیل در هنگام ارسال جلو) حساب می شود.

شما می توانید این کار را با استفاده از ابزار آزمایش زیر انجام دهید تا معنایی تولید عدد تصادفی بین نمودارها/جلسات TF1.x و اجرای مشتاق مطابقت داشته باشد.

نمودارها/جلسات میراث TF1 و اجرای مشتاق TF2 از معانی مختلف تولید عدد تصادفی حالت دار استفاده می کنند.

در tf.compat.v1.Session ، اگر هیچ دانه مشخص، تولید اعداد تصادفی بستگی دارد که چگونه بسیاری از عملیات در نمودار در زمانی که عملیات تصادفی اضافه شده است، و چند بار گراف اجرا می شود. در اجرای مشتاق ، تولید عدد تصادفی حالت دار بستگی به بذر جهانی ، بذر تصادفی عملیات و چند بار عملیات با عملیات با دانه تصادفی داده شده دارد. مشاهده tf.random.set_seed برای اطلاعات بیشتر.

در بر داشت زیر DeterministicTestTool شی فراهم می کند یک مدیر زمینه scope() است که می تواند عملیات تصادفی stateful به استفاده از دانه همان در سراسر هر دو نمودار TF1 / جلسات و اجرای مشتاق،

این ابزار دو حالت آزمایش را ارائه می دهد:

  1. constant که با استفاده از دانه برای هر عمل ساده بدون توجه به چند بار آن شده است به نام و،
  2. num_random_ops که با استفاده از تعداد عملیات تصادفی stateful به قبلا مشاهده شده به عنوان بذر عملیات.

این امر هم برای عملیات تصادفی حالت دار که برای ایجاد و راه اندازی متغیرها استفاده می شود ، و هم برای عملیات تصادفی حالت دار که در محاسبات استفاده می شود (مانند لایه های ترک تحصیل) اعمال می شود.

seed_implementation = sys.modules[tf.compat.v1.get_seed.__module__]

class DeterministicTestTool(object):
  def __init__(self, seed: int = 42, mode='constant'):
    """Set mode to 'constant' or 'num_random_ops'. Defaults to 'constant'."""
    if mode not in {'constant', 'num_random_ops'}:
      raise ValueError("Mode arg must be 'constant' or 'num_random_ops'. " +
                       "Got: {}".format(mode))

    self._mode = mode
    self._seed = seed
    self.operation_seed = 0
    self._observed_seeds = set()

  def scope(self):
    tf.random.set_seed(self._seed)

    def _get_seed(_):
      """Wraps TF get_seed to make deterministic random generation easier.

      This makes a variable's initialization (and calls that involve random
      number generation) depend only on how many random number generations
      were used in the scope so far, rather than on how many unrelated
      operations the graph contains.

      Returns:
        Random seed tuple.
      """
      op_seed = self.operation_seed
      if self._mode == "constant":
        tf.random.set_seed(op_seed)
      else:
        if op_seed in self._observed_seeds:
          raise ValueError(
              'This `DeterministicTestTool` object is trying to re-use the ' +
              'already-used operation seed {}. '.format(op_seed) +
              'It cannot guarantee random numbers will match between eager ' +
              'and sessions when an operation seed is reused. ' +
              'You most likely set ' +
              '`operation_seed` explicitly but used a value that caused the ' +
              'naturally-incrementing operation seed sequences to overlap ' +
              'with an already-used seed.')

        self._observed_seeds.add(op_seed)
        self.operation_seed += 1

      return (self._seed, op_seed)

    # mock.patch internal symbols to modify the behavior of TF APIs relying on them

    return mock.patch.object(seed_implementation, 'get_seed', wraps=_get_seed)

سه تنسور تصادفی ایجاد کنید تا نحوه استفاده از این ابزار را برای ایجاد هماهنگی بین تعداد اعداد تصادفی بین جلسات و اجرای مشتاق نشان دهید.

random_tool = DeterministicTestTool()
with random_tool.scope():
  graph = tf.Graph()
  with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
    a = tf.random.uniform(shape=(3,1))
    a = a * 3
    b = tf.random.uniform(shape=(3,3))
    b = b * 3
    c = tf.random.uniform(shape=(3,3))
    c = c * 3
    graph_a, graph_b, graph_c = sess.run([a, b, c])

graph_a, graph_b, graph_c
(array([[2.5063772],
        [2.7488918],
        [1.4839486]], dtype=float32),
 array([[2.5063772, 2.7488918, 1.4839486],
        [1.5633398, 2.1358476, 1.3693532],
        [0.3598416, 1.8287641, 2.5314465]], dtype=float32),
 array([[2.5063772, 2.7488918, 1.4839486],
        [1.5633398, 2.1358476, 1.3693532],
        [0.3598416, 1.8287641, 2.5314465]], dtype=float32))
random_tool = DeterministicTestTool()
with random_tool.scope():
  a = tf.random.uniform(shape=(3,1))
  a = a * 3
  b = tf.random.uniform(shape=(3,3))
  b = b * 3
  c = tf.random.uniform(shape=(3,3))
  c = c * 3

a, b, c
(<tf.Tensor: shape=(3, 1), dtype=float32, numpy=
 array([[2.5063772],
        [2.7488918],
        [1.4839486]], dtype=float32)>,
 <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
 array([[2.5063772, 2.7488918, 1.4839486],
        [1.5633398, 2.1358476, 1.3693532],
        [0.3598416, 1.8287641, 2.5314465]], dtype=float32)>,
 <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
 array([[2.5063772, 2.7488918, 1.4839486],
        [1.5633398, 2.1358476, 1.3693532],
        [0.3598416, 1.8287641, 2.5314465]], dtype=float32)>)
# Demonstrate that the generated random numbers match
np.testing.assert_allclose(graph_a, a.numpy(), **tol_dict)
np.testing.assert_allclose(graph_b, b.numpy(), **tol_dict)
np.testing.assert_allclose(graph_c, c.numpy(), **tol_dict)

با این حال، توجه داشته باشید که در constant حالت، چون b و c با دانه همان تولید شده و همان شکل شد، آنها را دقیقا همان ارزش را داشته باشد.

np.testing.assert_allclose(b.numpy(), c.numpy(), **tol_dict)

پیگیری سفارش

اگر شما در مورد برخی از اعداد تصادفی تطبیق در نگران constant حالت کاهش اعتماد به نفس خود را در آزمون معادل عددی خود را (به عنوان مثال اگر چند وزن در initializations همان را)، شما می توانید با استفاده از num_random_ops حالت برای جلوگیری از این. در num_random_ops حالت، اعداد تصادفی تولید شده بر روی سفارش از عملیات تصادفی در برنامه بستگی دارد.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  graph = tf.Graph()
  with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
    a = tf.random.uniform(shape=(3,1))
    a = a * 3
    b = tf.random.uniform(shape=(3,3))
    b = b * 3
    c = tf.random.uniform(shape=(3,3))
    c = c * 3
    graph_a, graph_b, graph_c = sess.run([a, b, c])

graph_a, graph_b, graph_c
(array([[2.5063772],
        [2.7488918],
        [1.4839486]], dtype=float32),
 array([[0.45038545, 1.9197761 , 2.4536333 ],
        [1.0371652 , 2.9898582 , 1.924583  ],
        [0.25679827, 1.6579313 , 2.8418403 ]], dtype=float32),
 array([[2.9634383 , 1.0862181 , 2.6042497 ],
        [0.70099247, 2.3920312 , 1.0470468 ],
        [0.18173039, 0.8359269 , 1.0508587 ]], dtype=float32))
random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  a = tf.random.uniform(shape=(3,1))
  a = a * 3
  b = tf.random.uniform(shape=(3,3))
  b = b * 3
  c = tf.random.uniform(shape=(3,3))
  c = c * 3

a, b, c
(<tf.Tensor: shape=(3, 1), dtype=float32, numpy=
 array([[2.5063772],
        [2.7488918],
        [1.4839486]], dtype=float32)>,
 <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
 array([[0.45038545, 1.9197761 , 2.4536333 ],
        [1.0371652 , 2.9898582 , 1.924583  ],
        [0.25679827, 1.6579313 , 2.8418403 ]], dtype=float32)>,
 <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
 array([[2.9634383 , 1.0862181 , 2.6042497 ],
        [0.70099247, 2.3920312 , 1.0470468 ],
        [0.18173039, 0.8359269 , 1.0508587 ]], dtype=float32)>)
# Demonstrate that the generated random numbers match
np.testing.assert_allclose(graph_a, a.numpy(), **tol_dict)
np.testing.assert_allclose(graph_b, b.numpy(), **tol_dict )
np.testing.assert_allclose(graph_c, c.numpy(), **tol_dict)
# Demonstrate that with the 'num_random_ops' mode,
# b & c took on different values even though
# their generated shape was the same
assert not np.allclose(b.numpy(), c.numpy(), **tol_dict)

با این حال ، توجه داشته باشید که در این حالت نسل تصادفی به ترتیب برنامه حساس است و بنابراین اعداد تصادفی تولید شده زیر مطابقت ندارند.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  a = tf.random.uniform(shape=(3,1))
  a = a * 3
  b = tf.random.uniform(shape=(3,3))
  b = b * 3

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  b_prime = tf.random.uniform(shape=(3,3))
  b_prime = b_prime * 3
  a_prime = tf.random.uniform(shape=(3,1))
  a_prime = a_prime * 3

assert not np.allclose(a.numpy(), a_prime.numpy())
assert not np.allclose(b.numpy(), b_prime.numpy())

برای اجازه برای اشکال زدایی تغییرات با توجه به ردیابی سفارش، DeterministicTestTool در num_random_ops حالت شما اجازه می دهد تا ببینید که چگونه بسیاری از عملیات های تصادفی با ترسیم شده operation_seed اموال.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  print(random_tool.operation_seed)
  a = tf.random.uniform(shape=(3,1))
  a = a * 3
  print(random_tool.operation_seed)
  b = tf.random.uniform(shape=(3,3))
  b = b * 3
  print(random_tool.operation_seed)
0
1
2

اگر شما به حساب نیاز برای متفاوت سفارش اثری در آزمون خود را، شما حتی می توانید تنظیم خودکار افزایش operation_seed صراحت. به عنوان مثال ، می توانید از این مورد برای ایجاد تطابق نسل اعداد تصادفی در دو دستور برنامه مختلف استفاده کنید.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  print(random_tool.operation_seed)
  a = tf.random.uniform(shape=(3,1))
  a = a * 3
  print(random_tool.operation_seed)
  b = tf.random.uniform(shape=(3,3))
  b = b * 3

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  random_tool.operation_seed = 1
  b_prime = tf.random.uniform(shape=(3,3))
  b_prime = b_prime * 3
  random_tool.operation_seed = 0
  a_prime = tf.random.uniform(shape=(3,1))
  a_prime = a_prime * 3

np.testing.assert_allclose(a.numpy(), a_prime.numpy(), **tol_dict)
np.testing.assert_allclose(b.numpy(), b_prime.numpy(), **tol_dict)
0
1

با این حال، DeterministicTestTool اجازه استفاده مجدد از دانه عملیات در حال حاضر استفاده می شود، بنابراین مطمئن شوید که توالی خودکار افزایش مییابد نمی تواند همپوشانی کند. این امر به این دلیل است که اجرای مشتاقانه اعداد متفاوتی را برای استفاده های بعدی از عملیات مشابه ایجاد می کند در حالی که نمودارها و جلسات TF1 اینطور نیستند ، بنابراین افزایش خطا به حفظ جلسه و اشتیاق اشتیاق ایجاد تعداد تصادفی حالت دار کمک می کند.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  random_tool.operation_seed = 1
  b_prime = tf.random.uniform(shape=(3,3))
  b_prime = b_prime * 3
  random_tool.operation_seed = 0
  a_prime = tf.random.uniform(shape=(3,1))
  a_prime = a_prime * 3
  try:
    c = tf.random.uniform(shape=(3,1))
    raise RuntimeError("An exception should have been raised before this, " +
                     "because the auto-incremented operation seed will " +
                     "overlap an already-used value")
  except ValueError as err:
    print(err)
This `DeterministicTestTool` object is trying to re-use the already-used operation seed 1. It cannot guarantee random numbers will match between eager and sessions when an operation seed is reused. You most likely set `operation_seed` explicitly but used a value that caused the naturally-incrementing operation seed sequences to overlap with an already-used seed.

تأیید استنتاج

شما هم اکنون می توانید با استفاده از DeterministicTestTool به مطمئن شوید که InceptionResnetV2 بازی مدل در استنباط، حتی زمانی که با استفاده از مقدار دهی اولیه وزن تصادفی. برای یک شرط قوی تر با توجه به تطبیق سفارش برنامه، استفاده از num_random_ops حالت.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  graph = tf.Graph()
  with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
    height, width = 299, 299
    num_classes = 1000
    inputs = tf.ones( (1, height, width, 3))

    out, endpoints = inception_resnet_v2(inputs, num_classes, is_training=False)

    # Initialize the variables
    sess.run(tf.compat.v1.global_variables_initializer())

    # Grab the outputs & regularization loss
    reg_losses = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
    tf1_regularization_loss = sess.run(tf.math.add_n(reg_losses))
    tf1_output = sess.run(out)

  print("Regularization loss:", tf1_regularization_loss)
Regularization loss: 1.2254326
height, width = 299, 299
num_classes = 1000

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  model = InceptionResnetV2(num_classes)

  inputs = tf.ones((1, height, width, 3))
  tf2_output, endpoints = model(inputs, training=False)

  # Grab the regularization loss as well
  tf2_regularization_loss = tf.math.add_n(model.losses)

print("Regularization loss:", tf2_regularization_loss)
Regularization loss: tf.Tensor(1.2254325, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
# when using the DeterministicTestTool:
np.testing.assert_allclose(tf1_regularization_loss, tf2_regularization_loss.numpy(), **tol_dict)
np.testing.assert_allclose(tf1_output, tf2_output.numpy(), **tol_dict)

تأیید آموزش

از آنجا DeterministicTestTool برای تمام عملیات تصادفی stateful به (از جمله هر دو مقدار دهی اولیه وزن و محاسبات مانند لایه ها ترک تحصیل) کار می کند، شما می توانید آن را به منظور بررسی مدل در حالت آموزش مطابقت نیز استفاده کنید. شما دوباره می توانید با استفاده از num_random_ops حالت چرا که نظم برنامه از عملیات تصادفی stateful به منطبق است.

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  graph = tf.Graph()
  with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
    height, width = 299, 299
    num_classes = 1000
    inputs = tf.ones( (1, height, width, 3))

    out, endpoints = inception_resnet_v2(inputs, num_classes, is_training=True)

    # Initialize the variables
    sess.run(tf.compat.v1.global_variables_initializer())

    # Grab the outputs & regularization loss
    reg_losses = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
    tf1_regularization_loss = sess.run(tf.math.add_n(reg_losses))
    tf1_output = sess.run(out)

  print("Regularization loss:", tf1_regularization_loss)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/layers/normalization/batch_normalization.py:532: _colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
Regularization loss: 1.22548
height, width = 299, 299
num_classes = 1000

random_tool = DeterministicTestTool(mode='num_random_ops')
with random_tool.scope():
  model = InceptionResnetV2(num_classes)

  inputs = tf.ones((1, height, width, 3))
  tf2_output, endpoints = model(inputs, training=True)

  # Grab the regularization loss as well
  tf2_regularization_loss = tf.math.add_n(model.losses)

print("Regularization loss:", tf2_regularization_loss)
Regularization loss: tf.Tensor(1.2254798, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
# when using the DeterministicTestTool
np.testing.assert_allclose(tf1_regularization_loss, tf2_regularization_loss.numpy(), **tol_dict)
np.testing.assert_allclose(tf1_output, tf2_output.numpy(), **tol_dict)

شما در حال حاضر تایید کرد که InceptionResnetV2 مدل در حال اجرا مشتاقانه با تزئین اطراف tf.keras.layers.Layer عددی منطبق بر شبکه باریک در حال اجرا در TF1 نمودار ها و جلسات.

به عنوان مثال، خواستار InceptionResnetV2 لایه به طور مستقیم با training=True interleaves مقدار دهی اولیه متغیر با سفارش ترک تحصیل با توجه به سفارش ایجاد شبکه می باشد.

از سوی دیگر، برای اولین بار قرار دادن tf.keras.layers.Layer دکوراتور در یک مدل کاربردی Keras و تنها پس از فراخوانی مدل با training=True معادل مقدار دهی اولیه تمام متغیرها پس از آن با استفاده از لایه ترک تحصیل است. این یک ترتیب ردیابی متفاوت و مجموعه ای متفاوت از اعداد تصادفی ایجاد می کند.

با این حال، به طور پیش فرض mode='constant' حساس به این تفاوت ها در ردیابی سفارش نیست و بدون کار اضافی حتی زمانی که تعبیه لایه در یک مدل کاربردی Keras منتقل می کند.

random_tool = DeterministicTestTool()
with random_tool.scope():
  graph = tf.Graph()
  with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
    height, width = 299, 299
    num_classes = 1000
    inputs = tf.ones( (1, height, width, 3))

    out, endpoints = inception_resnet_v2(inputs, num_classes, is_training=True)

    # Initialize the variables
    sess.run(tf.compat.v1.global_variables_initializer())

    # Get the outputs & regularization losses
    reg_losses = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
    tf1_regularization_loss = sess.run(tf.math.add_n(reg_losses))
    tf1_output = sess.run(out)

  print("Regularization loss:", tf1_regularization_loss)
Regularization loss: 1.2239965
height, width = 299, 299
num_classes = 1000

random_tool = DeterministicTestTool()
with random_tool.scope():
  keras_input = tf.keras.Input(shape=(height, width, 3))
  layer = InceptionResnetV2(num_classes)
  model = tf.keras.Model(inputs=keras_input, outputs=layer(keras_input))

  inputs = tf.ones((1, height, width, 3))
  tf2_output, endpoints = model(inputs, training=True)

  # Get the regularization loss
  tf2_regularization_loss = tf.math.add_n(model.losses)

print("Regularization loss:", tf2_regularization_loss)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer.py:1345: 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.
  warnings.warn('`layer.updates` will be removed in a future version. '
Regularization loss: tf.Tensor(1.2239964, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
# when using the DeterministicTestTool
np.testing.assert_allclose(tf1_regularization_loss, tf2_regularization_loss.numpy(), **tol_dict)
np.testing.assert_allclose(tf1_output, tf2_output.numpy(), **tol_dict)

مرحله 3b یا 4b (اختیاری): آزمایش با ایستهای بازرسی از قبل موجود

پس از مرحله 3 یا مرحله 4 در بالا ، اجرای آزمایشی معادل سازی عددی هنگام شروع از ایست های بازرسی از قبل موجود ، در صورت وجود ، مفید خواهد بود. این می تواند هر دو را بررسی کند که بارگذاری بازرسی قدیمی شما به درستی کار می کند و اینکه خود مدل درست کار می کند. پست های بازرسی استفاده مجدد TF1.x هدایت را پوشش می دهد که چگونه به استفاده مجدد از قبل موجود پست های بازرسی TF1.x خود و انتقال آنها به TF2 پست های بازرسی.

آزمایش و عیب یابی اضافی

همانطور که آزمونهای معادل عددی بیشتری اضافه می کنید ، ممکن است آزمایشی را نیز انتخاب کنید که محاسبه گرادیان (یا حتی به روزرسانی های بهینه ساز) شما را مطابقت دهد.

انتشار عقب و محاسبه گرادیان بیشتر از مدلهای رو به جلو مستعد ناپایداری های عددی شناور هستند. این بدان معناست که از آنجایی که آزمون های معادل سازی شما بخش های غیر جداگانه ای از آموزش شما را پوشش می دهد ، ممکن است تفاوتهای عددی بی اهمیتی را بین دویدن با اشتیاق کامل و نمودارهای TF1 مشاهده کنید. این ممکن است ناشی از بهینه سازی نمودار TensorFlow باشد که کارهایی مانند جایگزینی بیانهای فرعی در یک نمودار با عملیات ریاضی کمتر را انجام می دهد.

برای جداسازی این است که به احتمال زیاد مورد که آیا، شما می توانید کد TF1 خود را به TF2 محاسبات اتفاق می افتد در داخل یک مقایسه tf.function (که شامل بهینه سازی نمودار عبور مانند نمودار TF1 خود را) به جای یک محاسبه کاملا مشتاق. متناوبا، شما می توانید با استفاده از سعی tf.config.optimizer.set_experimental_options برای غیر فعال کردن بهینه سازی مانند عبور "arithmetic_optimization" پیش از محاسبه TF1 خود را به دیدن اگر نتیجه پایان می رسد تا عددی به نتایج محاسبات TF2 شما نزدیک تر است. در اجرا می شود آموزش واقعی خود را توصیه می شود شما با استفاده از tf.function با بهینه سازی عبور می کند به دلایل عملکرد را فعال کنید، اما شما ممکن است پیدا کردن آن مفید آنها را غیر فعال در تست های واحد ارزی عددی خود را.

به طور مشابه، شما همچنین ممکن است که پیدا tf.compat.v1.train بهینه و بهینه TF2 دارند کمی متفاوت خواص عدد ممیز شناور از TF2 بهینه، حتی اگر فرمول های ریاضی آنها به نمایندگی از یکسان هستند. این به احتمال زیاد در دوره های آموزشی شما مشکلی ایجاد نمی کند ، اما ممکن است به تحمل عددی بالاتری در آزمون های واحد معادل نیاز داشته باشد.