Xác thực tính đúng đắn và sự tương đương về số

Xem trên TensorFlow.org Chạy trong Google Colab Xem trên GitHub Tải xuống sổ ghi chép

Khi di chuyển mã TensorFlow của bạn từ TF1.x sang TF2, bạn nên đảm bảo rằng mã đã di chuyển của bạn hoạt động theo cùng một cách trong TF2 như trong TF1.x.

Hướng dẫn này bao gồm các ví dụ về mã di chuyển với các phương thức tf.compat.v1.keras.utils.track_tf1_style_variables , áp dụng cho các phương thức tf.keras.layers.Layer . Đọc hướng dẫn lập bản đồ mô hình để tìm hiểu thêm về miếng chêm mô hình TF2.

Hướng dẫn này chi tiết các cách tiếp cận bạn có thể sử dụng để:

  • Xác thực tính đúng đắn của các kết quả thu được từ các mô hình đào tạo bằng cách sử dụng mã đã di chuyển
  • Xác thực tính tương đương bằng số của mã của bạn trên các phiên bản TensorFlow

Thành lập

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.

Nếu bạn đang đặt một đoạn mã chuyển tiếp không đáng kể vào miếng đệm, bạn muốn biết rằng nó đang hoạt động giống như cách nó đã làm trong TF1.x. Ví dụ: hãy xem xét việc thử đặt toàn bộ mô hình TF-Slim Inception-Resnet-v2 vào miếng đệm như sau:

# 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.

Khi nó xảy ra, lớp này thực sự hoạt động hoàn toàn tốt ngoài hộp (hoàn chỉnh với tính năng theo dõi mất mát chính quy chính xác).

Tuy nhiên, đây không phải là điều bạn muốn coi là đương nhiên. Thực hiện theo các bước dưới đây để xác minh rằng nó thực sự đang hoạt động như nó đã làm trong TF1.x, để quan sát sự tương đương số hoàn hảo. Các bước này cũng có thể giúp bạn phân tích tam giác phần nào của đường chuyển tiếp đang gây ra sự phân kỳ so với TF1.x (xác định xem sự phân kỳ phát sinh trong đường chuyển tiếp của mô hình so với phần khác của mô hình).

Bước 1: Xác minh các biến chỉ được tạo một lần

Điều đầu tiên bạn nên xác minh là bạn đã xây dựng đúng mô hình theo cách sử dụng lại các biến trong mỗi lần gọi thay vì vô tình tạo và sử dụng các biến mới mỗi lần. Ví dụ: nếu mô hình của bạn tạo một lớp Keras mới hoặc gọi tf.Variable trong mỗi lần gọi chuyển tiếp thì rất có thể sẽ không nắm bắt được các biến và tạo các biến mới mỗi lần.

Dưới đây là hai phạm vi trình quản lý ngữ cảnh mà bạn có thể sử dụng để phát hiện khi nào mô hình của bạn đang tạo các biến mới và gỡ lỗi phần nào của mô hình đang thực hiện điều đó.

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

Phạm vi đầu tiên ( assert_no_variable_creations() ) sẽ phát sinh lỗi ngay lập tức khi bạn thử tạo một biến trong phạm vi. Điều này cho phép bạn kiểm tra stacktrace (và sử dụng gỡ lỗi tương tác) để tìm ra chính xác dòng mã nào đã tạo ra một biến thay vì sử dụng lại một biến hiện có.

Phạm vi thứ hai ( catch_and_raise_created_variables() ) sẽ đưa ra một ngoại lệ ở cuối phạm vi nếu bất kỳ biến nào kết thúc được tạo. Ngoại lệ này sẽ bao gồm danh sách tất cả các biến được tạo trong phạm vi. Điều này rất hữu ích để tìm ra tập hợp tất cả các trọng lượng mà mô hình của bạn đang tạo ra trong trường hợp bạn có thể phát hiện ra các mẫu chung. Tuy nhiên, nó ít hữu ích hơn cho việc xác định các dòng mã chính xác nơi các biến đó được tạo.

Sử dụng cả hai phạm vi bên dưới để xác minh rằng lớp InceptionResnetV2 dựa trên shim không tạo bất kỳ biến mới nào sau lần gọi đầu tiên (có lẽ là đang sử dụng lại chúng).

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 '

