经典网络复现系列(一):FCN

1、FCN网络架构
FCN的前半段与VGG19架构相同,直接使用了VGG19预训练好的权重。
前半段的具体架构如下:
layers = (
        'conv1_1', 'relu1_1', 'conv1_2', 'relu1_2', 'pool1',


        'conv2_1', 'relu2_1', 'conv2_2', 'relu2_2', 'pool2',


        'conv3_1', 'relu3_1', 'conv3_2', 'relu3_2', 'conv3_3',
        'relu3_3', 'conv3_4', 'relu3_4', 'pool3',


        'conv4_1', 'relu4_1', 'conv4_2', 'relu4_2', 'conv4_3',
        'relu4_3', 'conv4_4', 'relu4_4', 'pool4',


        'conv5_1', 'relu5_1', 'conv5_2', 'relu5_2', 'conv5_3',
        'relu5_3', 'conv5_4', 'relu5_4'
    )


输入层(理论上对图像大小无限制,复现时resize统一到了224*224,且经过了批归一化的预处理) -------------共16个卷积层(卷积+偏置),对应16个relu层,5个pool层。 其中卷积层卷积步长为1,padding=‘SAME’。所以经过卷积层后图像的大小不变。pool层采用平均池化,核大小为2*2,步长[1,2,2,1],padding='SAME',因此每经过一个pool层,图像的长和款就变为原本的二分之一。经过5个池化层后,map变为原来的1/32。






架构中段:
conv6 (卷积核[7,7,512,4096])+relu----drop_out层----conv7 (卷积核[1,1,4096,4096])+relu----dropout
卷积层卷积步长为1,padding=‘SAME’ 权重使用截断正态分布初始化。就是说产生正太分布的值如果与均值的差值大于两倍的标准差,那就重新生成。




架构后半段:
根据搭建方式的不同,FCN分为3种:FCN-32s,FCN-16s,FCN-8s
经过前、中段以后,conv8输出的map变为原来的1/32,后续不特指的话,缩小都是指长和宽。要实现端到端的像素分类,需要对其做反卷积,将map变为原本的大小。关于反卷积的原理,下文有讲述。所使用的函数是tf.nn.conv2d_transpose() 长宽扩大多少倍由参数stride决定。与stride=2 可以理解为将map反卷积成为原本的两倍。需要注意的是,与conv时的kernel [size,size,in,out]不同,tf.nn.conv2d_transpose()使用的kernel是[size,size,out,in]
FCN-8s: ((conv7*2+pool4)*2+pool3)*8    2*2*8=2^5 恢复到了原本的大小
FCN-16s:(conv7*2+pool4)*16
FCN-32s: conv7*32


我复现了FCN-8s的源码,这种skip layer可以将浅层的和深层的特征组合在一起。
三个反卷积核的大小依次是:[4,4,pool4.shape[-1].value,4096],[4,4,pool3.shape[-1].value,pool4.shape[-1].value],[16,16,NUM_OF_CLASSESS,pool3.shape[3].value] 
stride依次是2,2,8。


最后一个反卷积层的输出是[batchsize,224,224,NUM_CLASSES]  通过softmax(在损失函数优化器中自带)计算出像素点属于各类别的置信度,选择置信度最高的作为预测值,得到pre 
由pre和gt计算损失函数。






2、复现过程中的小trick
1)命令行参数
FCN.py




FLAGS=tf.flags.FLAGS
tf.flags.DEFINE_integer('batchsize','8','trainning batchsize')    #参数 默认值  说明
tf.flags.DEFINE_float('learning_rate','1e-4','learning_rate')
tf.flags.DEFINE_bool('train', "True", "Debug mode: True/ False")






python FCN.py会直接使用默认的参数
python FCN.py --batchsize 32  --learning_rate 0.05  --train False   参数可以根据需求进行修改
后续代码调用参数的话:
FLAGS.batchsize
FLAGS.learning_rate
FLAGS.train






2)np.squeeze()
去除array中为1的维度


3)tf.variable_scope和tf.name_scope的区别:
tf.variable_scope可以让变量有相同的命名,包括tf.get_variable得到的变量,还有tf.Variable的变量
tf.name_scope可以让变量有相同的命名,只是限于tf.Variable的变量


所以如果
with tf.namescope('V1'):
with tf.namescope('V2'):
中都使用tf.get_variable得到了name为 'kernel' 的variable 会报错
但如果都使用tf.Variable得到了name为 'kernel' 的variable 不会报错




4)tf.variable与tf.get_variable
tf.Variable() 每次都在创建新对象,所有reuse=True 和它并没有什么关系。对于get_variable(),来说,如果已经创建的变量对象,就把那个对象返回,如果没有创建变量对象的话,就创建一个新的。
具体参数说明参考:https://blog.csdn.net/u012436149/article/details/53696970




5)tf.nn.conv2d_transpose
反卷积
tf.nn.conv2d_transpose(input_tensor,kernel,out_shape,[1,strider,strider,1],padding='SAME')
长宽扩大多少倍由参数stride决定。与stride=2 可以理解为将map反卷积成为原本的两倍。需要注意的是,与conv时的kernel [size,size,in,out]不同,tf.nn.conv2d_transpose()使用的kernel是[size,size,out,in]
需要指定output_shape
反卷积原理可见:https://blog.csdn.net/mao_xiao_feng/article/details/71713358


