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 源码分析主要内容如下:

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_factorycaffe中 以此工厂模式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框架会更简单点.

神经网络python实现


最后如果需要cmake + CLion直接运行调试caffe的代码工程,可以评论留下你的邮箱,我给你发送过去.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券