Trong ví dụ dưới đây, hãy quan sát cách những trình trang trí này hoạt động trên một lớp tạo ra các trọng số mới không chính xác mỗi lần thay vì sử dụng lại các trọng số hiện có.

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

Bạn có thể sửa lớp bằng cách đảm bảo rằng nó chỉ tạo các trọng số một lần và sau đó sử dụng lại chúng mỗi lần.

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)

Xử lý sự cố

Dưới đây là một số lý do phổ biến khiến mô hình của bạn có thể vô tình tạo ra các trọng số mới thay vì sử dụng lại các trọng số hiện có:

  1. Nó sử dụng một cuộc gọi tf.Variable rõ ràng mà không sử dụng lại tf.Variables đã được tạo. Khắc phục điều này bằng cách kiểm tra trước nếu nó chưa được tạo, sau đó sử dụng lại những cái hiện có.
  2. Nó tạo ra một lớp hoặc mô hình Keras trực tiếp trong chuyển tiếp mỗi lần (trái ngược với tf.compat.v1.layers ). Khắc phục điều này bằng cách kiểm tra trước nếu nó chưa được tạo, sau đó sử dụng lại những cái hiện có.
  3. Nó được xây dựng trên đầu tf.compat.v1.layers nhưng không thể gán cho tất cả compat.v1.layers một tên rõ ràng hoặc để bao bọc việc sử dụng compat.v1.layer của bạn bên trong một variable_scope được đặt tên, khiến tên lớp được tạo tự động tăng lên mỗi cuộc gọi mô hình. Khắc phục điều này bằng cách đặt một tf.compat.v1.variable_scope có tên bên trong phương pháp trang trí miếng đệm của bạn để bao bọc tất cả việc sử dụng tf.compat.v1.layers của bạn.

Bước 2: Kiểm tra xem số lượng biến, tên và hình dạng có khớp nhau không

Bước thứ hai là đảm bảo lớp của bạn đang chạy trong TF2 tạo ra cùng một số trọng số, có cùng hình dạng, như mã tương ứng trong TF1.x.

Bạn có thể thực hiện kết hợp kiểm tra chúng theo cách thủ công để xem chúng khớp với nhau và thực hiện kiểm tra theo chương trình trong bài kiểm tra đơn vị như được hiển thị bên dưới.

# 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 '

Tiếp theo, làm tương tự đối với lớp bọc miếng đệm trong TF2. Lưu ý rằng mô hình cũng được gọi nhiều lần trước khi lấy các trọng lượng. Điều này được thực hiện để kiểm tra hiệu quả việc sử dụng lại biến.

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

Lớp InceptionResnetV2 dựa trên miếng đệm vượt qua bài kiểm tra này. Tuy nhiên, trong trường hợp chúng không khớp, bạn có thể chạy nó qua một điểm khác biệt (văn bản hoặc khác) để xem sự khác biệt nằm ở đâu.

Điều này có thể cung cấp manh mối về phần nào của mô hình không hoạt động như mong đợi. Với khả năng thực thi hăng hái, bạn có thể sử dụng pdb, gỡ lỗi tương tác và các điểm ngắt để tìm hiểu sâu hơn các phần của mô hình có vẻ đáng ngờ và gỡ lỗi những gì đang xảy ra một cách sâu sắc hơn.

Xử lý sự cố

  • Hãy chú ý đến tên của bất kỳ biến nào được tạo trực tiếp bởi các lệnh gọi tf.Variable rõ ràng tf.function khác đang hoạt động bình thường. Nếu đây là trường hợp của bạn, hãy điều chỉnh bài kiểm tra của bạn để giải thích bất kỳ ngữ nghĩa đặt tên hơi khác.

  • Đôi khi bạn có thể thấy rằng các tf.Variable s, tf.keras.layers.Layer s hoặc tf.keras.Model được tạo trong chuyển tiếp vòng lặp đào tạo của bạn bị thiếu trong danh sách các biến TF2 của bạn ngay cả khi chúng đã được thu thập bởi bộ sưu tập biến trong TF1.x. Khắc phục điều này bằng cách gán các biến / lớp / mô hình mà đường chuyển tiếp của bạn tạo cho các thuộc tính cá thể trong mô hình của bạn. Xem ở đây để biết thêm thông tin.

