TensorFlow CNN结果不稳定求助:设随机种子仍无法复现结果
我帮你梳理下几个可能导致结果不稳定的关键问题,结合你的代码逐一分析:
1. 随机种子的覆盖与图上下文冲突
你在train_network函数里先调用了tf.reset_default_graph(),然后设置了Python、NumPy和TensorFlow的种子,但之后又用with tf.Graph().as_default():创建了新的图上下文——虽然在新图里又补了tf.set_random_seed(0),但种子设置的时机容易出现上下文冲突。
建议把所有种子设置统一放在新图上下文内部,确保种子作用于当前构建的图:
with tf.Graph().as_default(): # 所有种子设置放在这里,避免跨图冲突 random.seed(0) np.random.seed(0) tf.set_random_seed(0) # 后续模型构建、会话创建代码...
另外,Python的random模块种子只需要在程序启动时设置一次,不需要在每个函数里重复设置,避免不必要的重复操作。
2. TensorFlow多线程调度的非确定性
TensorFlow默认会用多线程加速运算(比如卷积、矩阵乘法的内部并行),线程调度的顺序差异可能导致结果出现微小波动,哪怕种子设置正确。解决方法是创建会话时强制单线程执行:
config = tf.ConfigProto( intra_op_parallelism_threads=1, inter_op_parallelism_threads=1 ) with tf.Session(config=config) as sess: # 后续训练、验证代码...
这样就能消除线程调度带来的不确定性。
3. Batch Normalization的参数初始化遗漏
你使用的tf.contrib.layers.batch_norm中,beta和gamma参数的初始化没有指定种子,虽然你给Conv/FC层的权重加了种子,但BN的参数如果用默认初始化,可能引入随机变量。可以显式为BN参数指定带种子的初始化器:
bn1act = tf.contrib.layers.batch_norm( inputs=conv1act, is_training=is_training, param_initializers={ 'beta': tf.contrib.layers.variance_scaling_initializer(mode="FAN_AVG", seed=0), 'gamma': tf.contrib.layers.variance_scaling_initializer(mode="FAN_AVG", seed=0) } )
(moving_mean和moving_variance默认是0和1,本身是确定的,不需要额外设置种子)
4. 数据处理的一致性检查
虽然你注释掉了np.random.shuffle(train_indicies)确保batch顺序固定,但还要确认:
- 训练集和验证集的划分是固定的,没有每次运行都重新随机划分
- 数据加载过程中没有隐藏的随机操作(比如数据增强、随机翻转等),如果有必须为这些操作设置种子
修正后的核心代码片段
整合种子设置、会话配置和BN参数初始化后的关键部分:
def train_network(lr, ksz, kst, plsz, plst, bs): ksz = np.int(ksz) kst = np.int(kst) plsz = np.int(plsz) plst = np.int(plst) tf.reset_default_graph() with tf.Graph().as_default(): # 统一在当前图上下文内设置所有种子 random.seed(0) np.random.seed(0) tf.set_random_seed(0) # 配置单线程会话,消除并行调度差异 config = tf.ConfigProto( intra_op_parallelism_threads=1, inter_op_parallelism_threads=1 ) with tf.Session(config=config) as sess: X = tf.placeholder(tf.float32, [None, 32, 32, 3]) y = tf.placeholder(tf.int64, [None]) is_training = tf.placeholder(tf.bool) # 带种子的Conv层(保留你的现有代码) conv1act = tf.contrib.layers.conv2d(inputs=X, num_outputs=32, padding='same', kernel_size=ksz, stride=kst, activation_fn=tf.nn.relu, weights_initializer=tf.contrib.layers.variance_scaling_initializer( mode="FAN_AVG", seed=0), biases_initializer=tf.contrib.layers.variance_scaling_initializer( mode="FAN_AVG", seed=0)) # 带种子初始化的Batch Norm bn1act = tf.contrib.layers.batch_norm( inputs=conv1act, is_training=is_training, param_initializers={ 'beta': tf.contrib.layers.variance_scaling_initializer(mode="FAN_AVG", seed=0), 'gamma': tf.contrib.layers.variance_scaling_initializer(mode="FAN_AVG", seed=0) } ) # 剩余的Conv、BN、Pool、FC层同理修改Batch Norm的初始化... # 优化器和训练步骤(保留你的现有代码) mean_loss = tf.losses.softmax_cross_entropy(logits=y_out, onehot_labels=tf.one_hot(y, 10)) optimizer = tf.train.RMSPropOptimizer(learning_rate=lr) extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(extra_update_ops): train_step = optimizer.minimize(mean_loss) with tf.device("/cpu:0"): sess.run(tf.global_variables_initializer()) print('Training') run_model(X, y, mean_loss, is_training, sess, y_out, mean_loss, X_train, y_train, 10, np.int(bs), 100, train_step, True) print('Validation') loss, val = run_model(X, y, mean_loss, is_training, sess, y_out, mean_loss, X_val, y_val, 1, np.int(bs)) return 1 - val
按照这些修改后,应该就能稳定复现实验结果了。如果还是有问题,可以检查TensorFlow版本是否一致,不同版本的contrib模块可能存在细微的行为差异。
内容的提问来源于stack exchange,提问作者lucky kumar