6)tfrecord数据集的制作和读取
参考另一篇博客 tensorflow高效读取数据(tfrecord)


7) label_batch=tf.expand_dims(label_batch,-1)
增加维度,增加的是哪一维,可以通过dims= 制定




8)损失函数的选择:
参考另一篇博客:关于tensorflow交叉熵损失函数的一些理解 


9)优化器的选择:
参考博客:https://blog.csdn.net/aliceyangxi1987/article/details/73210204


10)权重的保存和恢复
with tf.Session() as sess:
    saver=tf.train.Saver(max_to_keep=5)#  只保存最近的5个
    if FLAGS.train==True:
        sess.run(init)    #如果是训练的话  初始化  不载入
    else:
        ckpt = tf.train.get_checkpoint_state('log')  
        saver.restore(sess,ckpt.model_checkpoint_path)  #恢复最新的权重到sess中


   saver.save(sess,'log/model.ckpt',step)  #保存第step步的权重




11)训练一段时间后报错:
OutOfRangeError (see above for traceback): RandomShuffleQueue '_2_shuffle_batch/random_shuffle_queue' is closed and has insufficient elements (requested 8, current size 0)
[[Node: shuffle_batch = QueueDequeueManyV2[component_types=[DT_UINT8, DT_UINT8], timeout_ms=-1, _device="/job:localhost/replica:0/task:0/device:CPU:0"](shuffle_batch/random_shuffle_queue, shuffle_batch/n)]]




可能原因:tfrecord存储和读取时候的数据类型不一致。
1、数据编码前是float  解码时指定为uint8。
2、编码方式与解码方式不配对。
3、数据集有图片是损坏的。


检查后发现是第三个原因。




12)ubuntu下移动数据集,当数据集比较大,尽量使用终端命令,会比较快。
mv ./training/ADE_train_000202*.png ./train_back/


13)tensorflow的可视化
tf.summary.histogram  直方图
tf.summary.scalar     变量


最后
merged =tf.summary.merge_all() 将所有需要可视化的合并


定义writer
train_writer = tf.summary.FileWriter(FLAGS.log + '/train')
训练时每隔几步并写入一次
summary=sess.run(merged)
train_writer.add_summary(summary,step)




训练结束后查看:
在终端输入 tensorboard --logdir=dir
其中dir是summary日志存储的目录。


13)采用
if __name__=='__main__':
	tf.app.run()




时 也就是python文件有预设参数时,调用main函数的定义:
def main(argv=None):






14)对于大数据集 制作numpy非常麻烦,因此数据格式为tfrecord会比较好,但是数据集比较小的话,制作成numpy会比较快。


15)
print('\r%d',step,end='')   #'\r'使得下一次的输出把上一次的覆盖掉。end=''确保不换行。
sys.stdout.flush()    #刷新

这两行代码是一个 在原位置不断刷新输出的简单例子


16)logging日志
使用方法参考
https://blog.csdn.net/hallo_ween/article/details/64906838


17)滑动平均的使用
以损失函数为例,滑动平均可以防止损失随着epoch的变化不太剧烈,而是相对平稳的下降,如果loss没有急剧的上涨,可以不考虑使用滑动平均,如果不稳定的话,可以使用。
使用方法:
v1 = tf.Variable(0, dtype=tf.float32)  
step = tf.Variable(tf.constant(0))  
  
ema = tf.train.ExponentialMovingAverage(0.99, step)  
maintain_average = ema.apply([v1])  
  
with tf.Session() as sess:  
    init = tf.initialize_all_variables()  
    sess.run(init)  
  
    print sess.run([v1, ema.average(v1)]) #初始的值都为0  
  
    sess.run(tf.assign(v1, 5)) #把v1变为5  
    sess.run(maintain_average)  
    print sess.run([v1, ema.average(v1)]) # decay=min(0.99, 1/10)=0.1, v1=0.1*0+0.9*5=4.5  
  
    sess.run(tf.assign(step, 10000)) # steps=10000  
    sess.run(tf.assign(v1, 10)) # v1=10  
    sess.run(maintain_average)  
    print sess.run([v1, ema.average(v1)]) # decay=min(0.99,(1+10000)/(10+10000))=0.99, v1=0.99*4.5+0.01*10=4.555  




18)assert 断言的用法
格式 : assert+空格+要判断语句+双引号“报错语句”






例子:
出错时候
assert 1>5, "chucuo"
输出值为:
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-5-6aa3d3e733df> in <module>()
----> 1 assert 2>5, "chucuo"


AssertionError: chucuo






19)tf.identity()和tf.group()均可将语句变为操作

3、相关代码

1)代码架构:

准备数据(读入事先制作好的tfrecord)-------定义网络结构-------可视化------计算损失---定义优化器----初始化-----开启Session----训练(每训练20个batch进行一次验证)------测试

2)代码地址:







猜你喜欢

转载自blog.csdn.net/zlrai5895/article/details/80473814