Bước 3: Đặt lại tất cả các biến, kiểm tra sự tương đương về số với tất cả tính ngẫu nhiên bị tắt

Bước tiếp theo là xác minh sự tương đương về số cho cả đầu ra thực tế và theo dõi tổn thất chính quy khi bạn sửa mô hình sao cho không có liên quan đến việc tạo số ngẫu nhiên (chẳng hạn như trong quá trình suy luận).

Cách chính xác để thực hiện việc này có thể phụ thuộc vào kiểu máy cụ thể của bạn, nhưng trong hầu hết các kiểu máy (chẳng hạn như kiểu này), bạn có thể thực hiện việc này bằng cách:

  1. Khởi tạo các trọng số đến cùng một giá trị mà không có ngẫu nhiên. Điều này có thể được thực hiện bằng cách đặt lại chúng về một giá trị cố định sau khi chúng đã được tạo.
  2. Chạy mô hình ở chế độ suy luận để tránh kích hoạt bất kỳ lớp bỏ nào có thể là nguồn ngẫu nhiên.

Đoạn mã sau minh họa cách bạn có thể so sánh kết quả TF1.x và TF2 theo cách này.

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)

Nhận kết quả 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)

Các con số khớp giữa TF1.x và TF2 khi bạn loại bỏ các nguồn ngẫu nhiên và lớp InceptionResnetV2 tương thích với TF2 vượt qua bài kiểm tra.

Nếu bạn đang quan sát kết quả phân kỳ cho các mô hình của riêng mình, bạn có thể sử dụng tính năng in hoặc pdb và gỡ lỗi tương tác để xác định vị trí và lý do tại sao kết quả bắt đầu phân kỳ. Thực hiện háo hức có thể làm cho điều này dễ dàng hơn đáng kể. Bạn cũng có thể sử dụng phương pháp cắt bỏ để chỉ chạy các phần nhỏ của mô hình trên các đầu vào trung gian cố định và cô lập nơi xảy ra sự phân kỳ.

Thuận tiện, nhiều lưới mỏng (và các kiểu khác) cũng để lộ các điểm cuối trung gian mà bạn có thể thăm dò.

Bước 4: Căn chỉnh việc tạo số ngẫu nhiên, kiểm tra sự tương đương của số trong cả đào tạo và suy luận

Bước cuối cùng là xác minh rằng mô hình TF2 khớp số với mô hình TF1.x, ngay cả khi tính đến việc tạo số ngẫu nhiên trong quá trình khởi tạo biến và trong chính quá trình chuyển tiếp (chẳng hạn như các lớp bỏ qua trong quá trình chuyển tiếp).

Bạn có thể thực hiện việc này bằng cách sử dụng công cụ kiểm tra bên dưới để làm cho ngữ nghĩa tạo số ngẫu nhiên khớp giữa biểu đồ / phiên TF1.x và thực thi mong muốn.

Các biểu đồ / phiên kế thừa TF1 và thực thi mong muốn TF2 sử dụng các ngữ nghĩa tạo số ngẫu nhiên có trạng thái khác nhau.

Trong tf.compat.v1.Session s, nếu không có hạt nào được chỉ định, việc tạo số ngẫu nhiên phụ thuộc vào số lượng phép toán có trong biểu đồ tại thời điểm thêm phép toán ngẫu nhiên và số lần chạy biểu đồ. Trong thực thi háo hức, việc tạo số ngẫu nhiên có trạng thái phụ thuộc vào hạt giống toàn cục, hạt ngẫu nhiên của hoạt động và số lần hoạt động với hoạt động với hạt ngẫu nhiên đã cho được chạy. Xem tf.random.set_seed để biết thêm thông tin.

Lớp v1.keras.utils.DeterministicRandomTestTool sau cung cấp scope() có thể thực hiện các hoạt động ngẫu nhiên có trạng thái sử dụng cùng một hạt giống trên cả biểu đồ / phiên TF1 và thực thi mong muốn.

