ช่วยปกป้อง 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 modeling shim ที่ใช้กับ 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.

หากคุณกำลังใส่รหัสส่งต่อที่ไม่สำคัญลงใน 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_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 แบบ 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/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)

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

ต่อไปนี้คือสาเหตุทั่วไปบางประการที่ทำให้โมเดลของคุณอาจสร้างน้ำหนักใหม่โดยไม่ได้ตั้งใจ แทนที่จะใช้น้ำหนักที่มีอยู่ซ้ำ:

  1. มันใช้การเรียก tf.Variable อย่างชัดเจนโดยไม่ต้องใช้ tf.Variables ที่สร้างไว้แล้วซ้ำ แก้ไขปัญหานี้โดยตรวจสอบก่อนว่าไม่ได้สร้างไว้หรือไม่ จากนั้นจึงนำสิ่งที่มีอยู่กลับมาใช้ใหม่
  2. มันสร้างเลเยอร์ Keras หรือโมเดลโดยตรงในการส่งต่อในแต่ละครั้ง (ตรงข้ามกับ tf.compat.v1.layers ) แก้ไขปัญหานี้โดยตรวจสอบก่อนว่าไม่ได้สร้างไว้หรือไม่ จากนั้นจึงนำสิ่งที่มีอยู่กลับมาใช้ใหม่
  3. มันถูกสร้างขึ้นบน tf.compat.v1.layers แต่ล้มเหลวในการกำหนดชื่อที่ชัดเจนของ compat.v1.layers ทั้งหมดหรือรวมการใช้งาน compat.v1.layer ของคุณภายในชื่อ 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 ที่ใช้แผ่นชิมผ่านการทดสอบนี้ อย่างไรก็ตาม ในกรณีที่ไม่ตรงกัน คุณสามารถเรียกใช้ผ่านส่วนต่าง (ข้อความหรืออื่น ๆ ) เพื่อดูว่าความแตกต่างอยู่ที่ไหน

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

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

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

  • บางครั้งคุณอาจพบว่า tf.Variable s, tf.keras.layers.Layer s หรือ tf.keras.Model ที่สร้างขึ้นในการส่งต่อของลูปการฝึกของคุณหายไปจากรายการตัวแปร 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 เมื่อคุณลบแหล่งที่มาของการสุ่มออก และเลเยอร์ InceptionResnetV2 ที่เข้ากันได้กับ TF2 จะผ่านการทดสอบ

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

ตาข่ายแบบบางจำนวนมาก (และรุ่นอื่นๆ) ที่สะดวกสบายยังมีจุดสิ้นสุดระดับกลางที่คุณตรวจสอบได้

ขั้นตอนที่ 4: จัดแนวการสร้างตัวเลขสุ่ม ตรวจสอบความเท่าเทียมกันของตัวเลขทั้งในการฝึกและการอนุมาน

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

คุณสามารถทำได้โดยใช้เครื่องมือทดสอบด้านล่างเพื่อสร้างความหมายของการสร้างตัวเลขสุ่มที่ตรงกันระหว่างกราฟ/เซสชัน TF1.x และการดำเนินการที่กระตือรือร้น

กราฟ/เซสชันดั้งเดิมของ TF1 และการดำเนินการที่กระตือรือร้นของ TF2 ใช้ความหมายการสร้างตัวเลขสุ่มแบบระบุสถานะที่แตกต่างกัน

ใน tf.compat.v1.Session หากไม่มีการระบุเมล็ด การสร้างตัวเลขสุ่มจะขึ้นอยู่กับจำนวนการดำเนินการในกราฟ ณ เวลาที่เพิ่มการดำเนินการแบบสุ่ม และจำนวนครั้งที่รันกราฟ ในการดำเนินการอย่างกระฉับกระเฉง การสร้างหมายเลขสุ่มแบบเก็บสถานะขึ้นอยู่กับโกลบอลซีด เมล็ดสุ่มปฏิบัติการ และจำนวนครั้งที่ดำเนินการกับการดำเนินการกับเมล็ดสุ่มที่ให้มา ดู tf.random.set_seed สำหรับข้อมูลเพิ่มเติม

คลาส v1.keras.utils.DeterministicRandomTestTool ต่อไปนี้มี scope() ที่สามารถทำให้การดำเนินการสุ่มเก็บสถานะใช้เมล็ดพันธุ์เดียวกันในทั้งกราฟ/เซสชัน TF1 และการดำเนินการที่กระตือรือร้น

เครื่องมือนี้มีโหมดการทดสอบสองโหมด:

  1. constant ที่ซึ่งใช้เมล็ดเดียวกันสำหรับทุกการดำเนินการไม่ว่าจะเรียกกี่ครั้งและ,
  2. num_random_ops ซึ่งใช้จำนวนของการดำเนินการสุ่ม stateful ที่สังเกตก่อนหน้านี้เป็นเมล็ดการดำเนินการ

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

สร้างเมตริกซ์สุ่มสามตัวเพื่อแสดงวิธีใช้เครื่องมือนี้เพื่อสร้างตัวเลขสุ่มแบบเก็บสถานะที่ตรงกันระหว่างเซสชันและการดำเนินการที่กระตือรือร้น

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

หากคุณต้องการพิจารณาลำดับการติดตามที่แตกต่างกันในการทดสอบของคุณ คุณสามารถตั้งค่า 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
ตัวยึดตำแหน่ง46

อย่างไรก็ตาม 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 ได้อีกครั้ง เนื่องจากลำดับโปรแกรมของ stateful 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 interleaves การเริ่มต้นตัวแปรด้วยลำดับการเลื่อนออกตามลำดับการสร้างเครือข่าย

ในทางกลับกัน ก่อนอื่นให้ใส่ tf.keras.layers.Layer decorator ใน Keras functional model จากนั้นจึงเรียกโมเดลด้วย 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

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

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

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 มีคุณสมบัติตัวเลขทศนิยมแตกต่างกันเล็กน้อยจากเครื่องมือเพิ่มประสิทธิภาพ TF2 แม้ว่าสูตรทางคณิตศาสตร์ที่พวกเขากำลังแสดงจะเหมือนกันก็ตาม สิ่งนี้ไม่น่าจะเป็นปัญหาในการฝึกซ้อมของคุณ แต่อาจต้องใช้ความอดทนเชิงตัวเลขที่สูงขึ้นในการทดสอบหน่วยสมมูล