مشاهده در 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 DeterministicRandomTestTool is available only in
# Tensorflow 2.8
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 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: 3192, done.[K remote: Counting objects: 100% (3192/3192), done.[K remote: Compressing objects: 100% (2696/2696), done.[K remote: Total 3192 (delta 848), reused 1381 (delta 453), pack-reused 0[K Receiving objects: 100% (3192/3192), 33.39 MiB | 12.89 MiB/s, done. Resolving deltas: 100% (848/848), 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_27382/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 مبتنی بر شیم هیچ متغیر جدیدی پس از اولین تماس ایجاد نمی کند (احتمالاً با استفاده مجدد از آنها) استفاده کنید.
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/tf_slim/layers/layers.py:684: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead. outputs = layer.apply(inputs, training=is_training) /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/keras/legacy_tf_layers/core.py:332: 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_27382/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_27382/3224979076.py", line 6, in call var = tf.Variable(initial_value=2.0) File "/tmp/ipykernel_27382/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)
عیب یابی
در اینجا چند دلیل متداول وجود دارد که چرا مدل شما ممکن است بهطور تصادفی وزنهای جدیدی را به جای استفاده مجدد از وزنهای موجود ایجاد کند:
- از یک تماس صریح
tf.Variable
بدون استفاده مجدد ازtf.Variables
که از قبل ایجاد شده استفاده می کند. این مشکل را ابتدا بررسی کنید که آیا ایجاد نشده است و سپس از موارد موجود استفاده مجدد کنید. - هر بار یک لایه یا مدل Keras را مستقیماً در گذر به جلو ایجاد می کند (برخلاف
tf.compat.v1.layers
). این مشکل را ابتدا بررسی کنید که آیا ایجاد نشده است و سپس از موارد موجود استفاده مجدد کنید. - این بر روی
tf.compat.v1.layers
ساخته شده است، اما نمی تواند به همه لایه هایcompat.v1.layers
. یک نام صریح اختصاص دهد یا استفاده از لایه compat.v1. شما را در داخلcompat.v1.layer
نامگذاریvariable_scope
قرار دهد، که باعث می شود نام لایه های تولید شده به صورت خودکار افزایش یابد. هر مدل تماس بگیرید با قرار دادن یک tf.compat.v1.variable_scope به نام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 انجام دهید. توجه داشته باشید که مدل قبل از گرفتن وزنه ها نیز چندین بار فراخوانی می شود. این کار برای آزمایش موثر استفاده مجدد متغیر انجام می شود.
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-12-04 02:27:27.209445: 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 مبتنی بر شیم این آزمایش را با موفقیت پشت سر می گذارد. با این حال، در مواردی که آنها مطابقت ندارند، میتوانید آن را از طریق یک تفاوت (متن یا موارد دیگر) اجرا کنید تا ببینید تفاوتها کجاست.
این می تواند سرنخی از این که کدام قسمت از مدل آنطور که انتظار می رود رفتار نمی کند ارائه دهد. با اجرای مشتاقانه میتوانید از pdf، اشکالزدایی تعاملی، و نقاط شکست برای حفاری در بخشهایی از مدل که مشکوک به نظر میرسند، استفاده کنید و آنچه را که اشتباه میکند را با عمق بیشتری عیبیابی کنید.
عیب یابی
به نام هر متغیری که مستقیماً توسط فراخوانیهای
tf.Variable
صریح و لایهها/مدلهای Keras ایجاد میشود، دقت کنید زیرا معنای تولید نام متغیر آنها ممکن است بین نمودارهای TF1.x و عملکرد TF2 مانند اجرای مشتاق وtf.function
کمی متفاوت باشد، حتی اگر همه چیز باشد. دیگری به درستی کار می کند اگر این مورد برای شماست، آزمون خود را طوری تنظیم کنید که معنای نامگذاری کمی متفاوت باشد.ممکن است گاهی متوجه شوید که
tf.Variable
s،tf.keras.layers.Layer
s، یاtf.keras.Model
ایجاد شده در پاس رو به جلو حلقه آموزشی شما از لیست متغیرهای TF2 شما غایب است، حتی اگر توسط مجموعه متغیرها ضبط شده باشند. در TF1.x. با تخصیص متغیرها/لایهها/مدلهایی که پاس فوروارد شما ایجاد میکند به ویژگیهای نمونه در مدلتان این مشکل را برطرف کنید. برای اطلاعات بیشتر اینجا را ببینید
مرحله 3: همه متغیرها را بازنشانی کنید، هم ارزی عددی را با غیرفعال بودن همه تصادفی بررسی کنید
گام بعدی این است که هم ارزی عددی را برای خروجی های واقعی و هم برای ردیابی تلفات منظم زمانی که مدل را طوری اصلاح می کنید که تولید اعداد تصادفی درگیر نباشد (مانند هنگام استنتاج) تأیید کنید.
روش دقیق انجام این کار ممکن است به مدل خاص شما بستگی داشته باشد، اما در اکثر مدلها (مانند این مدل)، میتوانید این کار را به صورت زیر انجام دهید:
- مقدار اولیه وزن ها به همان مقدار بدون تصادفی. این را می توان با تنظیم مجدد آنها به یک مقدار ثابت پس از ایجاد آنها انجام داد.
- اجرای مدل در حالت استنتاج برای جلوگیری از ایجاد هرگونه لایه حذفی که می تواند منابع تصادفی باشد.
کد زیر نشان می دهد که چگونه می توانید نتایج 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 مطابقت دارند و لایه InceptionResnetV2
سازگار با TF2 آزمایش را با موفقیت پشت سر می گذارد.
اگر در حال مشاهده نتایج متفاوت برای مدلهای خود هستید، میتوانید از چاپ یا pdf و اشکالزدایی تعاملی برای تشخیص اینکه کجا و چرا نتایج شروع به واگرایی میکنند استفاده کنید. اجرای مشتاقانه می تواند این کار را به میزان قابل توجهی آسان کند. همچنین میتوانید از یک روش فرسایش برای اجرای تنها بخشهای کوچکی از مدل بر روی ورودیهای میانی ثابت و جداسازی محل وقوع واگرایی استفاده کنید.
بهراحتی، بسیاری از شبکههای باریک (و سایر مدلها) نقاط پایانی میانی را نیز نشان میدهند که میتوانید آنها را بررسی کنید.
مرحله 4: تولید اعداد تصادفی را تراز کنید، هم ارزی عددی را هم در آموزش و هم در استنتاج بررسی کنید
مرحله آخر تأیید این است که مدل TF2 از نظر عددی با مدل TF1.x مطابقت دارد، حتی زمانی که تولید اعداد تصادفی در مقدار دهی اولیه متغیر و در خود گذر پیشرو در نظر گرفته میشود (مانند لایههای حذفی در طول عبور به جلو).
می توانید این کار را با استفاده از ابزار تست زیر انجام دهید تا معنایی تولید اعداد تصادفی بین نمودارها/جلسات TF1.x و اجرای مشتاق مطابقت داشته باشد.
نمودارها/جلسات میراث TF1 و اجرای مشتاق TF2 از معناشناسی متفاوت تولید اعداد تصادفی حالت دار استفاده می کنند.
در tf.compat.v1.Session
s، اگر هیچ دانه ای مشخص نشده باشد، تولید اعداد تصادفی به تعداد عملیات در نمودار در زمان اضافه شدن عملیات تصادفی و تعداد دفعات اجرای نمودار بستگی دارد. در اجرای مشتاق، تولید اعداد تصادفی حالت دار به دانه جهانی، دانه تصادفی عملیات، و چند بار اجرای عملیات با دانه تصادفی داده شده بستگی دارد. برای اطلاعات بیشتر به tf.random.set_seed
مراجعه کنید.
کلاس v1.keras.utils.DeterministicRandomTestTool
زیر یک scope()
مدیر متن ارائه می دهد که می تواند عملیات تصادفی حالت دار را از همان seed در هر دو نمودار/جلسات TF1 و اجرای مشتاق استفاده کند.
این ابزار دو حالت تست را ارائه می دهد:
-
constant
که از یک دانه برای هر عملیات استفاده می کند مهم نیست که چند بار فراخوانی شده باشد و -
num_random_ops
که از تعداد عملیات تصادفی حالتی مشاهده شده قبلی به عنوان دانه عملیات استفاده می کند.
این هم برای عملیات تصادفی حالتی که برای ایجاد و مقداردهی اولیه متغیرها استفاده می شود و هم برای عملیات تصادفی حالتی که در محاسبات استفاده می شود (مانند لایه های حذفی) صدق می کند.
سه تانسور تصادفی ایجاد کنید تا نشان دهید چگونه از این ابزار برای تطبیق تولید اعداد تصادفی حالتی بین جلسات و اجرای مشتاق استفاده کنید.
random_tool = v1.keras.utils.DeterministicRandomTestTool()
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 = v1.keras.utils.DeterministicRandomTestTool()
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
هستید که اطمینان شما را در آزمون هم ارزی عددی کاهش میدهد (مثلاً اگر چندین وزن مقدار اولیه یکسانی داشته باشند)، میتوانید از حالت num_random_ops
برای جلوگیری از این امر استفاده کنید. در حالت num_random_ops
، اعداد تصادفی تولید شده به ترتیب عملیات های تصادفی در برنامه بستگی دارد.
random_tool = v1.keras.utils.DeterministicRandomTestTool(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 = v1.keras.utils.DeterministicRandomTestTool(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 = v1.keras.utils.DeterministicRandomTestTool(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 = v1.keras.utils.DeterministicRandomTestTool(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())
برای اجازه دادن به اشکالزدایی تغییرات به دلیل ترتیب ردیابی، DeterministicRandomTestTool
در حالت num_random_ops
به شما امکان میدهد ببینید که چه تعداد عملیات تصادفی با ویژگی operation_seed
ردیابی شدهاند.
random_tool = v1.keras.utils.DeterministicRandomTestTool(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
اگر میخواهید ترتیب ردیابی متفاوتی را در آزمایشهای خود در نظر بگیرید، حتی میتوانید عملیات_seed افزایش خودکار operation_seed
به صراحت تنظیم کنید. به عنوان مثال، می توانید از این برای مطابقت دادن تولید اعداد تصادفی بین دو ترتیب برنامه مختلف استفاده کنید.
random_tool = v1.keras.utils.DeterministicRandomTestTool(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 = v1.keras.utils.DeterministicRandomTestTool(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
با این حال، DeterministicRandomTestTool
استفاده مجدد از دانههای عملیاتی که قبلاً استفاده شده را مجاز نمیداند، بنابراین مطمئن شوید که توالیهای افزایشیافته خودکار نمیتوانند همپوشانی داشته باشند. این به این دلیل است که اجرای مشتاق، اعداد متفاوتی را برای استفادههای بعدی از یک عملیات مشابه تولید میکند، در حالی که نمودارها و جلسات TF1 این کار را نمیکنند، بنابراین بالا بردن یک خطا به حفظ نشست و تولید اعداد تصادفی حالت مشتاق در یک خط کمک میکند.
random_tool = v1.keras.utils.DeterministicRandomTestTool(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 `DeterministicRandomTestTool` 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.
تایید استنباط
اکنون می توانید از DeterministicRandomTestTool
استفاده کنید تا مطمئن شوید که مدل InceptionResnetV2
در استنتاج مطابقت دارد، حتی زمانی که از مقدار اولیه وزن تصادفی استفاده می کنید. برای شرایط تست قوی تر به دلیل مطابقت با ترتیب برنامه، از حالت num_random_ops
استفاده کنید.
random_tool = v1.keras.utils.DeterministicRandomTestTool(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 = v1.keras.utils.DeterministicRandomTestTool(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 DeterministicRandomTestTool:
np.testing.assert_allclose(tf1_regularization_loss, tf2_regularization_loss.numpy(), **tol_dict)
np.testing.assert_allclose(tf1_output, tf2_output.numpy(), **tol_dict)
تایید آموزش
از آنجایی که DeterministicRandomTestTool
برای همه عملیات تصادفی حالت دار (شامل مقداردهی اولیه وزن و محاسبات مانند لایه های حذفی) کار می کند، می توانید از آن برای تأیید مطابقت مدل ها در حالت آموزش نیز استفاده کنید. می توانید دوباره از حالت num_random_ops
استفاده کنید زیرا ترتیب برنامه عملیات تصادفی حالتی مطابقت دارد.
random_tool = v1.keras.utils.DeterministicRandomTestTool(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/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 = v1.keras.utils.DeterministicRandomTestTool(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 DeterministicRandomTestTool
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
، مقدار دهی اولیه متغیر را با ترتیب انصراف مطابق با ترتیب ایجاد شبکه به هم می زند.
از طرف دیگر، ابتدا قرار دادن tf.keras.layers.Layer
decorator در یک مدل کاربردی Keras و تنها پس از آن فراخوانی مدل با training=True
معادل مقداردهی اولیه همه متغیرها و سپس استفاده از لایه dropout است. این یک ترتیب ردیابی متفاوت و مجموعه متفاوتی از اعداد تصادفی ایجاد می کند.
با این حال، mode='constant'
به این تفاوتها در ترتیب ردیابی حساس نیست و حتی در هنگام جاسازی لایه در یک مدل عملکردی Keras، بدون کار اضافی عبور میکند.
random_tool = v1.keras.utils.DeterministicRandomTestTool()
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 = v1.keras.utils.DeterministicRandomTestTool()
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. ' /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) Regularization loss: tf.Tensor(1.2239964, shape=(), dtype=float32)
# Verify that the regularization loss and output both match
# when using the DeterministicRandomTestTool
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 هستند، حتی اگر فرمولهای ریاضی که آنها ارائه میکنند یکسان باشند. احتمال کمتری وجود دارد که این مشکل در اجراهای آموزشی شما باشد، اما ممکن است به تحمل عددی بالاتری در آزمونهای واحد هم ارزی نیاز داشته باشد.