神经网络压缩的研究近三年十分热门,笔者查阅到相关的两篇博客,博主们非常奉献的提供了源代码,但是发发现在使用gpu训练添加mask的网络上,稍微有些不顺,特此再进行详细说明。

此文是在 基于Caffe的CNN剪枝[1]和 Deep Compression阅读理解及Caffe源码修改[2] 的基础上修改的。

mask的结构?

[1]中使用的blob,存储mask。blob是一块数据块,在初始化时,需要为gpu上的数据块申请一块空间,故有Addmask()函数。AddMask()是blob.hpp中的blob的成员方法,需要在blob.cpp中实现。使用时将Addmask()添加在innerproduct.cpp和base_conv.cpp中,使得网络在setuplayer的过程中,为fc层和conv层多开辟一块存放mask的syncedmemory。blob有一系列需要实现的cpu_data()/mutable_cpu_data()等,初始化中改变mask的值时需要注意使用合理的方式。

InnerProductLayer.cpp

  1. void InnerProductLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
  2. const vector<Blob<Dtype>*>& top) {
  3. ...
  4. this->blobs_[].reset(new Blob<Dtype>(weight_shape));
  5. this->blobs_[]->Addmask();
  6. ...}

base_conv.cpp:

  1. template <typename Dtype>
  2. void BaseConvolutionLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
  3. const vector<Blob<Dtype>*>& top) {
  4. ...
  5. this->blobs_[].reset(new Blob<Dtype>(weight_shape));
  6. this->blobs_[]->Addmask();
  7. ...}

修改blob.hpp和blob.cpp,添加成员mask_和相关的方法,在[1]文章的评论里作者已给出源代码。

[2]中使用layer结构定义mask,layer是相当于数据的一系列操作,或者说是blob的组合方法。

但是,想要实现在gpu上的操作,数据需要有gpu有关的操作。故此处采用[1]中的方法,将mask_添加到blob class中,实现mask_属性

mask的初始化?

在Caffe框架下,网络的初始化有两种方式,一种是调用filler,按照模型中定义的初始化方式进行初始化,第二种是从已有的caffemodel或者snapshot中读取相应参数矩阵进行初始化[1]。

1、filler的方法

在程序开始时,网络使用net.cpp中的Init()进行初始化,由输入至输出,依次调用各个层的layersetup,建立网络结构。如下所示是caffe中使用xavier方法进行填充的操作。

  1. virtual void Fill(Blob<Dtype>* blob) {
  2. CHECK(blob->count());
  3. int fan_in = blob->count() / blob->num();
  4. int fan_out = blob->count() / blob->channels();
  5. Dtype n = fan_in; // default to fan_in
  6. if (this->filler_param_.variance_norm() ==
  7. FillerParameter_VarianceNorm_AVERAGE) {
  8. n = (fan_in + fan_out) / Dtype();
  9. } else if (this->filler_param_.variance_norm() ==
  10. FillerParameter_VarianceNorm_FAN_OUT) {
  11. n = fan_out;
  12. }
  13. Dtype scale = sqrt(Dtype() / n);
  14. caffe_rng_uniform<Dtype>(blob->count(), -scale, scale,
  15. blob->mutable_cpu_data());
  16. //Filler<Dtype>:: FillMask(blob);
  17. CHECK_EQ(this->filler_param_.sparse(), -)
  18. << "Sparsity not supported by this Filler.";
  19. }

filler的作用是,为建立的网络结构产生随机初始化值。

即使是从snapshot或caffemodel中读入数据,也执行随机填充操作。

2、从snapshot或caffemodel中读入数据

tools/caffe.cpp 中的phase:train可以从snapshot或caffemodel中提取参数,进行finetune。phase:test则可以从提取的参数中建立网络,进行预测过程。

