本文主要分析caffe
inner_product_layer
源码,主要内容如下:
-
结合使用以及
proto
定义介绍InnerProductLayer
的参数; -
简要分析
Filler
初始化,caffe中的layer参数,例如constant
,gaussian
; -
InnerProductLayer
的函数 核心LayerSetUp
参数初始化,Reshape
,Forward_cpu
以及矩阵的运算底层调用cublas
运算;
1. 结合使用以及proto
定义介绍InnerProductLayer
的参数;
下面我们来看下全连接层InnerProductLayer
, 成员变量定义如下:
template <typename Dtype>
class InnerProductLayer : public Layer<Dtype> {
public:
explicit InnerProductLayer(const LayerParameter& param)
: Layer<Dtype>(param) {}
protected:
int M_; // batch size/输入样本个数
int K_; // 输入特征长度
int N_; // 输出神经元数量
bool bias_term_; //是否添加偏置
Blob<Dtype> bias_multiplier_; //偏置的乘子
bool transpose_; ///< if true, assume transposed weights
};
InnerProduct
的常见使用示例如下:
layer {
name: "ip2"
type: "InnerProduct"
bottom: "fc1"
top: "fc2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
2. Filler
初始化,caffe中的layer参数,例如constant
, gaussian
;
参数填充的proto定义如下:
/填充参数,设置一些初始化参数
message FillerParameter {
// The filler type.
optional string type = 1 [default = 'constant'];
optional float value = 2 [default = 0]; // the value in constant filler
optional float min = 3 [default = 0]; // the min value in uniform filler
optional float max = 4 [default = 1]; // the max value in uniform filler
optional float mean = 5 [default = 0]; // the mean value in Gaussian filler
optional float std = 6 [default = 1]; // the std value in Gaussian filler
}
下面给出具体的定义,简单起见仅仅给出ConstantFiller的定义:
//在网络初始化时,根据layer的定义进行初始参数的填充。
template <typename Dtype>
class Filler {
public:
explicit Filler(const FillerParameter& param) : filler_param_(param) {}
virtual ~Filler() {}
virtual void Fill(Blob<Dtype>* blob) = 0;
protected:
FillerParameter filler_param_;
}; // class Filler
template <typename Dtype>
class ConstantFiller : public Filler<Dtype> {
public:
explicit ConstantFiller(const FillerParameter& param)
: Filler<Dtype>(param) {}
virtual void Fill(Blob<Dtype>* blob) {
Dtype* data = blob->mutable_cpu_data();
const int count = blob->count();
const Dtype value = this->filler_param_.value();
for (int i = 0; i < count; ++i) {
data[i] = value;
}
};
根据类型获取不同的filter指针:
template <typename Dtype>
Filler<Dtype>* GetFiller(const FillerParameter& param) {
const std::string& type = param.type();
if (type == "constant") {
return new ConstantFiller<Dtype>(param);
} else if (type == "gaussian") {
return new GaussianFiller<Dtype>(param);
} else if (type == "uniform") {
return new UniformFiller<Dtype>(param);
} else if (type == "xavier") {
return new XavierFiller<Dtype>(param);
} else {
CHECK(false) << "Unknown filler name: " << param.type();
}
return (Filler<Dtype>*)(NULL);
}
3. InnerProductLayer
的函数
LayerSetUp初始化参数(读取配置初始化N, 对权重weight以及bias填充):
template<typename Dtype>
void InnerProductLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype> *> &bottom, const vector<Blob<Dtype> *> &top) {
const int num_output = this->layer_param_.inner_product_param().num_output();
// whether to have bias terms
bias_term_ = this->layer_param_.inner_product_param().bias_term();
N_ = num_output;
//For examples, shape is (N, C, H, W),and axis == 1, K_ = C*H*W
K_ = bottom[0]->count(axis);
if (this->blobs_.size() > 0) {// Check if we need to set up the weights
LOG(INFO) << "Skipping parameter initialization";
} else {
if (bias_term_) {this->blobs_.resize(2);}
else { this->blobs_.resize(1); }
vector<int> weight_shape(2);// 权值初始化, shape size is 2 not 4
this->blobs_[0].reset(new Blob<Dtype>(weight_shape));
// get filter type and then fill weights
shared_ptr<Filler<Dtype> > weight_filler(GetFiller<Dtype>(
this->layer_param_.inner_product_param().weight_filler()));
weight_filler->Fill(this->blobs_[0].get());// fill the weights
if (bias_term_) {// If necessary, intiialize and fill the 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());
}
} // end of parameter initialization
}
Reshape函数在layer的初始化,以及修改网络的输入个数(如batch size)。
template <typename Dtype>
void InnerProductLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
const int new_K = bottom[0]->count(axis);
CHECK_EQ(K_, new_K)
<< "Input size incompatible with inner product parameters.";
M_ = bottom[0]->count(0, axis); // M_就是batch size N/输入的样本个数
vector<int> top_shape = bottom[0]->shape(); // top_shape:[N,C,H,W]
top_shape.resize(axis + 1);// top_shape:[N,C]
top_shape[axis] = N_; // top_shape:[N,N_]
top[0]->Reshape(top_shape);// 设置top的形状大小
if (bias_term_) {// Set up the bias multiplier
vector<int> bias_shape(1, M_);
bias_multiplier_.Reshape(bias_shape);
caffe_set(M_, Dtype(1), bias_multiplier_.mutable_cpu_data());
}
}
接下来就是前向传播Forward_cpu:
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();
// top = bottom * weight + bias (option)
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);
}
}
矩阵的运算底层调用的是cublas运算。
//C=alpha*A*B+beta*C
template<>
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) {
int lda = (TransA == CblasNoTrans) ? K : M;
int ldb = (TransB == CblasNoTrans) ? N : K;
cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
ldb, beta, C, N);
}
caffe
系列源码分析介绍
本系列深度学习框架caffe
源码分析主要内容如下:
1. caffe源码分析-cmake 工程构建:
caffe源码分析-cmake 工程构建主要内容:
自己从头构建一遍工程,这样能让我更好的了解大型的项目的构建。当然原始的caffe的构建感觉还是比较复杂(主要是cmake),我这里仅仅使用cmake构建,而且简化点,当然最重要的是支持CLion直接运行调试(如果需要这个工程可以评论留下你的邮箱,我给你发送过去)。
2. caffe的数据内存分配类SyncedMemory
, 以及类Blob
数据传输的媒介.
主要内容:
caffe源码分析-SyncedMemory
caffe源码分析-Blob
其中Blob
分析给出了其直接与opencv的图片相互转化以及操作,可以使得我们更好的理解Blob
.
3. caffe layer
的源码分析,包括从整体上说明了layer
类别以及其proto定义与核心函数.
内容如下:
caffe源码分析-layer
caffe源码分析-ReLULayer
caffe源码分析-inner_product_layer
caffe源码分析-layer_factory
首先分析了最简单的layer
Relu
,然后在是inner_product_layer全连接层
, 最后是layer_factory
caffe中 以此工厂模式create各种Layer.
4. 数据输入层,主要是多线程+BlockingQueue的方式读取数据训练:
内容如下:
caffe源码分析-BlockingQueue
caffe源码分析-InternalThread
caffe源码分析-DataReader
5. IO处理例如读取proto文件转化为网络,以及网络参数的序列化
内容如下:
caffe源码分析-DataTransformer
caffe源码分析-db, io
6. 最后给出了使用纯C++结合多层感知机网络训练mnist的示例
内容如下:
caffe c++示例(mnist 多层感知机c++训练,测试)
类似与caffe
一样按照layer、solver、loss、net
等模块构建的神经网络实现可以见下面这篇blog,相信看懂了这个python的代码理解caffe框架会更简单点.
最后如果需要cmake
+ CLion
直接运行调试caffe
的代码工程,可以评论留下你的邮箱,我给你发送过去.