TensorFlow加载ResNet模型权重后测试结果异常波动问题排查
我在TensorFlow中实现并训练了一个ResNet模型,通过以下方式保存模型:
save_path=saver.save(sess,"/home/.../model.ckpt",global_step=50) saver.save(sess,"/home/s.../model.ckpt",global_step=50)
在另一个脚本中,我尝试加载该模型并在一些图片上测试,脚本结构如下:
files = [s for s in os.listdir(dossier_source+'/'+d) if s.endswith(".png") or(".jpeg")] print files.__len__() batch_size_arg=files.__len__() x = tf.placeholder(tf.float32, [None,IMAGE_WIDTH,IMAGE_HEIGHT,NUMBER_OF_CHANNELS], name='x-input') y = tf.placeholder(tf.int32, [None], name='y-input') train_file =str(dossier_source+d+"/*.png") # 我重建了所有变量[...] with tf.Session() as sess: sess.run(tf.local_variables_initializer()) saver=tf.train.Saver() saver.restore(sess,tf.train.latest_checkpoint(global_path)) tab= sess.run('conv0/conv:0') print tab[0][0][0] # 此测试证明conv0权重与训练时一致 for i in xrange(1): im_batch, lab_batch = sess.run([images_batch, labels_batch]) curr=sess.run(accuracy,feed_dict={x: im_batch, y: lab_batch}) top=sess.run(top_k_op ,feed_dict={x: im_batch, y: lab_batch}) conf =sess.run(confusion,feed_dict={x: im_batch, y: lab_batch, }) print "Results" print curr print conf
我的问题是:尽管conv0权重每次都一致,但同一批图片测试时混淆矩阵和结果却每次不同……我在加载权重和模型时是否操作有误?
编辑:最新测试发现,即使执行以下循环测试同一批3张图片,结果也不同:
for i in xrange(3): im_batch, lab_batch = sess.run([images_batch, labels_batch]) curr=sess.run(accuracy,feed_dict={x: im_batch, y: lab_batch}) top=sess.run(top_k_op ,feed_dict={x: im_batch, y: lab_batch}) conf =sess.run(confusion,feed_dict={x: im_batch, y: lab_batch, }) print "Results" print curr print conf
兄弟,权重没问题但结果随机,基本可以排除模型加载的问题——毕竟你已经验证了conv0的权重是对的。大概率是你的模型或者数据管道里还有没关闭的随机操作,我给你列几个最常见的原因和排查方向:
1. Dropout层没切换到测试模式
如果你的ResNet里用了Dropout,训练时会随机关掉一部分神经元来防止过拟合,但测试时必须把keep_prob设为1.0。要是你测试时还是用了训练时的keep_prob(比如0.5),那每次前向传播都会随机失活不同的神经元,结果自然不一样。
检查一下测试时的feed_dict,有没有传入keep_prob: 1.0,或者模型里的Dropout层参数是不是固定死了训练模式?
2. Batch Normalization(BN层)没切换到测试模式
BN层在训练和测试时的逻辑完全不一样:训练时用当前batch的均值和方差,测试时要用训练阶段累积的滑动均值和方差。要是你测试时没把BN层的is_training设为False,那每次测试都会重新计算当前batch的均值方差,结果肯定会波动。
比如你用tf.layers.batch_normalization的话,构建模型时要传training这个占位符,测试时在feed_dict里传入training: False;要是用的是旧版的tf.contrib.layers.batch_norm,要确保测试时把is_training设为False。
3. 数据读取/预处理有随机操作
这个可能性也很大,你看你每次run(images_batch)得到的是不是真的同一批图片?
- 如果用了
tf.train.shuffle_batch这类带shuffle的读取操作,即使是同一数据集,每次run都会打乱顺序,要是你的batch size和数据集大小不一致,每次拿到的图片都不一样;就算batch size一致,顺序变了的话,混淆矩阵的行列对应也会乱(不过你说accuracy也变,那可能不是顺序问题)。 - 另外,训练时加的数据增强操作(随机裁剪、翻转、亮度调整这些),测试时必须关掉!要是测试时还在随机增强,每张图片每次读进来都不一样,结果肯定波动。
先把数据预处理里的所有随机操作都注释掉,把数据读取的shuffle改成False,再跑一遍测试,看看结果是不是一致了。
4. 局部变量初始化的小坑
你测试时只跑了sess.run(tf.local_variables_initializer()),虽然你restore了模型,但有些局部变量(比如BN层的滑动均值方差)如果没被包含在saver的变量列表里,初始化后会变成随机值?不过你已经验证了conv0权重是对的,这个可能性相对小,但可以试试把初始化步骤放在restore之后?或者检查一下saver是不是包含了所有需要的变量(比如用tf.global_variables()来创建saver,确保覆盖所有可训练变量)。
快速验证方法:固定随机种子
在脚本开头加上:
import numpy as np np.random.seed(123) tf.set_random_seed(123)
如果结果变得完全一致,那百分百是某个随机操作没控制住,再回头按上面的方向排查就行。
内容的提问来源于stack exchange,提问作者Ho Lee