这里笔者的网络结构是在pycaffe中进行稀疏化的,因此读入网络的proto文件是一个连接数不变、存在部分连接权值为零的网络。需要在读入参数的同时初始化mask_。因此修改blob.cpp中的fromproto函数:

  1. template <typename Dtype>
  2. void Blob<Dtype>::FromProto(const BlobProto& proto, bool reshape) {
  3. if (reshape) {
  4. vector<int> shape;
  5. if (proto.has_num() || proto.has_channels() ||
  6. proto.has_height() || proto.has_width()) {
  7. // Using deprecated 4D Blob dimensions --
  8. // shape is (num, channels, height, width).
  9. shape.resize();
  10. shape[] = proto.num();
  11. shape[] = proto.channels();
  12. shape[] = proto.height();
  13. shape[] = proto.width();
  14. } else {
  15. shape.resize(proto.shape().dim_size());
  16. for (int i = ; i < proto.shape().dim_size(); ++i) {
  17. shape[i] = proto.shape().dim(i);
  18. }
  19. }
  20. Reshape(shape);
  21. } else {
  22. CHECK(ShapeEquals(proto)) << "shape mismatch (reshape not set)";
  23. }
  24. // copy data
  25. Dtype* data_vec = mutable_cpu_data();
  26. if (proto.double_data_size() > ) {
  27. CHECK_EQ(count_, proto.double_data_size());
  28. for (int i = ; i < count_; ++i) {
  29. data_vec[i] = proto.double_data(i);
  30. }
  31. } else {
  32. CHECK_EQ(count_, proto.data_size());
  33. for (int i = ; i < count_; ++i) {
  34. data_vec[i] = proto.data(i);
  35. }
  36. }
  37. if (proto.double_diff_size() > ) {
  38. CHECK_EQ(count_, proto.double_diff_size());
  39. Dtype* diff_vec = mutable_cpu_diff();
  40. for (int i = ; i < count_; ++i) {
  41. diff_vec[i] = proto.double_diff(i);
  42. }
  43. } else if (proto.diff_size() > ) {
  44. CHECK_EQ(count_, proto.diff_size());
  45. Dtype* diff_vec = mutable_cpu_diff();
  46. for (int i = ; i < count_; ++i) {
  47. diff_vec[i] = proto.diff(i);
  48. }
  49. }
  50. if(shape_.size()==||shape_.size()==){
  51. Dtype* mask_vec = mutable_cpu_data();
  52. CHECK(count_);
  53. for(int i=;i<count_;i++)
  54. mask_vec[i]=data_vec[i]?:;
  55. }

在读入proto文件的同时,如果层的大小是4D——conv层、或2D——fc层时,初始化mask_为data_vec[i]?1:0。当层的大小是1Ds——pool或relu层时,不进行mask的初始化。

反向传播的修改?

1、修改blob的更新方式,添加math_funcion.hpp头文件。

  1. template <typename Dtype>
  2. void Blob<Dtype>::Update() {
  3. // We will perform update based on where the data is located.
  4. switch (data_->head()) {
  5. case SyncedMemory::HEAD_AT_CPU:
  6. // perform computation on CPU
  7. caffe_axpy<Dtype>(count_, Dtype(-),
  8. static_cast<const Dtype*>(diff_->cpu_data()),
  9. static_cast<Dtype*>(data_->mutable_cpu_data()));
  10. caffe_mul<Dtype>(count_,
  11. static_cast<const Dtype*>(mask_->cpu_data()),
  12. static_cast<const Dtype*>(data_->cpu_data()),
  13. static_cast<Dtype*>(data_->mutable_cpu_data()));
  14. break;
  15. case SyncedMemory::HEAD_AT_GPU:
  16. case SyncedMemory::SYNCED:
  17. #ifndef CPU_ONLY
  18. // perform computation on GPU
  19. caffe_gpu_axpy<Dtype>(count_, Dtype(-),
  20. static_cast<const Dtype*>(diff_->gpu_data()),
  21. static_cast<Dtype*>(data_->mutable_gpu_data()));
  22. caffe_gpu_mul<Dtype>(count_,
  23. static_cast<const Dtype*>(mask_->gpu_data()),
  24. static_cast<const Dtype*>(data_->gpu_data()),
  25. static_cast<Dtype*>(data_->mutable_gpu_data()));
  26. #else
  27. NO_GPU;
  28. #endif
  29. break;
  30. default:
  31. LOG(FATAL) << "Syncedmem not initialized.";
  32. }
  33. }

2、为cpu下的计算和gpu下的计算分别添加形如weight[i]*=mask[i];的运算方式。

inner_product_layer.cpp:

  1. void InnerProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
  2. const vector<bool>& propagate_down,
  3. const vector<Blob<Dtype>*>& bottom) {
  4. if (this->param_propagate_down_[]) {
  5. const Dtype* top_diff = top[]->cpu_diff();
  6. const Dtype* bottom_data = bottom[]->cpu_data();
  7. // Gradient with respect to weight
  8. Dtype* weight_diff = this->blobs_[]->mutable_cpu_diff();
  9. vector<int> weight_shape();
  10. if (transpose_) {
  11. weight_shape[] = K_;
  12. weight_shape[] = N_;
  13. } else {
  14. weight_shape[] = N_;
  15. weight_shape[] = K_;
  16. }
  17. int count = weight_shape[]*weight_shape[];
  18. const Dtype* mask = this->blobs_[]->cpu_mask();
  19. for(int j=;j<count;j++)
  20. weight_diff[j]*=mask[j];
  21.  
  22. if (transpose_) {
  23. caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
  24. K_, N_, M_,
  25. (Dtype)., bottom_data, top_diff,
  26. (Dtype)., weight_diff);
  27. } else {
  28. caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
  29. N_, K_, M_,
  30. (Dtype)., top_diff, bottom_data,
  31. (Dtype)., weight_diff);
  32. }
  33. }
  34. if (bias_term_ && this->param_propagate_down_[]) {
  35. const Dtype* top_diff = top[]->cpu_diff();
  36. // Gradient with respect to bias
  37. caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)., top_diff,
  38. bias_multiplier_.cpu_data(), (Dtype).,
  39. this->blobs_[]->mutable_cpu_diff());
  40. }
  41. if (propagate_down[]) {
  42. const Dtype* top_diff = top[]->cpu_diff();
  43. // Gradient with respect to bottom data
  44. if (transpose_) {
  45. caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans,
  46. M_, K_, N_,
  47. (Dtype)., top_diff, this->blobs_[]->cpu_data(),
  48. (Dtype)., bottom[]->mutable_cpu_diff());
  49. } else {
  50. caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans,
  51. M_, K_, N_,
  52. (Dtype)., top_diff, this->blobs_[]->cpu_data(),
  53. (Dtype)., bottom[]->mutable_cpu_diff());
  54. }
  55. }
  56. }

