【菜菜的CV进阶之路-Pytorch基础-batch normalization】同一个模型预测结果随batchsize的不同而不同

训练一个网络,在测试结果的时候遇到的问题:我同一个模型,当batchsize设置成1的时候准确率70%,但当batchsize设置成8的时候就是90%,真是百思不得其解...然后问师兄,师兄说可能是一个取了平均,一个没有取平均(当时没太懂TT...),Google后发现了答案QAQ:

请教:对于同一个模型,预测结果随输入样本数量而改变

目前在做一个softmax的分类,非常基础,是分类电视台标的。输入维度是[None, 96*35]遇到个问题,对于模型的训练,精度可以达到99.9%。把这个模型保存下来,然后调用这个模型进行预测,发现当输入的预测图像矩阵是一副图片,即[1,96*35],不论输入啥图片,哪怕是zeros,都会得到同一个判断分类,但是,如果测试输入矩阵变多,比如一次10幅图[10,96*35],输出就正常了。想问下我哪里做错了导致模型不变的情况下判断结果跟输入的图片个数有关?
下面是我的测试代码

def recognize(img_input, pb_file_path):
    with tf.Graph().as_default():
        output_graph_def = tf.GraphDef()

    with open(pb_file_path, "rb") as f:
        output_graph_def.ParseFromString(f.read())
        _ = tf.import_graph_def(output_graph_def, name="")

    with tf.Session() as sess:
        init = tf.global_variables_initializer()
        sess.run(init)

        input_x = sess.graph.get_tensor_by_name("Input:0")
        # print input_x
       out_softmax = sess.graph.get_tensor_by_name("Y_predict:0")
            
       img_try = img_input

       test_input = np.float32(img_try/255)
       img_out_softmax = sess.run(out_softmax, feed_dict={input_x:test_input})

       prediction_labels = np.argmax(img_out_softmax, axis=1)

       return prediction_labels


 #----------------------Use Sub Function to Predict --------------------------------------------
start = 10
for i in range(1,10):  #将测试图像从一次带入1张遍历到一次带入10张,看测试结果
    r = recognize(img_test[start:start+i], "./pretrained/graph.pb")
    print(listmapping(r,TV_LOGO_s))

测试结果,可以看到图像为1张的时候结果误判成cctv5+,但是随着一次带入图片变多(图片没有变化,还是这些图,可以理解为总共10张图,第一次带入第一张,第二次带入第一第二张,以此类推),结果就正确了
 
这个问题已经困扰我一周了,请指教!!!
最后附上模型截图

解答: 

首先再次感谢楼主的分享,借楼主的模型学习了一下batch normalization,收益良多。对于楼主的代码我还没有能力修改,对于出现不稳定性的原因,我比较确信是batch normalization的原因,但是由于没有能力直接对楼主的代码进行修改测试,不好说百分百肯定,希望下面我的一些心得能对楼主有帮助。在分享拙见之前,想请教楼主两个问题:
1. 我只对batch normalization代码方面有些许收获,关于它对模型拟合学习上的作用(比如说数学上的特性)完全不了解,楼主能否谈谈batch normalization对于模型哪方面有所提升?有什么原理?有什么适用问题?

2. 楼主模型中每一层都进行了batch normalization,在其他例子中也见过这样的,请问这是常见或者必要的作法吗?它跟其他层应该如何搭配比较好?

先重复一下我点评里的分析,楼主模型中batch normalization部分,均值和方差都是来源于输入特征数据的,而且与batch的构成有关。此外,均值和方差都是张量而不是变量,所以不存在训练和固定的问题,也就是说在楼主导入固定模型后,这两个量仍然会随输入特征数据变化,定性的说,这就有可能解释楼主说的“预测结果随输入样本数量而改变”。至于我说的关于即使样本数量不变而只改变样本构成的做法,根据上述解释,可以推测也同样会出现不稳定性。这个假说可以通过将楼主代码最后的循环改为以下代码来证实:

start = 30
for i in range(1,30):
    r = recognize(mnist.test.images[i:i+30], "graph_minst.pb")
    print(np.pad(r, (i,0), 'constant'))

结果我就不贴了,随着这30个样本抽样窗口的移动,我的运行可以看到不稳定性出现。这个结果算是从侧面说明了楼主的问题很有可能与batch normalization有关。

下面说有什么办法,我只有一个比较简单又比较无脑的例子,应该不适用于楼主,但算是给出一个稳定的例子,加上后面的一些想法,希望楼主能找到适合自己的解决办法。下面这个例子用的是tf.layers.batch_normalization而不是tf.nn.batch_normalization,也就是捡现成的,tf.layers.batch_normalization有一个"training"参数可以用来区分训练和预测(到这个地步我不打算深入关于区分的实现了,比较懒,不好意思)。

构建训练模型(这里特别缩短了训练次数以便降低精确度,这样后面的不稳定性更容易看到):

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("test", one_hot=True)

tf.reset_default_graph()
x = tf.placeholder('float32', (None, 784), name='x')
y = tf.placeholder('float32', (None, 10), name='y')
phase = tf.placeholder(tf.bool, name='phase')

dense = tf.layers.dense(x, 100)
batchnorm = tf.layers.batch_normalization(dense, training=phase)
activation = tf.nn.relu(batchnorm, 'relu')
logits = tf.layers.dense(activation, 10)
predicts = tf.argmax(logits, 1, name='predicts')
accuracy = tf.reduce_mean(tf.cast(
    tf.equal(tf.argmax(y, 1), predicts), tf.float32))
loss = tf.losses.softmax_cross_entropy(onehot_labels=y, logits=logits)

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
    train_op = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for _ in range(200):
        x_train, y_train = mnist.train.next_batch(100)
        sess.run(train_op, feed_dict={'x:0': x_train, 
                            'y:0': y_train, 
                            'phase:0': 1})
    print(sess.run(accuracy, feed_dict={'x:0': mnist.test.images,
                                        'y:0': mnist.test.labels,
                                        'phase:0': 0}))
    saver.save(sess, 'testpb/testpb.ckpt')

保存模型:

import tensorflow as tf

restore_saver = tf.train.import_meta_graph('testpb/testpb.ckpt.meta')
with tf.Session() as sess:
    restore_saver.restore(sess, tf.train.latest_checkpoint('testpb'))
    output_graph_def = tf.graph_util.convert_variables_to_constants(
        sess, sess.graph_def , ['predicts'])
tf.train.write_graph(output_graph_def, '.', 'graph_mnist.pb', as_text=False)

导入模型进行预测:

import numpy as np
import tensorflow as tf
import tensorflow.examples.tutorials.mnist.input_data as input_data

mnist = input_data.read_data_sets('test', one_hot=True)

graph_def = tf.GraphDef()
with open('graph_mnist.pb', "rb") as f:
    graph_def.ParseFromString(f.read())
tf.import_graph_def(graph_def, name='')

with tf.Session() as sess:
    for i in range(30):
        print(sess.run('predicts:0', feed_dict={'x:0': mnist.test.images[:i],
                                                'phase:0': 1}))

当你将最后一行的'phase:0': 1改为'phase:0': 0后,应该可以观察到区别。

最后说一下对于实现训练预测区分的一个小想法,楼主代码中用到了"tf.train.ExponentialMovingAverage",我大胆猜测是想要训练完成后用这个替代均值和方差,从而实现模型的稳定。看过一些例子,确实有训练完替换的做法,想来这应该可以解决楼主模型稳定性的问题。

你可能感兴趣的:(Pytorch)