caffe原始碼分析-inner_product_layer
本文主要分析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
原始碼分析主要內容如下:
自己從頭構建一遍工程,這樣能讓我更好的瞭解大型的專案的構建。當然原始的caffe的構建感覺還是比較複雜(主要是cmake),我這裡僅僅使用cmake構建,而且簡化點,當然最重要的是支援CLion直接執行除錯(如果需要這個工程可以評論留下你的郵箱,我給你傳送過去)。
2. caffe的資料記憶體分配類SyncedMemory
, 以及類Blob
資料傳輸的媒介.
主要內容:
caffe原始碼分析-SyncedMemory
caffe原始碼分析-Blob
其中Blob
分析給出了其直接與opencv的圖片相互轉化以及操作,可以使得我們更好的理解Blob
.
3. caffe layer
的原始碼分析,包括從整體上說明了layer
類別以及其proto定義與核心函式.
首先分析了最簡單的layer
Relu
,然後在是inner_product_layer全連線層
, 最後是layer_factory
caffe中 以此工廠模式create各種Layer.
4. 資料輸入層,主要是多執行緒+BlockingQueue的方式讀取資料訓練:
5. IO處理例如讀取proto檔案轉化為網路,以及網路引數的序列化
6. 最後給出了使用純C++結合多層感知機網路訓練mnist的示例
內容如下:
類似與caffe
一樣按照layer、solver、loss、net
等模組構建的神經網路實現可以見下面這篇blog,相信看懂了這個python的程式碼理解caffe框架會更簡單點.
最後如果需要cmake
+ CLion
直接執行除錯caffe
的程式碼工程,可以評論留下你的郵箱,我給你傳送過去.