训练网络的一些trick(1):pre_train与逐层拷贝网络

来源:互联网 发布:mac rar解压软件下载 编辑:程序博客网 时间:2024/06/16 12:21


最近做detection方面的工作,在实际训练方面做了一些尝试,这一篇主要记录对于网络pre_train的理解。


首先说pre_train,目前几乎所有做detection的网络都会用到这个技巧,相比于ImageNet数据集里张百万甚至千万的图片量,detection数据集几万张的图片量实在有些少(Pascal VOC、COCO),单纯用这些图片去训练一个比较深的随机初始化的cnn然后得到一个比较好的结果几乎是不可能的事情,基本一开始就是loss=NaN。这时候就用到pre_train的技巧,先构建一个分类网络(比如VGG16)并且用ImageNet来训练,这样得到一个能提取较好特征的卷积网络,然后把后面几层用来分类的fc层去掉,换成做detection的rpn、roi或者其他的层,相当于detection训练的一开始就能得到一个比较好的卷积特征(虽然是用来分类的),这样就解决了网络难训练的问题(或者数据量不够的问题?)。


在做的项目里,比如我要检测非自然图像里的一个特定形状(比如在七龙珠动画片里检测龙珠。。或者其他什么奇怪的东西),那么有两个问题,1)我做检测的domain和正常检测的domain是不一样的,是在非自然图像中的(而不是一张照片),这样分类任务训练好的特征提取器(pre_train model)可能不会很好的适用,或许应该在相同domain下训练一个提取器会比较好,这样就引出另一个问题,2)数据量不够。没有一个大型的动画片的分类数据库,而且用作detection的训练数据也并不很多(大概3000张)。然后我自己做了一些探索和猜想。


首先domain的问题几乎没办法解决,除非有一个大的数据集;而数据量的问题(或者说优化detection的问题更合适),可以参考pre_train的过程。博主认为pre_train的过程从梯度下降的角度来考虑,相当于随机初始化等于把参数坐标随机设置到一个位置,这样距离高维空间中的最优解坐标相差非常远,而分类任务的最优解和detection最优解是有一些共通之处的,比如前面conv层提取的特征对于两个任务都有效,两个任务最优解在高维空间的位置可能相对比较接近(当然这两的维度都不一样,这是一个不是很恰当的比喻),而pretrain相当于把最开始随机初始化的参数坐标向着最优位置移动了一大步,这样利用有限的detection训练数据就有可能达到比较好的结果(比随机初始化要好)。


那么,对于要做的detection任务,在voc07或其他数据集上训好的model是不是比分类的pretrain model更接近最优解呢?要知道分类的model最后不会保留fc层,那pvanet来说,最后两层fc6、fc7共有2*4096*4096几千万的参数量,很大的数量级,更不用提还有rpn的一些层。基于这个猜想做了一些实验。


SSD的实验:用在Pascal voc上训练好的ssd300去初始化新任务的网络,base网络是VGG16,除了输出类别数目不同的层其它层全部复制(主要多了fc层),分类网络初始化map在17%左右,新的方法map提升到30%左右。数值整体比较差我认为是数据量和标注质量的问题。

pvanet的实验:几乎同样的做法,map从31%提高到35%,base网络是论文作者release出代码中的默认网络。


以上的实验和猜想都并不严格,还存在很多改进空间甚至是错误之处,欢迎指正。


附逐层拷贝网络的方法:

1.利用pycaffe的接口,初始化net之后会有一些给出的接口来操作net的param,这个在net_surgery里面有很好地例子。这里贴一些其他接口的代码。

if (net.layers[i].type == 'Convolution') or (net.layers[i].type == 'Scale'):            net.layers[i].blobs[0].data.flat = net_.layers[i].blobs[0].data.flat            #net.layers[i].blobs[1].data.flat = net_.layers[i].blobs[1].data.flat

主要给出了layer、blobs、data的接口,conv层blob有两个,bn层一般有3个。

2.拆分caffemodel成prototxt。拆分后的结果如下:

layer {  name: "conv1"  type: "Convolution"  bottom: "data"  top: "conv1"  param {    lr_mult: 1.0  }  param {    lr_mult: 2.0  }  blobs {    data: 0.264199763536    data: -0.220321893692    data: -0.251213937998    data: 0.150178313255    ...    data: -0.05886515975    data: 0.0170712769032    data: -0.185302212834    shape {      dim: 70      dim: 3      dim: 3      dim: 3    }  }  blobs {    data: 0.0    data: 0.0    data: 0.0      ...    data: 0.0    data: 0.0    shape {      dim: 70    }  }  phase: TEST  convolution_param {    num_output: 70    kernel_size: 3    stride: 1    weight_filler {      type: "xavier"    }    bias_filler {      type: "constant"    }  }}
这是一个conv层的形式。这种方法解析出来的prototxt文件太大,非常难操作,虽然比较直观,但还是不建议这样做。

附拆分代码:

import caffe.proto.caffe_pb2 as caffe_pb2caffemodel_filename = 'deploy.caffemodel'model = caffe_pb2.NetParameter()f = open(caffemodel_filename, 'rb')model.ParseFromString(f.read())f.close()proto_file = open('deploy.prototxt', 'w')proto_file.write(str(model.__str__))proto_file.close()print 'all done'