inner_product_layer.cu:

  1. template <typename Dtype>
  2. void InnerProductLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
  3. const vector<bool>& propagate_down,
  4. const vector<Blob<Dtype>*>& bottom) {
  5. if (this->param_propagate_down_[]) {
  6. const Dtype* top_diff = top[]->gpu_diff();
  7. const Dtype* bottom_data = bottom[]->gpu_data();
  8. vector<int> weight_shape();
  9. if (transpose_) {
  10. weight_shape[] = K_;
  11. weight_shape[] = N_;
  12. } else {
  13. weight_shape[] = N_;
  14. weight_shape[] = K_;
  15. }
  16. int count = weight_shape[]*weight_shape[];
  17. caffe_gpu_mul<Dtype>(count,static_cast<const Dtype*>(this->blobs_[]->mutable_gpu_diff()),static_cast<const Dtype*>(this->blobs_[]->gpu_mask()),static_cast<Dtype*>(this->blobs_[]->mutable_gpu_diff()));
  18. Dtype* weight_diff = this->blobs_[]->mutable_gpu_diff();
  19. //for(int j=0;j<count;j++)
  20. //weight_diff[j]*=this->masks_[j];
  21. // Gradient with respect to weight
  22. if (transpose_) {
  23. caffe_gpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
  24. K_, N_, M_,
  25. (Dtype)., bottom_data, top_diff,
  26. (Dtype)., weight_diff);
  27. } else {
  28. caffe_gpu_gemm<Dtype>(CblasTrans, CblasNoTrans,
  29. N_, K_, M_,
  30. (Dtype)., top_diff, bottom_data,
  31. (Dtype)., weight_diff);
  32. }
  33. }
  34. if (bias_term_ && this->param_propagate_down_[]) {
  35. const Dtype* top_diff = top[]->gpu_diff();
  36. // Gradient with respect to bias
  37. caffe_gpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)., top_diff,
  38. bias_multiplier_.gpu_data(), (Dtype).,
  39. this->blobs_[]->mutable_gpu_diff());
  40. }
  41. if (propagate_down[]) {
  42. const Dtype* top_diff = top[]->gpu_diff();
  43. // Gradient with respect to bottom data
  44. if (transpose_) {
  45. caffe_gpu_gemm<Dtype>(CblasNoTrans, CblasTrans,
  46. M_, K_, N_,
  47. (Dtype)., top_diff, this->blobs_[]->gpu_data(),
  48. (Dtype)., bottom[]->mutable_gpu_diff());
  49. } else {
  50. caffe_gpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans,
  51. M_, K_, N_,
  52. (Dtype)., top_diff, this->blobs_[]->gpu_data(),
  53. (Dtype)., bottom[]->mutable_gpu_diff());
  54. }
  55. }
  56. }

至此修改完毕。

另外,caffe在新的版本中已添加sparse_参数,参考 https://github.com/BVLC/caffe/pulls?utf8=%E2%9C%93&q=sparse

