1. 程式人生 > >caffe原始碼分析-inner_product_layer

caffe原始碼分析-inner_product_layer

本文主要分析caffe inner_product_layer原始碼,主要內容如下:

  1. 結合使用以及proto定義介紹InnerProductLayer的引數;

  2. 簡要分析Filler初始化,caffe中的layer引數,例如constantgaussian;

  3. InnerProductLayer的函式 核心LayerSetUp引數初始化, ReshapeForward_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引數,例如constantgaussian;

引數填充的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_factorycaffe中 以此工廠模式create各種Layer.

4. 資料輸入層,主要是多執行緒+BlockingQueue的方式讀取資料訓練:
5. IO處理例如讀取proto檔案轉化為網路,以及網路引數的序列化
6. 最後給出了使用純C++結合多層感知機網路訓練mnist的示例

內容如下:

類似與caffe一樣按照layer、solver、loss、net等模組構建的神經網路實現可以見下面這篇blog,相信看懂了這個python的程式碼理解caffe框架會更簡單點.

最後如果需要cmake + CLion直接執行除錯caffe的程式碼工程,可以評論留下你的郵箱,我給你傳送過去.