Dynamic Network Surgery运行及应用

目的:了解该论文《Dynamic Network Surgery》开源代码,1)运行;2)应用;3)了解内部构造。

各种链接

论文:Guo Y,Yao A, Chen Y. Dynamic Network Surgery for Efficient DNNs[J]. 2016.

原版开源代码:https://github.com/yiwenguo/Dynamic-Network-Surgery

第三方链接:

代码链接:https://github.com/HolmesShuan/Dynamic-Network-Surgery-Caffe-Reimplementation

编译(针对官网版本

修改编译文件makefile

原本的开源代码,是去除了python/matlab和experiment以及script/lint的简化版本。编译时,需要讲用到的这部分给去掉

比如修改后的makefile如此所示

此外呢,除了makefile外,还需要编译时的配置文件config。这个没太多需要注意的,毕竟python什么的设置了,makefile也不会用到,但是需要将cudnn给注释掉,这在另外一个github资源池中有提到:Caffe reimplementation of dynamic network surgery(GPU-only/cuDNN unsupported yet).所以,如有必要,还可以参考修改好的makefiel.config文件

顺便科普一下cudn和cudnn。两个都能实现快速的矩阵运算。cudnn是增强库,优化了一些计算,相比于cudn有进一步的算力的提高。比如某块卡cudn大概是cpu的17倍,cudnn会进一个的提升至19倍,二者的关系粗略的就是这样。所以不支持cudnn就先不要在乎了。至于将这个代码升级caffe版本,我试过之后出现了一堆问题,无从下手,还是这样一点点配置较为妥当。毕竟以后不大可能不自己写,如果用到的话。

数据

    先使用alexnet,然后这是使用了Imagenet数据进行训练,所以,顺便对imagenet了解一下。

    Imagenet2012的链接,可以在网上找到百度云的部分链接,更多的链接暂时没去找。

    生成lmdb,imagenet是典型的数据库,在caffe中有相应的示例,在cafferoot/examples/imagenet/下就有相关的文件,可以拿来直接用。这次注意到示例文件中有这个文件:make_imagenet_mean.sh。之前没太注意,caffe这个还是考虑挺全面的。这是数据预处理的时候将数据特征归一化。

    然后是准备图片索引,一般都是txt文件,其指明了最后的路径还有相对应的类别。这儿顺便贴两个对ILSVRC2012 _img_train_t3.tar文件解压缩的代码。根据此索引文件调用caffe_root/TOOLS/convert_imageset程序将对应图片进行预处理。

#该文件下的压缩包下面是一堆tar文件
import os
path='../ImageNet_ILSVRC2012/ILSVRC2012_img_train_t3'
os.chdir(path)
for p in os.listdir(path):
    name=p[:-4]
    os.system('mkdir '+name)
    os.system('tar -xvf '+p+' -C '+name)
#img_train_t3是train_label.txt的子集
#所以,这里就拿来当验证集了
import os
path = '../ImageNet_ILSVRC2012/'
label = path + 'train_label.txt'
jpg_path = path + 'ILSVRC2012_img_train_t3/'
traintxt = '../work/train_t3.txt'
trainfile = open(traintxt, 'w')
with open(label)as f:
    while 1:
        line = f.readline()
        if not line:
            break
        name, lable = line.strip().split(' ')
        jpgs = jpg_path + name
        if not os.path.exists(jpgs):
            continue
        trainfile.write(line)
trainfile.close()

训练 

再然后就是神经网络文件的网络结构,主要是conv还有FC层,conv层的示例可以第三方文件,FC可以参考官方文件。注意:论文中说两个模块(卷积和全连接层)一块进行动态剪枝的时候,可能导致不收敛,所以作者建议分开来做。所以先做卷积层的剪枝,然后再对FC层进行剪枝。

layer {
  name: "conv1"
  type: "CConvolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  convolution_param {
    num_output: 96
    kernel_size: 11
    stride: 4
    pad: 2
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
  cconvolution_param {
    #gamma和power是用来控制更新掩膜T的频率的
    #Probability = (1+gamma*iter)^-power 
    #即原文中所说的一个减函数
    gamma: 0.0000125
    power: 1
    c_rate: -0.7
    iter_stop: 450000
    weight_mask_filler {
      type: "constant"
      value: 1
    }
    bias_mask_filler {
      type: "constant"
      value: 1
    }
  }
}

以及fc层

 cinner_product_param {
    gamma: 0.0001
    power: 1
    c_rate: 4
    iter_stop: 14000  
    weight_mask_filler {
      type: "constant"
      value: 1
    }
    bias_mask_filler {
      type: "constant"
      value: 1
    }        
  } 

修改:

原版的dynamic代码是简化的caffe,而且又是早期的caffe,一些层没有实现。所以在这里想做些修改,以重新让带这些层的重新运行起来。

1. 示例网络及参数下载:网络使用的AlexNet-BN-Caffemodel-on-ImageNet,然后里面也有训练好的caffemodel。

2. 然后里面又BatchNormal和Scale层,是原版的dynamic network surgery中没实现的。

一些尝试:

a. BN和scale函数不存在,我打算直接将二进制文件迁移过去,以为caffe是使用的gflags解析库函数,too naive。这么做的结果就是出现了以下的错误。

[libprotobuf ERROR google/protobuf/text_format.cc:274] Error parsing text-format caffe.NetParameter: 86:15: Message type "caffe.LayerParameter" has no field named "scale_param".
F0619 11:40:08.498093 43568 upgrade_proto.cpp:928] Check failed: ReadProtoFromTextFile(param_file, param) Failed to parse NetParameter file: models/alexnet/train_val.prototxt
*** Check failure stack trace: ***
    @     0x7f3c45a995cd  google::LogMessage::Fail()
    @     0x7f3c45a9b433  google::LogMessage::SendToLog()
    @     0x7f3c45a9915b  google::LogMessage::Flush()
    @     0x7f3c45a9be1e  google::LogMessageFatal::~LogMessageFatal()
    @     0x7f3c461a57b1  caffe::ReadNetParamsFromTextFileOrDie()
    @     0x7f3c4615c467  caffe::Net<>::Net()
    @           0x409947  test()
    @           0x408328  main
    @     0x7f3c44f2b830  __libc_start_main
    @           0x4088a9  _start
    @              (nil)  (unknown)