CNN压缩:为反向传播添加mask(caffe代码修改)的更多相关文章

  1. 反向传播BackPropagation

    http://www.cnblogs.com/charlotte77/p/5629865.html http://www.cnblogs.com/daniel-D/archive/2013/06/03 ...

  2. BP(back propagation)反向传播

    转自:http://www.zhihu.com/question/27239198/answer/89853077 机器学习可以看做是数理统计的一个应用,在数理统计中一个常见的任务就是拟合,也就是给定 ...

  3. cs231n(三) 误差反向传播

    摘要 本节将对反向传播进行直观的理解.反向传播是利用链式法则递归计算表达式的梯度的方法.理解反向传播过程及其精妙之处,对于理解.实现.设计和调试神经网络非常关键.反向求导的核心问题是:给定函数 $f( ...

  4. CS231n课程笔记翻译5:反向传播笔记

    译者注:本文智能单元首发,译自斯坦福CS231n课程笔记Backprop Note,课程教师Andrej Karpathy授权翻译.本篇教程由杜客翻译完成,堃堃和巩子嘉进行校对修改.译文含公式和代码, ...

  5. 【cs231n】反向传播笔记

    前言 首先声明,以下内容绝大部分转自知乎智能单元,他们将官方学习笔记进行了很专业的翻译,在此我会直接copy他们翻译的笔记,有些地方会用红字写自己的笔记,本文只是作为自己的学习笔记.本文内容官网链接: ...

  6. DL反向传播理解

    作者:寒小阳 时间:2015年12月. 出处:http://blog.csdn.net/han_xiaoyang/article/details/50321873 声明:版权所有,转载请联系作者并注明 ...

  7. 卷积神经网络(CNN)反向传播算法

    在卷积神经网络(CNN)前向传播算法中,我们对CNN的前向传播算法做了总结,基于CNN前向传播算法的基础,我们下面就对CNN的反向传播算法做一个总结.在阅读本文前,建议先研究DNN的反向传播算法:深度 ...

  8. CNN中卷积层 池化层反向传播

    参考:https://blog.csdn.net/kyang624823/article/details/78633897 卷积层 池化层反向传播: 1,CNN的前向传播 a)对于卷积层,卷积核与输入 ...

  9. 《神经网络的梯度推导与代码验证》之CNN的前向传播和反向梯度推导

    在FNN(DNN)的前向传播,反向梯度推导以及代码验证中,我们不仅总结了FNN(DNN)这种神经网络结构的前向传播和反向梯度求导公式,还通过tensorflow的自动求微分工具验证了其准确性.在本篇章 ...

随机推荐

  1. C++中发声函数Beep详解

    By zhcs 以前,我听过一个神犇用C++函数做的音乐,当时的心里就十分激动:哇,好厉害啊,好神啊. 这次,我终于通过自己无助的盲目的摸索.研究,写出了这篇文章(此时我的内心是鸡冻的233) 下面是 ...

  2. WPF 简易手风琴 (ListBox+Expander)

    概述 之前听说很多大神的成长之路,几乎都有个习惯--写博文,可以有效的对项目进行总结.从而提高开发的经验.所以初学WPF的我想试试,顺便提高一下小学作文的能力.O(∩_∩)O哈哈~ 读万卷书不如行万里 ...

  3. 也许是目前实现最好的js模拟滚动插件

    finger-mover 是一个集成 Fingerd(移动端以手指为单位的事件管理方案) 和 Moved(微型运动方案) 为一体的移动端动效平台,finger-mover 本身并不能为你做什么,但是附 ...

  4. NancyFx 2.0的开源框架的使用-CustomModule(自定义模块)

    NancyFx框架的自定义模块 新建一个空的Web项目 然后通过NuGet库安装下面的包 Nancy Nancy.Hosting.Aspnet 然后添加Models,Module,Views三个文件夹 ...

  5. Jmeter 初学(一)

    Jmeter 目前属于比较流行的测试工具,即可做自动化测试也可以做性能测试,而且比较方便. 环境准备: Jmeter 运行环境需要跑在java环境,首先需要安装一下java的环境,由于我目前使用的Jm ...

  6. 使用adb报错;error: unknown host service

    用adb往虚拟机Genymotion上安装apk时报错 报这个错误是因为主机端口5037被占用 接下来就要查看5037被哪个应用程序占用,然后结束该程序,才能使用adb 在cmd输入命令netstat ...

  7. NodeJs的包漏洞扫描与漏洞测试攻击

    一个典型的Node应用可能会有几百个,甚至上千个包依赖(大部分的依赖是间接的,即下载一个包,这个包会依赖其他的好多包),所以最终的结果是,应用程序就会像是这个样子的:

  8. MyBatis_Generator插件的安装以及简单使用

    MyBatis_Generator_1.3.1.zip 1       下载安装包 安装包名称:MyBatis_Generator_1.3.1.zip 2       在Eclipse上进行安装 l  ...

  9. “织梦”CMS注入高危漏洞情况

    "织梦"CMS注入高危漏洞情况 作者:    时间:2014-04-17   "织梦"CMS是由上海卓卓网络科技有限公司研发的一款网站建站系统软件,又称&quo ...

  10. iOS CAReplicatorLayer 实现脉冲动画效果

    iOS CAReplicatorLayer 实现脉冲动画效果 效果图 脉冲数量.速度.半径.透明度.渐变颜色.方向等都可以设置.可以用于地图标注(Annotation).按钮长按动画效果(例如录音按钮 ...