Công cụ này cung cấp hai chế độ kiểm tra:

  1. constant sử dụng cùng một hạt giống cho mọi hoạt động bất kể nó đã được gọi bao nhiêu lần và,
  2. num_random_ops sử dụng số lượng các hoạt động ngẫu nhiên có trạng thái đã quan sát trước đó làm hạt giống hoạt động.

Điều này áp dụng cho cả các phép toán ngẫu nhiên có trạng thái được sử dụng để tạo và khởi tạo các biến và các phép toán ngẫu nhiên có trạng thái được sử dụng trong tính toán (chẳng hạn như cho các lớp bỏ học).

Tạo ba tenxơ ngẫu nhiên để chỉ ra cách sử dụng công cụ này để tạo số ngẫu nhiên có trạng thái khớp giữa các phiên và thực thi háo hức.

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)

Tuy nhiên, lưu ý rằng trong chế độ constant , vì bc được tạo ra với cùng một hạt giống và có cùng hình dạng, chúng sẽ có cùng giá trị.

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

Lần theo thứ tự

Nếu bạn lo lắng về việc một số số ngẫu nhiên khớp trong chế độ constant làm giảm sự tự tin của bạn trong kiểm tra tính tương đương số của bạn (ví dụ: nếu một số trọng số thực hiện cùng một lần khởi tạo), bạn có thể sử dụng chế độ num_random_ops để tránh điều này. Trong chế độ num_random_ops , các số ngẫu nhiên được tạo ra sẽ phụ thuộc vào thứ tự của các hoạt động ngẫu nhiên trong chương trình.

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)

Tuy nhiên, lưu ý rằng trong chế độ này, việc tạo ngẫu nhiên nhạy cảm với thứ tự chương trình, và do đó, các số ngẫu nhiên được tạo sau đây không khớp.

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

Để cho phép gỡ lỗi các biến thể do thứ tự theo dõi, DeterministicRandomTestTool ở chế độ num_random_ops cho phép bạn xem có bao nhiêu thao tác ngẫu nhiên đã được theo dõi với thuộc tính 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

Nếu bạn cần tính đến các thứ tự theo dõi khác nhau trong các thử nghiệm của mình, bạn thậm chí có thể đặt rõ ràng hàm Operation_seed tự operation_seed tăng dần. Ví dụ: bạn có thể sử dụng điều này để thực hiện việc tạo số ngẫu nhiên đối với hai thứ tự chương trình khác nhau.

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

Tuy nhiên, DeterministicRandomTestTool không cho phép sử dụng lại các hạt hoạt động đã được sử dụng, vì vậy hãy đảm bảo rằng các chuỗi tự động tăng dần không được chồng chéo lên nhau. Điều này là do việc thực thi háo hức tạo ra các số khác nhau cho các lần sử dụng tiếp theo của cùng một hạt hoạt động trong khi các biểu đồ và phiên TF1 thì không, do đó, việc tạo lỗi sẽ giúp giữ phiên và việc tạo số ngẫu nhiên ở trạng thái háo hức theo dòng.

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.

Xác minh suy luận

Bây giờ bạn có thể sử dụng Công cụ DeterministicRandomTestTool để đảm bảo mô hình InceptionResnetV2 khớp trong suy luận, ngay cả khi sử dụng khởi tạo trọng số ngẫu nhiên. Đối với điều kiện kiểm tra mạnh hơn do khớp với thứ tự chương trình, hãy sử dụng chế độ 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)

Đào tạo xác minh

Bởi vì Công cụ DeterministicRandomTestTool làm việc cho tất cả các hoạt động ngẫu nhiên ở trạng thái (bao gồm cả khởi tạo trọng số và tính toán, chẳng hạn như các lớp bỏ qua), bạn cũng có thể sử dụng nó để xác minh các mô hình phù hợp trong chế độ đào tạo. Bạn có thể sử dụng lại chế độ num_random_ops vì thứ tự chương trình của các hoạt động ngẫu nhiên có trạng thái khớp với nhau.

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)

Bây giờ bạn đã xác minh rằng mô hình InceptionResnetV2 đang chạy háo hức với các trình trang trí xung quanh tf.keras.layers.Layer về số khớp với mạng mỏng đang chạy trong biểu đồ và phiên TF1.

