Caffe Code Analysis-3
2016-06-27 20:47
260 查看
Caffe Code Analysis - 全连接层和卷积层
本周我们一起学习了Caffe 的源代码,重点讨论了全连接层和部分卷积层。现就本周内容进行一个复盘总结。
1. C++类模板和函数模板回顾
caffe工程的所有layer都是从几个基本layer继承而来,而为了工程代码的简介和可重用性,caffe的layer层都是基于泛型程序设计而开发的。现就类模板和函数模板进行简单回顾
1.1 类模板
类模板是对一批仅仅成员数据类型不同的类的抽象,程序员只要为这一批类所组成的整个类家族创建一个类模板,给出一套程序代码,就可以用来生成多种具体的类—-类模板的实例类模板 template <类型名 参数名1,类型名参数名2,…> class 类名 { 类声明体 };
1.2 函数模板
函数模板提供了一种机制通过它我们可以保留函数定义和函数调用的语义在一个程序位置上封装了一段代码确保在函数调用之前实参只被计算一次.函数模板提供一个种用来自动生成各种类型函数实例的算法程序员对于函数接口参数和返回类型中的全部或者部分类型进行参数化(parameterize)而函数体保持不变
函数模板 Template<class 类型参数1,class 类型参数2,……> 返回值类型 模板名(形参表) { 函数体 };
//模板使用举例 #include<iostream> using namespace std; Template<class T> void Swap(T &x, T & y) { T tmp = x; x = y; y = tmp; } int main() { int n = 1, m = 2; Swap(n, m); double p = 1.0, q = 2.0; Swap(p, q); return 0; }
2. 全连接层和卷积层
2.1 全连接层
全连接层为inner_product_layer.cpp主要有三个函数组成
- LayerSetUp
- Forward
- Backward
下面对三个函数进行简单的介绍
2.1.1 LayerSetUp
// Initialize the weights vector<int> weight_shape(2); if (transpose_) { weight_shape[0] = K_; weight_shape[1] = N_; } else { weight_shape[0] = N_; weight_shape[1] = K_; } this->blobs_[0].reset(new Blob<Dtype>(weight_shape)); // fill the weights shared_ptr<Filler<Dtype> > weight_filler(GetFiller<Dtype>( this->layer_param_.inner_product_param().weight_filler())); weight_filler->Fill(this->blobs_[0].get()); // If necessary, intiialize and fill the bias term if (bias_term_) { vector<int> bias_shape(1, N_); this->blobs_[1].reset(new Blob<Dtype>(bias_shape)); shared_ptr<Filler<Dtype> > bias_filler(GetFiller<Dtype>( this->layer_param_.inner_product_param().bias_filler())); bias_filler->Fill(this->blobs_[1].get()); } } // parameter initialization this->param_propagate_down_.resize(this->blobs_.size(), true); }
2.1.2 Forward层
实现的功能 Y=WX+Bx为输入,维度 MxK
y为输出,维度 Nx1
w为权重,维度 NxK
b为偏置,维度 Nx1
caffe_cpu_gemm<Dtype>(CblasNoTrans, transpose_ ? CblasNoTrans : CblasTrans, M_, N_, K_, (Dtype)1., bottom_data, weight, (Dtype)0., top_data); // 实现y =wx if (bias_term_) { caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, N_, 1, (Dtype)1., bias_multiplier_.cpu_data(), this->blobs_[1]->cpu_data(), (Dtype)1., top_data); } // 实现了 y = y + b // 分析一下caffe_cpu_gemm void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA, const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K, const float alpha, const float* A, const float* B, const float beta, float* C) const CBLAS_TRANSPOSE TransA # A是否转置 const CBLAS_TRANSPOSE TransB # B是否转置 # 直观 const int M const int N const int K const float alpha const float* A const float* B const float beta, float* C # 其中A维度是MxK,B维度是KxN,C维度为MxN
2.1.2 Backward层
实现的功能如下步骤
更新w
更新b
计算δ
实现的功能如下公式
代码分析
if (transpose_) { caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans, K_, N_, M_, (Dtype)1., bottom_data, top_diff, (Dtype)1., this->blobs_[0]->mutable_cpu_diff()); } else { caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans, N_, K_, M_, (Dtype)1., top_diff, bottom_data, (Dtype)1., this->blobs_[0]->mutable_cpu_diff()); }
// 更新w
需要更新的w的梯度的维度是NxK
公式中的δ(l)j对应的是bottom_data,维度是KxM
公式中的δ(l+1)i 对应的是top_diff,维度是NxM
caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff, bias_multiplier_.cpu_data(), (Dtype)1., this->blobs_[1]->mutable_cpu_diff()); // 更新b
对caffe_cpu_gemv 进行分析
void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,const int N, const float alpha, const float* A, const float* x,const float beta, float* y) #实现的功能类似 Y←αAX + βY# 其中A的维度为 MxN # X是一个向量, 维度为 Nx1# Y是结果 ,也是一个向量, 维度为Mx1 const CBLAS_TRANSPOSE TransA # 是否对A进行转置 const int M const float alpha const float* x const float beta float* y
2.2 卷积层
卷积层为conv_layer.cpp实现如下的功能
在全连接中的实现如下:
卷积层的操作可以套回全连接层的参数求导公式
只需要增加一步im2col这一步的意思是将首先将整张图片按照卷积的窗口大小切好(按照stride来切,可以有重叠)然后各自拉成一列.对于这个小窗口内拉成一列的神经元来说来说,它们跟下一层神经元就是全连接了,所以这个小窗口里面的梯度计算就可以按照全连接来计算就可以了.
具体实现上:
1.第一部分先将 input 变成一维具有(channel, height, weight)形式的array。在conv_im2col_cpu里实现。
2.第二部分做卷积,实际上是将3D的卷积变成2D的矩阵相乘。
2.2.1 forward函数
template <typename Dtype> Void BaseConvolutionLayer<Dtype>::forward_cpu_gemm(const Dtype* input, const Dtype* weights, Dtype* output, bool skip_im2col) { const Dtype* col_buff = input; if (!is_1x1_) { if (!skip_im2col) { conv_im2col_cpu(input, col_buffer_.mutable_cpu_data()); } col_buff = col_buffer_.cpu_data(); } for (int g = 0; g < group_; ++g) { caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, conv_out_channels_ / group_, conv_out_spatial_dim_, kernel_dim_ / group_, (Dtype)1., weights + weight_offset_ * g, col_buff + col_offset_ * g, (Dtype)0., output + output_offset_ * g); } }
这样做的出发点就是把3D运算变成2D运算, 对于weight, 把每一个小的卷积块平展成一个array, 所有的小卷积块变成矩阵。对应的,input image也要变成一个2维矩阵。实际上,就是对每一个output上的一点, 拿到在原图中得到这点的对应kernel的那些点,并flat它们。
如下图
forward_cpu_bias 函数,把计算卷积中的bias加上。
template <typename Dtype> void BaseConvolutionLayer<Dtype>::forward_cpu_bias(Dtype* output, const Dtype* bias) { caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, num_output_, height_out_ * width_out_, 1, (Dtype)1., bias, bias_multiplier_.cpu_data(), (Dtype)1., output); }
Forward_cpu函数,对每一张图进行卷积计算,然后存储。
先计算weight, 再计算bias。
template <typename Dtype> void ConvolutionLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const Dtype* weight = this->blobs_[0]->cpu_data(); for (int i = 0; i < bottom.size(); ++i) { const Dtype* bottom_data = bottom[i]->cpu_data(); Dtype* top_data = top[i]->mutable_cpu_data(); for (int n = 0; n < this->num_; ++n) { this->forward_cpu_gemm(bottom_data + bottom[i]->offset(n), weight, top_data + top[i]->offset(n)); if (this->bias_term_) { const Dtype* bias = this->blobs_[1]->cpu_data(); this->forward_cpu_bias(top_data + top[i]->offset(n), bias); } } } }
参考资料:
http://zhangliliang.com/2015/02/11/about-caffe-code-convolutional-layer/
http://blog.csdn.net/ayst123/article/details/43924151
http://cogprints.org/5869/1/cnn_tutorial.pdf
相关文章推荐
- CUDA搭建
- 稀疏自动编码器 (Sparse Autoencoder)
- 白化(Whitening):PCA vs. ZCA
- softmax回归
- 卷积神经网络初探
- Some Notes of Caffe Installation
- Some Notes of Python Interfaces Pycaffe (Caffe)
- TensorFlow人工智能引擎入门教程之九 RNN/LSTM循环神经网络长短期记忆网络使用
- TensorFlow人工智能引擎入门教程之十 最强网络 RSNN深度残差网络 平均准确率96-99%
- TensorFlow人工智能入门教程之十一 最强网络DLSTM 双向长短期记忆网络(阿里小AI实现)
- TensorFlow人工智能引擎入门教程之十二 Caffe转换tensorflow并 跨平台调用
- TensorFlow人工智能入门教程之十四 自动编码机AutoEncoder 网络
- TensorFlow人工智能引擎入门教程所有目录
- 如何用70行代码实现深度神经网络算法
- 近200篇机器学习&深度学习资料分享(含各种文档,视频,源码等)
- 安装caffe过程记录
- DIGITS的安装与使用记录
- 图像识别和图像搜索
- 51CTO学院优质新课抢先体验-5折好课帮你技能提升、升职加薪
- caffe ubuntu 14.04 install