在相同的数据上训练相同的模型会产生截然不同的测试精度
Training the same model on the same data yielding extremely different test accuracy
我的模型得到的测试精度非常不一致,但无法弄清楚原因。
我试图对一些 TensorFlow/Keras 的东西进行基准测试,发现我的结果不可靠。不是时间,而是模型的测试精度。在某些运行中,模型会达到测试精度 = 0.65,有时它只会达到 0.35。相同的架构,相同的优化器在相同的数据集上训练,一切都相同。
我尝试删除大多数随机源,例如使用相同的 numpy rng 种子、TensorFlow rng 种子、在运行之间重置 TensorFlow 后端,以及使用相同的种子对数据集进行混洗等。问题仍然存在。
谁能帮我弄清楚我做错了什么?
Here is a reproducible example 在 Google Colab 上,
但是这个问题也会在本地发生在 Python、TensorFlow 和 Cuda/Cudnn.
的不同版本上
而here is the dataset我正在使用。它只是 CIFAR-10,训练分区分为训练和验证。
编辑:
问题似乎是我在 .shuffle
和 batch
之间的一些交互引起的,更具体地说是它们的参数 buffer_size
、reshuffle_each_iteration
和 drop_remainder
。
以下组合似乎可以避免该问题(但会导致大量内存使用):
dataset.shuffle(buffer_size=dataset.cardinality().numpy(),
reshuffle_each_iteration=False,
seed=rng_seed)
.batch(batch_size=batch_size,
drop_remainder=True)
看来您遇到了 2 个问题,每个问题都导致了不同的结果 运行:
- 使用 GPU
- 使用
tf.data.AUTOTUNE
1 的解决方案:
将您在 Colab 中的 运行时间切换为“None”
2 的解决方案:
将您的 return 从 tf.data.AUTOTUNE
更改为固定的 BatchSize(例如 128):
train.prefetch(128),
val.prefetch(128),
test.prefetch(128),
你也可以看看我改编的your code here.
为了速度,在接下来的所有实验中,我都将epoch固定为1
。
最暴力的解决方案之一是在构建模型之前添加以下两行,不需要任何其他更改并支持random
:
tf.keras.utils.set_random_seed(some_seed)
tf.config.experimental.enable_op_determinism()
即:
def why_u_inconsistent():
# tf.keras.backend.clear_session()
# tf.random.set_seed(42)
# np.random.seed(42)
tf.keras.utils.set_random_seed(0)
tf.config.experimental.enable_op_determinism()
train, val, test = get_train_val_test()
model = get_model()
model.fit(train, validation_data=val, epochs=1, verbose=0)
scores = model.evaluate(test, verbose=0)
for name, value in zip(model.metrics_names, scores):
print(f"test {name}: {value}")
输出是(你会满意的,因为它们完全一样):
run: 0
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 1
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 2
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 3
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 4
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
P.S。在设置随机种子时,我们应该使用tf.keras.utils.set_random_seed
,包括random
、numpy.random
和tensorflow.random
。他们实际上使用了三个不同的随机随机实例,即他们的随机种子不会相互影响,但确实影响了我们的代码,因为我们不可避免地使用它们。当然也可以单独设置,tf.keras.utils.set_random_seed
只是一种方便的方式。不幸的是,您的原始代码没有设置基本模块的种子random
。
然后,我检查了每个 run_nr
的数据管道输出是否必须相同。我将每个批次的reduce_mean
写入csv
文件来比较每个run_nr
的输出,发现它们根本没有区别。所以在这里,我们可以将问题绘制为 determinism
problem and I have explained
关于你的问题,我找到了更好的解释,即2个关键点momentum
和learning_rate
使你的算法遇到determinism
问题。如果我们不使用tf.config.experimental.enable_op_determinism
,任何算法在最后几位精度都是不确定的(可能是cuda
或TensorFloat-32
dtype造成的),但是momentum
累积不确定性误差和 learning_rate
放大误差。如果我们使用
SGD
(不是说你必须使用它)没有 momentum
的优化器,像这样:
...
optimizer = tf.keras.optimizers.SGD(0.001)
model.compile(
optimizer=optimizer,
loss="categorical_crossentropy",
metrics=["accuracy"],
)
...
def why_u_inconsistent(index):
tf.keras.utils.set_random_seed(0)
# tf.config.experimental.enable_op_determinism()
train, val, test = get_train_val_test()
model = get_model()
model.fit(train, validation_data=val, epochs=1, verbose=0)
scores = model.evaluate(test, verbose=0)
for name, value in zip(model.metrics_names, scores):
print(f"test {name}: {value}")
...
1
纪元之后的输出将如下所示,其中输出非常接近:
run: 0
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.20281720161438
test accuracy: 0.1808999925851822
=====
run: 1
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.202397346496582
test accuracy: 0.1809999942779541
=====
run: 2
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.2010912895202637
test accuracy: 0.18019999563694
=====
run: 3
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.200165271759033
test accuracy: 0.18230000138282776
=====
run: 4
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.2025880813598633
test accuracy: 0.18019999563694
=====
所以让我们得出一个更好的结论:
使用tf.keras.utils.set_random_seed(some_seed)
控制所有需要的随机行为
尽可能优化算法,减轻计算精度的影响。即让算法不产生或积累精度上的小误差。可调节的键有很多,不仅是optimizer
或optimizer
的参数,还有其他东西,如bacth_size
、gradient_clip
、gradient_norm
等。
如果你想获得完全一致的可重现结果并接受其副作用(减速),请使用tf.config.experimental.enable_op_determinism
。
我的模型得到的测试精度非常不一致,但无法弄清楚原因。
我试图对一些 TensorFlow/Keras 的东西进行基准测试,发现我的结果不可靠。不是时间,而是模型的测试精度。在某些运行中,模型会达到测试精度 = 0.65,有时它只会达到 0.35。相同的架构,相同的优化器在相同的数据集上训练,一切都相同。
我尝试删除大多数随机源,例如使用相同的 numpy rng 种子、TensorFlow rng 种子、在运行之间重置 TensorFlow 后端,以及使用相同的种子对数据集进行混洗等。问题仍然存在。
谁能帮我弄清楚我做错了什么?
Here is a reproducible example 在 Google Colab 上, 但是这个问题也会在本地发生在 Python、TensorFlow 和 Cuda/Cudnn.
的不同版本上而here is the dataset我正在使用。它只是 CIFAR-10,训练分区分为训练和验证。
编辑:
问题似乎是我在 .shuffle
和 batch
之间的一些交互引起的,更具体地说是它们的参数 buffer_size
、reshuffle_each_iteration
和 drop_remainder
。
以下组合似乎可以避免该问题(但会导致大量内存使用):
dataset.shuffle(buffer_size=dataset.cardinality().numpy(),
reshuffle_each_iteration=False,
seed=rng_seed)
.batch(batch_size=batch_size,
drop_remainder=True)
看来您遇到了 2 个问题,每个问题都导致了不同的结果 运行:
- 使用 GPU
- 使用
tf.data.AUTOTUNE
1 的解决方案: 将您在 Colab 中的 运行时间切换为“None”
2 的解决方案:
将您的 return 从 tf.data.AUTOTUNE
更改为固定的 BatchSize(例如 128):
train.prefetch(128),
val.prefetch(128),
test.prefetch(128),
你也可以看看我改编的your code here.
为了速度,在接下来的所有实验中,我都将epoch固定为1
。
最暴力的解决方案之一是在构建模型之前添加以下两行,不需要任何其他更改并支持random
:
tf.keras.utils.set_random_seed(some_seed)
tf.config.experimental.enable_op_determinism()
即:
def why_u_inconsistent():
# tf.keras.backend.clear_session()
# tf.random.set_seed(42)
# np.random.seed(42)
tf.keras.utils.set_random_seed(0)
tf.config.experimental.enable_op_determinism()
train, val, test = get_train_val_test()
model = get_model()
model.fit(train, validation_data=val, epochs=1, verbose=0)
scores = model.evaluate(test, verbose=0)
for name, value in zip(model.metrics_names, scores):
print(f"test {name}: {value}")
输出是(你会满意的,因为它们完全一样):
run: 0
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 1
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 2
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 3
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
=====
run: 4
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 3.872633934020996
test accuracy: 0.1818999946117401
P.S。在设置随机种子时,我们应该使用tf.keras.utils.set_random_seed
,包括random
、numpy.random
和tensorflow.random
。他们实际上使用了三个不同的随机随机实例,即他们的随机种子不会相互影响,但确实影响了我们的代码,因为我们不可避免地使用它们。当然也可以单独设置,tf.keras.utils.set_random_seed
只是一种方便的方式。不幸的是,您的原始代码没有设置基本模块的种子random
。
然后,我检查了每个 run_nr
的数据管道输出是否必须相同。我将每个批次的reduce_mean
写入csv
文件来比较每个run_nr
的输出,发现它们根本没有区别。所以在这里,我们可以将问题绘制为 determinism
problem and I have explained
关于你的问题,我找到了更好的解释,即2个关键点momentum
和learning_rate
使你的算法遇到determinism
问题。如果我们不使用tf.config.experimental.enable_op_determinism
,任何算法在最后几位精度都是不确定的(可能是cuda
或TensorFloat-32
dtype造成的),但是momentum
累积不确定性误差和 learning_rate
放大误差。如果我们使用
SGD
(不是说你必须使用它)没有 momentum
的优化器,像这样:
...
optimizer = tf.keras.optimizers.SGD(0.001)
model.compile(
optimizer=optimizer,
loss="categorical_crossentropy",
metrics=["accuracy"],
)
...
def why_u_inconsistent(index):
tf.keras.utils.set_random_seed(0)
# tf.config.experimental.enable_op_determinism()
train, val, test = get_train_val_test()
model = get_model()
model.fit(train, validation_data=val, epochs=1, verbose=0)
scores = model.evaluate(test, verbose=0)
for name, value in zip(model.metrics_names, scores):
print(f"test {name}: {value}")
...
1
纪元之后的输出将如下所示,其中输出非常接近:
run: 0
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.20281720161438
test accuracy: 0.1808999925851822
=====
run: 1
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.202397346496582
test accuracy: 0.1809999942779541
=====
run: 2
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.2010912895202637
test accuracy: 0.18019999563694
=====
run: 3
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.200165271759033
test accuracy: 0.18230000138282776
=====
run: 4
Found 42500 files belonging to 10 classes.
Found 7500 files belonging to 10 classes.
Found 10000 files belonging to 10 classes.
test loss: 2.2025880813598633
test accuracy: 0.18019999563694
=====
所以让我们得出一个更好的结论:
使用
tf.keras.utils.set_random_seed(some_seed)
控制所有需要的随机行为尽可能优化算法,减轻计算精度的影响。即让算法不产生或积累精度上的小误差。可调节的键有很多,不仅是
optimizer
或optimizer
的参数,还有其他东西,如bacth_size
、gradient_clip
、gradient_norm
等。如果你想获得完全一致的可重现结果并接受其副作用(减速),请使用
tf.config.experimental.enable_op_determinism
。