您的位置:首页 > Web前端

caffe全连接层(INNER_PRODUCT)源码注释与分析

2014-12-29 17:43 399 查看
// Copyright 2014 BVLC and contributors.
//这是全连接层的实现
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/layer.hpp"
#include "caffe/vision_layers.hpp"
#include "caffe/util/math_functions.hpp"

//  主要是三个方法,setup,forward,backward
//  setup 初始化网络参数,包括了w和b
//	forward 前向传播的实现
//	backward 后向传播的实现

//   M_ 表示的样本数
//	K_ 表示单个样本的特征长度
//	N_ 表示输出神经元的个数
namespace caffe {

template <typename Dtype>
void InnerProductLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
vector<Blob<Dtype>*>* top)
{
//若输入或输出的blob的size不为1的话输出警告
CHECK_EQ(bottom.size(), 1) << "IP Layer takes a single blob as input.";
CHECK_EQ(top->size(), 1) << "IP Layer takes a single blob as output.";

//通过读取配置proto文件获得输出神经元的个数及是否使用偏置项
const int num_output = this->layer_param_.inner_product_param().num_output();
bias_term_ = this->layer_param_.inner_product_param().bias_term();

// Figure out the dimensions
M_ = bottom[0]->num();//表示样本数
K_ = bottom[0]->count() / bottom[0]->num();//表示单个样本的特征长度,count_ = num_ * channels_ * height_ * width_;
N_ = num_output; //全连接之后输出的神经元的个数
(*top)[0]->Reshape(bottom[0]->num(), num_output, 1, 1);//全连接层输出的Blob维数为样本的个数*输出神经元的个数*1*1(M*N)

// Check if we need to set up the weights
if (this->blobs_.size() > 0)
{
LOG(INFO) << "Skipping parameter initialization";
}
else
{
//如果配置文件使用偏置项,则开辟2个Blob类智能指针,否则开辟一个
if (bias_term_)
{
this->blobs_.resize(2);
}
else
{
this->blobs_.resize(1);
}

//初始化权重和偏差
// Intialize the weight
//vector<shared_ptr<Blob<Dtype> > > blobs_;
//blobs_[0]指向权重矩阵,blobs_[1]指向偏置矩阵
//因为是全链接,所以权重矩阵的维数为N_*K_
this->blobs_[0].reset(new Blob<Dtype>(1, 1, N_, K_));//新开辟一个Blob,指针返回给blobs_[0];

// fill the weights
//根据配置文件中的权重核( weight_filler)的类型初始化填充权重矩阵blobs_[0];
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
//填充偏置矩阵,每个输出单元对应一个偏置,共N_个
if (bias_term_)
{
this->blobs_[1].reset(new Blob<Dtype>(1, 1, 1, N_));
shared_ptr<Filler<Dtype> > bias_filler(GetFiller<Dtype>(
this->layer_param_.inner_product_param().bias_filler()));
bias_filler->Fill(this->blobs_[1].get());
}
}  // parameter initialization

// Setting up the bias multiplier
if (bias_term_)
{
//只是把大小(size)设置了,并未申请内存
//shared_ptr<SyncedMemory> bias_multiplier_;
bias_multiplier_.reset(new SyncedMemory(M_ * sizeof(Dtype)));
Dtype* bias_multiplier_data =
reinterpret_cast<Dtype*>(bias_multiplier_->mutable_cpu_data());//返回数据在CPU里面的指针

for (int i = 0; i < M_; ++i)
{
bias_multiplier_data[i] = 1.;
}
}
}

template <typename Dtype>
//实现的功能就是 y=wx+b
//  x为输入,维度 M_*K_
//	y为输出,维度 M_*N_
//	w为权重,维度 K_*N_
//	b为偏置,维度 N_*1_
//一批次处理多个样本,在每一批次中权重矩阵与偏置矩阵是不变的
Dtype InnerProductLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
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();//内存中的权重矩阵是N*K

//它的功能其实很直观,即C←αA×B+βC,前两个参数控制A,B是否转置
//其中A维度是MxK,B维度是KxN,C维度为MxN

//全连接层的forward包括了两步:
//这一步表示 y←wx,或者说是y←xw'
//bottom_data:M*K, weight:N*K, top_data:M*N
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasTrans, M_, N_, K_,
(Dtype)1., bottom_data, weight, (Dtype)0., top_data);
//# 这一步表示 y←y+b
if (bias_term_)
{
const Dtype* bias = this->blobs_[1]->cpu_data();

caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, N_, 1,
(Dtype)1., reinterpret_cast<const Dtype*>(bias_multiplier_->cpu_data()),
bias, (Dtype)1., top_data);
}
//所以两步连起来就等价于y=wx+b
//处理之后矩阵的每一行代表一个样本的N个输出神经元,共M行
return Dtype(0);
}

template <typename Dtype>
void InnerProductLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const bool propagate_down,
vector<Blob<Dtype>*>* bottom) {
//data传递的是数据,diff传递的是梯度,top_diff的维度是N*M,每一列代表一个样本的error term
const Dtype* top_diff = top[0]->cpu_diff();
const Dtype* bottom_data = (*bottom)[0]->cpu_data();
// Gradient with respect to weight
//更新W
//其中A维度是NxM,B维度是MxK,C维度为NxK
//top_diff:M*N, bottom_data:M*K, this->blobs_[0]->mutable_cpu_diff():N*K
//C=A'*B,this->blobs_[0]->mutable_cpu_diff()是权重梯度矩阵(N*K)
caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans, N_, K_, M_, (Dtype)1.,
top_diff, bottom_data, (Dtype)0., this->blobs_[0]->mutable_cpu_diff());
if (bias_term_) {
// Gradient with respect to bias
caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff,
reinterpret_cast<const Dtype*>(bias_multiplier_->cpu_data()), (Dtype)0.,
this->blobs_[1]->mutable_cpu_diff());
}
if (propagate_down) {
// Gradient with respect to bottom data
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());
}
}

INSTANTIATE_CLASS(InnerProductLayer);

}  // namespace caffe
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: