ช่วยปกป้อง Great Barrier Reef กับ TensorFlow บน Kaggle เข้าร่วมท้าทาย

ตรวจสอบความถูกต้อง & เทียบเท่าตัวเลข

ดูบน 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 shims

คู่มือนี้มีรายละเอียดแนวทางที่คุณสามารถใช้เพื่อ:

  • ตรวจสอบความถูกต้องของผลลัพธ์ที่ได้จากแบบจำลองการฝึกอบรมโดยใช้รหัสที่ย้ายมา
  • ตรวจสอบความสมมูลเชิงตัวเลขของโค้ดของคุณในเวอร์ชัน TensorFlow

ติดตั้ง

# Note that the model mapping shim is available only in TF 2.7.
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: 3081, done.[K
remote: Counting objects: 100% (3081/3081), done.[K
remote: Compressing objects: 100% (2607/2607), done.[K
remote: Total 3081 (delta 774), reused 1338 (delta 433), pack-reused 0[K
Receiving objects: 100% (3081/3081), 33.33 MiB | 19.59 MiB/s, done.
Resolving deltas: 100% (774/774), done.

หากคุณกำลังใส่รหัสส่งต่อที่ไม่สำคัญลงใน shim คุณต้องการรู้ว่ามันทำงานแบบเดียวกับที่ทำใน 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_12277/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_12277/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_12277/3224979076.py", line 6, in call
    var = tf.Variable(initial_value=2.0)
  File "/tmp/ipykernel_12277/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 ภายในวิธีการชิมตกแต่งของคุณที่ wraps ทั้งหมดของคุณ 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-11-13 02:33:12.818082: 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 ที่ใช้แผ่นชิมผ่านการทดสอบนี้ อย่างไรก็ตาม ในกรณีที่ไม่ตรงกัน คุณสามารถเรียกใช้ผ่านส่วนต่าง (ข้อความหรืออื่น ๆ ) เพื่อดูว่าความแตกต่างอยู่ที่ไหน

ข้อมูลนี้สามารถให้เบาะแสได้ว่าส่วนใดของแบบจำลองที่ไม่เป็นไปตามที่คาดไว้ คุณสามารถใช้ pdb การดีบักเชิงโต้ตอบ และจุดสั่งหยุดเพื่อเจาะลึกเข้าไปในส่วนต่างๆ ของโมเดลที่ดูน่าสงสัย และดีบักสิ่งที่ผิดพลาดในเชิงลึกยิ่งขึ้น

การแก้ไขปัญหา

  • ใส่ใจกับชื่อของตัวแปรใด ๆ ที่สร้างขึ้นโดยตรงโดยชัดเจน tf.Variable โทรและชั้น Keras / รุ่นเป็นชื่อตัวแปรรุ่นความหมายของพวกเขาอาจแตกต่างกันเล็กน้อยระหว่างกราฟ TF1.x และการทำงาน TF2 เช่นการดำเนินการและความกระตือรือร้นที่ tf.function แม้ว่าทุกอย่าง อย่างอื่นทำงานอย่างถูกต้อง หากเป็นกรณีนี้สำหรับคุณ ให้ปรับการทดสอบของคุณเพื่อพิจารณาความหมายการตั้งชื่อที่แตกต่างกันเล็กน้อย

  • บางครั้งคุณอาจพบว่า tf.Variable s, tf.keras.layers.Layer s หรือ tf.keras.Model s สร้างขึ้นในห่วงไปข้างหน้าผ่านการฝึกอบรมของคุณจะหายไปจากรายการตัวแปร TF2 ของคุณแม้ว่าพวกเขาจะถูกจับโดยคอลเลกชันตัวแปร ใน TF1.x แก้ไขปัญหานี้โดยกำหนดตัวแปร/เลเยอร์/โมเดลที่ forward pass ของคุณสร้างให้กับแอตทริบิวต์ของอินสแตนซ์ในโมเดลของคุณ ดู ที่นี่ สำหรับข้อมูลเพิ่มเติม

ขั้นตอนที่ 3: รีเซ็ตตัวแปรทั้งหมด ตรวจสอบการเทียบเท่าตัวเลขโดยปิดการสุ่มทั้งหมด

ขั้นตอนต่อไปคือการตรวจสอบความสมมูลเชิงตัวเลขสำหรับทั้งเอาต์พุตจริงและการติดตามการสูญเสียการทำให้เป็นมาตรฐาน เมื่อคุณแก้ไขแบบจำลองโดยที่ไม่มีการสร้างตัวเลขสุ่มที่เกี่ยวข้อง (เช่น ในระหว่างการอนุมาน)

วิธีที่แน่นอนในการทำเช่นนี้อาจขึ้นอยู่กับรุ่นเฉพาะของคุณ แต่ในรุ่นส่วนใหญ่ (เช่นรุ่นนี้) คุณสามารถทำได้โดย:

  1. การเริ่มต้นน้ำหนักให้เป็นค่าเดียวกันโดยไม่มีการสุ่ม ซึ่งสามารถทำได้โดยการรีเซ็ตเป็นค่าคงที่หลังจากสร้างแล้ว
  2. การรันโมเดลในโหมดอนุมานเพื่อหลีกเลี่ยงการทริกเกอร์เลเยอร์ dropout ใด ๆ ที่อาจเป็นแหล่งของการสุ่ม

รหัสต่อไปนี้สาธิตวิธีที่คุณสามารถเปรียบเทียบผลลัพธ์ 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 s หากไม่มีเมล็ดจะระบุรุ่นจำนวนสุ่มขึ้นอยู่กับจำนวนการดำเนินงานอยู่ในกราฟในขณะที่การดำเนินการสุ่มจะมีการเพิ่มและจำนวนครั้งที่กราฟเป็นระยะ ในการดำเนินการอย่างกระฉับกระเฉง การสร้างหมายเลขสุ่มแบบเก็บสถานะขึ้นอยู่กับโกลบอลซีด เมล็ดสุ่มปฏิบัติการ และจำนวนครั้งที่ดำเนินการกับการดำเนินการกับเมล็ดสุ่มที่ให้มา ดู 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 โหมดที่สร้างตัวเลขสุ่มจะขึ้นอยู่กับการสั่งซื้อของ 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

หากคุณจำเป็นต้องบัญชีที่แตกต่างกันเพื่อร่องรอยในการทดสอบของคุณคุณยังสามารถตั้งค่าอัตโนมัติ incrementing 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 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=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 ข้างต้น การเรียกใช้การทดสอบการสมมูลเชิงตัวเลขอาจเป็นประโยชน์เมื่อเริ่มต้นจากจุดตรวจสอบตามชื่อที่มีอยู่แล้ว หากคุณมีบางส่วน วิธีนี้สามารถทดสอบได้ว่าการโหลดจุดตรวจแบบเดิมของคุณทำงานอย่างถูกต้องและตัวแบบทำงานถูกต้องหรือไม่ ด่าน Reusing TF1.x แนะนำ ครอบคลุมวิธีการที่จะนำมาใช้ใหม่ที่มีอยู่ก่อนด่าน TF1.x คุณและโอนพวกเขาไปยังจุดตรวจ TF2

การทดสอบและการแก้ไขปัญหาเพิ่มเติม

เมื่อคุณเพิ่มการทดสอบการสมมูลเชิงตัวเลขมากขึ้น คุณอาจเลือกที่จะเพิ่มการทดสอบที่ยืนยันว่าการคำนวณการไล่ระดับสีของคุณตรงกัน (หรือแม้แต่การอัปเดตเครื่องมือเพิ่มประสิทธิภาพของคุณ)

Backpropagation และการคำนวณไล่ระดับมีแนวโน้มที่จะเกิดความไม่เสถียรของตัวเลขทศนิยมมากกว่าการส่งต่อแบบจำลอง ซึ่งหมายความว่าเนื่องจากการทดสอบความสมมูลครอบคลุมส่วนที่ไม่ได้แยกจากการฝึกของคุณ คุณอาจเริ่มเห็นความแตกต่างของตัวเลขที่ไม่สำคัญระหว่างการวิ่งอย่างกระตือรือร้นและกราฟ TF1 ของคุณ ซึ่งอาจเกิดจากการเพิ่มประสิทธิภาพกราฟของ TensorFlow ที่ทำสิ่งต่างๆ เช่น แทนที่นิพจน์ย่อยในกราฟด้วยการดำเนินการทางคณิตศาสตร์น้อยลง

เพื่อแยกว่าเรื่องนี้น่าจะเป็นกรณีที่คุณสามารถเปรียบเทียบรหัส TF1 TF2 ของคุณเพื่อการคำนวณที่เกิดขึ้นภายในของ tf.function (ซึ่งนำไปใช้ในการเพิ่มประสิทธิภาพกราฟผ่านเช่นกราฟ TF1 ของคุณ) มากกว่าที่จะคำนวณความกระตือรือร้นอย่างหมดจด หรือคุณสามารถลองใช้ tf.config.optimizer.set_experimental_options ที่จะปิดการใช้งานการเพิ่มประสิทธิภาพผ่านเช่น "arithmetic_optimization" ก่อนที่จะคำนวณ TF1 คุณเพื่อดูว่าผลจะจบลงตัวเลขที่ใกล้ชิดกับผลการคำนวณ TF2 ของคุณ ในการฝึกอบรมการทำงานที่แท้จริงของคุณจึงแนะนำให้คุณใช้ tf.function กับการเพิ่มประสิทธิภาพผ่านการเปิดใช้งานสำหรับเหตุผลประสิทธิภาพ แต่คุณอาจพบว่ามันมีประโยชน์ในการปิดการใช้งานพวกเขาในการทดสอบหน่วยความเท่าเทียมตัวเลขของคุณ

ในทำนองเดียวกันคุณอาจพบว่า tf.compat.v1.train เพิ่มประสิทธิภาพและเพิ่มประสิทธิภาพ TF2 มีแตกต่างกันเล็กน้อย floating point numerics คุณสมบัติกว่า TF2 เพิ่มประสิทธิภาพแม้ว่าสูตรทางคณิตศาสตร์ที่พวกเขาเป็นตัวแทนเหมือนกัน สิ่งนี้ไม่น่าจะเป็นปัญหาในการฝึกซ้อมของคุณ แต่อาจต้องใช้ความอดทนเชิงตัวเลขที่สูงขึ้นในการทดสอบหน่วยสมมูล