Aborted

b. 然后去解决上面出现的这个问题,是caffe的Netparamer不识别层里面的超参数设定。

方法:看来caffe在新加载一些函数的时候,还是需要将文件以及函数名等这些有一个“注册”的过程。然后据此想法去定位问题。这儿有篇博客也遇到了这种情况,但是不太符合目前的情况,目前就是遇到了版本不符合,该怎么修改的问题。

首先,先将bn层和Scale层移到相应的位置,包括.h和.cpp文件。编译的时候可能还需要bias层,也一并移动过去。再次编译报出一个有价值的错误,定位到这个问题,发现指向了caffe.proto。这个文件在编译时会生成proto.pb.h和proto.pb.cc两个文件。对于该文件的说明,可以参考两篇博客,《caffe的proto》以及《caffe.proto》。其实仔细看下这个文件的内容就一目了然了,这是对一些层/数据类提前给出预定义,然后里面会给一些变量等单独的ID号,具体的写法类似于c语言的结构体。在结构体的最上方有时会给出最新的编号——比如:Update the next available ID when you add a new SolverParameter field.  SolverParameter next available ID: 42 (last added: layer_wise_reduce)——告诉你想添加内容时,要记得加入合适的ID号,最新的ID号是XX。

到此为止,接下来要做的就一目了然了,我们使用了Ubuntu中的diff命令,比较了此Dynamic源码中用到的caffe.proto和最新的之间的差异,将涉及到BN层和Scale层的加上去。

diff caffe_master/src/caffe/proto/caffe.proto Dynamic/src/caffe/proto/caffe.proto

在此用到了eclipse,因为这可以很方便的寻找需要出问题的数据/类等的定义。注意,使用eclipse-- file/new/MakeFile project with existing code

c. 再然后,再次编译时我这儿又出现了新问题

src/caffe/layers/base_conv_layer.cpp: In member function ‘virtual void caffe::BaseConvolutionLayer<Dtype>::LayerSetUp(const std::vector<caffe::Blob<Dtype>*>&, const std::vector<caffe::Blob<Dtype>*>&)’:
src/caffe/layers/base_conv_layer.cpp:18:21: error: ‘class caffe::ConvolutionParameter’ has no member named ‘has_kernel_size’
   CHECK(!conv_param.has_kernel_size() !=
这个问题还真不太好定位,但是目前的话,只增加了三个库函数(各包含.h .cpp .cu文件),以及将一些超参以及函数类都放到了caffe.proto中。仅此而已,所以这个错误大概率是proto修改时出的问题。再次对比proto文件,发现原Dynamic中的卷积层对应proto文件中对应的参数是optional,而最新的则是repaeated。这两个关键字再proto介绍里有提及,所以再做了修改后,错误消失,可以顺利编译!!!

d. 但是,到此还没有结束,接下来又出现的问题简直崩溃,不知道怎么解决,先看报错信息。

*** Aborted at 1529475730 (unix time) try "date -d @1529475730" if you are using GNU date ***
PC: @     0x7f17c93995c6 caffe::CConvolutionLayer<>::compute_output_shape()
*** SIGFPE (@0x7f17c93995c6) received by PID 35041 (TID 0x7f17c9c78a80) from PID 18446744072790578630; stack trace: ***
    @     0x7f17c817e4b0 (unknown)
    @     0x7f17c93995c6 caffe::CConvolutionLayer<>::compute_output_shape()
    @     0x7f17c930bcb6 caffe::BaseConvolutionLayer<>::Reshape()
    @     0x7f17c93c4053 caffe::Net<>::Init()
    @     0x7f17c93c65b9 caffe::Net<>::Net()
    @           0x40bf3a time()
    @           0x408328 main
    @     0x7f17c8169830 __libc_start_main
    @           0x4088a9 _start
    @                0x0 (unknown)
Floating point exception

关于 try "date -d "这个问题,在网上能找到相关的帖子,这个帖子对同样的问题和解决方法做了搜集,但是对我这个问题的解决没有帮助。我定位到出问题的地方,ConvolutionLayer<>::compute_output_shape(),是简单的求算卷积之后feature map大小的公式,可能的原因是除数没有预先定义。因为我做过修改的就是proto文件,所以去对比新旧proto文件,发现optional uint32 stride = 6 [default = 1];类似的定义,在修改后的proto文件中没有保留default信息。估计就是该变量没有默认值的原因。不想再重新编译,于是在网络配置文件prototxt的paramer中添加了stride信息,结果果然没再报错!!!

其实仔细思考Floating point exception报错信息的话,也许能更快的定位到错误的位置,现在这么想多少有点事后诸葛的感觉。反正问题解决了   :-)

猜你喜欢

转载自blog.csdn.net/daniaokuye/article/details/80668504