Ví dụ: gọi trực tiếp lớp InceptionResnetV2 với khởi tạo biến training=True xen kẽ với thứ tự bỏ theo thứ tự tạo mạng.

Mặt khác, đầu tiên đặt trình trang trí tf.keras.layers.Layer vào mô hình chức năng Keras và chỉ sau đó gọi mô hình với training=True tương đương với việc khởi tạo tất cả các biến sau đó sử dụng lớp bỏ qua. Điều này tạo ra một thứ tự truy tìm khác nhau và một tập hợp các số ngẫu nhiên khác.

Tuy nhiên, mode='constant' không nhạy cảm với những khác biệt này trong thứ tự truy tìm và sẽ trôi qua mà không cần thực hiện thêm công việc ngay cả khi nhúng lớp vào mô hình chức năng 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)

Bước 3b hoặc 4b (tùy chọn): Kiểm tra với các điểm kiểm tra đã có từ trước

Sau bước 3 hoặc bước 4 ở trên, có thể hữu ích khi chạy các bài kiểm tra tương đương số của bạn khi bắt đầu từ các điểm kiểm tra dựa trên tên đã có từ trước nếu bạn có một số. Điều này có thể kiểm tra cả việc tải điểm kiểm tra kế thừa của bạn có hoạt động chính xác và bản thân mô hình đang hoạt động bình thường hay không. Hướng dẫn Sử dụng lại các điểm kiểm tra TF1.x bao gồm cách sử dụng lại các điểm kiểm tra TF1.x đã có từ trước của bạn và chuyển chúng sang các điểm kiểm tra TF2.

Kiểm tra và khắc phục sự cố bổ sung

Khi bạn thêm nhiều bài kiểm tra tương đương số hơn, bạn cũng có thể chọn thêm một bài kiểm tra xác minh sự phù hợp của tính toán gradient (hoặc thậm chí là các bản cập nhật trình tối ưu hóa của bạn).

Việc nhân giống ngược và tính toán độ dốc dễ bị mất ổn định số dấu phẩy động hơn là chuyển tiếp mô hình. Điều này có nghĩa là khi các bài kiểm tra tương đương của bạn bao gồm nhiều phần không riêng biệt trong quá trình đào tạo của bạn, bạn có thể bắt đầu thấy sự khác biệt về số lượng không nhỏ giữa việc chạy hoàn toàn hăng hái và đồ thị TF1 của bạn. Điều này có thể do tối ưu hóa đồ thị của TensorFlow thực hiện những việc như thay thế các biểu thức phụ trong một biểu đồ với ít phép toán hơn.

Để phân biệt xem có khả năng xảy ra trường hợp này hay không, bạn có thể so sánh mã TF1 của mình với tính toán TF2 xảy ra bên trong tf.function (áp dụng các đường đi tối ưu hóa đồ thị giống như đồ thị TF1 của bạn) thay vì một tính toán hoàn toàn mong muốn. Ngoài ra, bạn có thể thử sử dụng tf.config.optimizer.set_experimental_options để tắt các bước tối ưu hóa, chẳng hạn như "arithmetic_optimization" trước khi tính toán TF1 của bạn để xem liệu kết quả có gần với kết quả tính toán TF2 của bạn hơn không. Trong các lần chạy đào tạo thực tế của bạn, bạn nên sử dụng tf.function với các đường chuyền tối ưu hóa được bật vì lý do hiệu suất, nhưng bạn có thể thấy hữu ích khi tắt chúng trong các bài kiểm tra đơn vị tương đương số của mình.

Tương tự, bạn cũng có thể thấy rằng trình tối ưu hóa tf.compat.v1.train và trình tối ưu hóa TF2 có các thuộc tính số dấu phẩy động hơi khác so với trình tối ưu hóa TF2, ngay cả khi các công thức toán học mà chúng đang đại diện là giống nhau. Điều này ít có khả năng là một vấn đề trong quá trình đào tạo của bạn, nhưng nó có thể yêu cầu dung sai số cao hơn trong các bài kiểm tra đơn vị tương đương.