TL;DR:您需要在创建模型的循环中重置随机生成器状态,方法是调用tf.random.set_seed。
解释:
神经网络由一系列数学运算组成。神经网络中最常见的操作可以视为以下线性方程:y=W*x+b,其中:
-
x 是输入
-
y 是输出
-
W是网络节点的权重
-
b 是网络节点的偏差。
(所有这些线性方程然后被非线性分隔以避免网络崩溃)。
大多数时候,神经网络的权重 (W) 是随机初始化的。这意味着每次你运行神经网络的训练时,你都会得到稍微不同的结果。如果您的模型足够稳健,那么这些结果将在统计上相似。
现在,如果想要重现一些精确的结果,权重的随机初始化可能是个问题。为了能够重现一些精确的数字,可以为随机生成器设置一个种子,以保证在程序执行期间,生成的随机数始终来自同一系列、相同的顺序。
在终端中两次运行同一个程序
考虑以下程序,名为network_with_seed.py:
import tensorflow as tf
tf.random.set_seed(0)
model = tf.keras.Sequential([tf.keras.layers.Dense(1,input_shape=(1,))])
print(model.trainable_weights)
该程序是一个简单的网络,具有一个与节点完全连接的层(也称为感知器)。该网络作为一个权重W,程序将打印。
如果我们第一次执行该程序,我们会得到:
$ python network_with_seed.py
[<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[-0.7206192]], dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
现在,如果我们再次执行该代码:
python network_with_seed.py
[<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[-0.7206192]], dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
感谢种子,我们得到了完全相同的权重值W、-0.7206192。
循环创建模型
现在,假设我们想要创建该网络两次,并获得一些数字,但我们想在一个 python 脚本中完成所有事情。
考虑一下那个程序,loop_network_with_seed.py:
import tensorflow as tf
tf.random.set_seed(0)
for idx in range(2):
print(f"Model {idx+1}")
model = tf.keras.Sequential([tf.keras.layers.Dense(1,input_shape=(1,))])
print(model.trainable_weights)
如果我们运行它
$ python loop_network_with_seed.py
Model 1
[<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[-0.7206192]], dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
Model 2
[<tf.Variable 'dense_1/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[0.19195998]], dtype=float32)>, <tf.Variable 'dense_1/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
如果我们只查看随机初始化的内核W,我们会看到,对于同一个模型,不同的值:-0.7206192, 0.19195998,与我们通过调用脚本network_with_seed.py。如果我们训练这两个网络,我们将不会得到完全相同的结果,因为它们的初始权重 W 是不同的。
请注意,如果您再次运行该程序,您将得到完全相同的结果,因为模型 1 的权重将再次设置为 -0.7206192,而模型 2 的权重 W 将是设置为 0.19195998,感谢种子。种子使每次运行之间的随机性可重现。
这是因为随机数生成器的内部状态。更多信息可以阅读documentation of tf.random.set_seed
重置随机发生器状态
如果您想要同一组参数的可重现结果,您需要在创建每个模型之前重置随机状态。通过设置种子,我们确保相同的随机值将用于我们的权重W。
考虑最终方案network_with_seed_in_loop.py
import tensorflow as tf
for _ in range(2):
print(f"Model {idx+1}")
tf.random.set_seed(0)
model = tf.keras.Sequential([tf.keras.layers.Dense(1,input_shape=(1,))])
print(model.trainable_weights)
输出:
$ python network_with_seed_in_loop.py`
Model 1
[<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[-0.7206192]], dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
Model 2
[<tf.Variable 'dense_1/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[-0.7206192]], dtype=float32)>, <tf.Variable 'dense_1/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
这里内部状态复位,每个网络的内核W相等。