全连接层解析(二)——源码解析
来源:互联网 发布:淘宝店铺男装名字大全 编辑:程序博客网 时间:2024/06/05 17:24
C++非常差,整理下来三个目的:第一搞懂caffe原理,第二在这个过程中会学C++,整理下来,便于回头梳理,第三和志轩的那个约定。第四努力当一个不被志轩抛弃的菜逼。
- Inner_Product Layer.hpp
先看Inner_Product Layer.hpp:
template <typename Dtype>
class InnerProductLayer : public Layer<Dtype> {
public:
explicit InnerProductLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
很明显,内积层是继承于Layer,然后是LayerSetUp,层建立,两个参数,bottom和top,Reshape每个SetUP之后都必须有一个Reshape实现。
virtual inline const char* type() const { return "InnerProduct"; } virtual inline int ExactNumBottomBlobs() const { return 1; } virtual inline int ExactNumTopBlobs() const { return 1; }
整个代码块规定了底部Blob的个数,Top Blob的个数,这里是全连接层,所以是bottom和top的Blob都只有一个。
protected: virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top); virtual void Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom); virtual void Backward_gpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
这部分是forward和backwards,具体实现见Inner_product Layer.cpp,最后代码:
int M_; int K_; int N_; bool bias_term_; Blob<Dtype> bias_multiplier_; bool transpose_; ///< if true, assume transposed weights};
这里是定义各种变量,M_是num_minibatch, N_是 num_output, bool bias_term_ 是否有bias项,bias_multiplier_ 暂时不说 ,后面解释。bool transpose是否w需要偏置。到此Inner _product实现了什么功能已经说明,具体的实现则需要去Inner_product Layer.cpp中一探究竟。
- Inner_Product Layer.cpp
const int num_output = this->layer_param_.inner_product_param().num_output(); bias_term_ = this->layer_param_.inner_product_param().bias_term(); transpose_ = this->layer_param_.inner_product_param().transpose();
声明了输出维数num_ output(即:N_),bias,transpose,
N_ = num_output; const int axis = bottom[0]->CanonicalAxisIndex( this->layer_param_.inner_product_param().axis()); // Dimensions starting from "axis" are "flattened" into a single // length K_ vector. For example, if bottom[0]'s shape is (N, C, H, W), // and axis == 1, N inner products with dimension CHW are performed. K_ = bottom[0]->count(axis);
这部分写的很清楚了,注释里有,就不解释了。
接下来再看下面的代码:
if (this->blobs_.size() > 0) { LOG(INFO) << "Skipping parameter initialization"; } else { if (bias_term_) { this->blobs_.resize(2); } else { this->blobs_.resize(1); } // 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_; }
上面代码片其实是对w和b进行初始化,而初始化有两种形式,一种是随机初始化,一种是利用现有保存的model进行初始化。
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
先给Blob分配内存,然后获取一个filler(人为规定)——GetFiller,然后在fill这个filler。同理bias的填充代码一样,不贴了。
} // parameter initialization this->param_propagate_down_.resize(this->blobs_.size(), true);}
这里是规定后向传播的Blob的个数
template <typename Dtype>void InnerProductLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { // Figure out the dimensions const int axis = bottom[0]->CanonicalAxisIndex( this->layer_param_.inner_product_param().axis()); const int new_K = bottom[0]->count(axis); CHECK_EQ(K_, new_K) //检查输入尺寸和全连接层的输入是否一致。 << "Input size incompatible with inner product parameters."; // The first "axis" dimensions are independent inner products; the total // number of these is M_, the product over these dimensions. M_ = bottom[0]->count(0, axis); //主维数 即 num_output // The top shape will be the bottom shape with the flattened axes dropped, // and replaced by a single axis with dimension num_output (N_).// 输出神经元数目 vector<int> top_shape = bottom[0]->shape(); top_shape.resize(axis + 1); top_shape[axis] = N_; top[0]->Reshape(top_shape); //将输出的维度变为M_*N_,原来是M_*K_,K_=CHW // Set up the bias multiplier if (bias_term_) { vector<int> bias_shape(1, M_); bias_multiplier_.Reshape(bias_shape); caffe_set(M_, Dtype(1), bias_multiplier_.mutable_cpu_data());//这里说明一下 bias_multiplier是将一个标量变成一个向量,之前一个样本,我们的bias是N_个不同的bias,这N_个不同的bias值组成了一个N维向量,而现在有M个向量,所以要实现这个问题,需要M_个相同的bias向量,每个向量有N_个不同的bias值。 }}
代码分析注释在源码里了。下面看前向传播和后向传播。
template <typename Dtype>void InnerProductLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); Dtype* top_data = top[0]->mutable_cpu_data(); const Dtype* weight = this->blobs_[0]->cpu_data(); caffe_cpu_gemm<Dtype>(CblasNoTrans, transpose_ ? CblasNoTrans : CblasTrans, M_, N_, K_, (Dtype)1., bottom_data, weight, (Dtype)0., top_data); 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); }}template <typename Dtype>void InnerProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { if (this->param_propagate_down_[0]) { const Dtype* top_diff = top[0]->cpu_diff(); const Dtype* bottom_data = bottom[0]->cpu_data(); // Gradient with respect to weight 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()); } } if (bias_term_ && this->param_propagate_down_[1]) { const Dtype* top_diff = top[0]->cpu_diff(); // Gradient with respect to bias caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff, bias_multiplier_.cpu_data(), (Dtype)1., this->blobs_[1]->mutable_cpu_diff()); } if (propagate_down[0]) { const Dtype* top_diff = top[0]->cpu_diff(); // Gradient with respect to bottom data if (transpose_) { caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, M_, K_, N_, (Dtype)1., top_diff, this->blobs_[0]->cpu_data(), (Dtype)0., bottom[0]->mutable_cpu_diff()); } else { caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, K_, N_, (Dtype)1., top_diff, this->blobs_[0]->cpu_data(), (Dtype)0., bottom[0]->mutable_cpu_diff()); } }}这里要提到一个东西,是gemm,blas里的矩阵相乘实现。backwards里的代码实现了三个东西,第一对w的导数,第二对b的导数,第三求求前一层delta。
- 全连接层解析(二)——源码解析
- 全连接层解析——源码解析
- 全连接层解析(一)
- 解析ViewPager(二)——ViewPager源码解析
- caffe源码简单解析—Layer层
- Caffe框架源码剖析(7)—全连接层InnerProductLayer
- Caffe框架源码剖析(7)—全连接层InnerProductLayer
- Storyboard 全解析 (二)
- OKHttp源码解析二(复用连接池)
- STKFramework层源码解析
- RoundedImageView源码解析(二)RoundedDrawable解析
- caffe源码 全连接层
- 基于rt-thread+lwip源码分析-LWIP的IP层数据处理代码解析(lwip源码解析二)
- Java层Binder全解析。
- zencart全解析(二)
- Storyboard全解析(二)
- Hadoop RPC源码解析——Server类(二)
- OkHttp源码解析(二)——整体流程(下)
- JS window对象
- 图解密码技术
- Android模块化编程——WebView使用之与JavaScript交互
- Docker简介
- 解决mysql忘记帐号密码
- 全连接层解析(二)——源码解析
- 报表页面集成天气,简单三步,一看就懂
- Android Fragment完全解析,关于碎片你所需知道的一切
- WEB页面中常见的四种控件的必须的测试
- java 实现支付宝
- SQL SERVER T-SQL一些常用语句使用记录
- 网络获取图片轮播
- C++单例模式对比
- 使用射线碰撞攻击目标