/************************************************************************************************************************
文件说明:
【1】blob.hpp文件位于../libcaffe/include.hpp下
【2】Blob是caffe中实际处理和传递数据的数据封装包,并且在CPU和GPU之间有数据同步能力
【3】Blob是一个模板类,所以在实例化Blob对象时,需要传递对象的类型参数
Layer的计算和连接:
【1】Layer是caffe模型的【本质内容】和【执行计算】的基本单元。
【2】Layer可以进行很多运算,如:convolve(卷积)、pool(池化)、inner product(内积)、sigmod等非线性运算,元素级别的数
据变换,normalize(归一化)、load data(数据加载)、softmax和loss
【3】一个Layer通过底部bottom(底部)连接层接收数据,通过top(顶部)连接层输出数据
【4】每一个Layer层都定义了3中重要的运算:
【1】setup(初始化设置):在模型初始化时,重置Layers及其相互之间的连接
【2】forward(前向传播):从bottom层中接收数据,进行计算后,将输出送入到top层中
【3】backforward(反向传播):给定相对于top层输出的梯度,计算其相对于输入的梯度,并传递到bottom层。一个有
参数的layer需要计算相对各个参数的梯度值,并存储在内部。
【5】需要特别指出的,Forward和Backward函数分别有CPU和GPU两种实现方式。如果没有GPU版本,那么layer将转向作为备用选
型的CPU方式。尽管这样会增加额外的数据传递成本(输入数据由GPU上复制到CPU,之后输出数据从CPU又复制回到GPU),但是
对于做一些快速实验,这样的操作还是很方便的。
【6】总的来说,layer承担了网络的【两个核心操作】:
【1】forward pass(前向传播):接收输入,并计算输出。
【2】backward pass(反向传播):接收关于输出的梯度,计算相对于参数和输入的梯度并反向传播给在它前面的层。由此
组成每个layer的前向和反向通道
【7】由于caffe网络的组合性和其代码的模块化,自定义layer是很容易的。只要定义好layer的:
【1】setup
【2】forward
【3】backward
就可以将layer纳入到网络。
开发环境:
Win10+caffe+cuda7.5+opencv+vs2013
时间地点:
陕西师范大学 文津楼 2017.8.7
作 者:
九 月
*************************************************************************************************************************/
#ifndef CAFFE_LAYER_H_
#define CAFFE_LAYER_H_
#include <algorithm>
#include <string>
#include <vector>
#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer_factory.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/math_functions.hpp"
/**
Forward declare boost::thread instead of including boost/thread.hpp
to avoid a boost/NVCC issues (#1009, #1010) on OSX.
*/
namespace boost { class mutex; }
namespace caffe
{
template <typename Dtype>
class Layer
{
public:
/****************************************************************************************************************
函数说明:
显式的构造函数,从LayerParameter对象中加载配置
C++语言:
【1】一般地,除非有明显理由想要定义隐式转换,否则,单形参构造函数应该为explicit
【2】C++关键字explicit用来修饰类的构造函数,指明该构造函数是显式的。explicit关键字只能用于类内部的构造函数
声明上,在类定义外部不能重复它。
【3】只有单个形参,而且形参是本类类型对象的引用常量,这样的构造函数称为复制构造函数。
****************************************************************************************************************/
explicit Layer(const LayerParameter& param): layer_param_(param), is_shared_(false)
{
phase_ = param.phase(); //【1】设置当前阶段(TRAIN/TEST)
if (layer_param_.blobs_size() > 0)
{
blobs_.resize(layer_param_.blobs_size()); //【2】按照layer_param_设置本身Blob对象个数。并依次将每个Blob
// 对象尺寸调整为layer_param中的Blob尺寸。
for (int i = 0; i < layer_param_.blobs_size(); ++i)
{
blobs_[i].reset(new Blob<Dtype>());
blobs_[i]->FromProto(layer_param_.blobs(i));
}
}
}
virtual ~Layer() {} //【3】虚虚构函数
//【4】配置函数,实现常用层配置接口,不可被覆盖
void SetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
{
InitMutex();
CheckBlobCounts(bottom, top);
LayerSetUp(bottom, top);
Reshape(bottom, top);
SetLossWeights(top);
}
//【5】层配置虚函数,做特定类型层的相关配置,该类型层自己实现
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) {}
//【6】在数据并行中,一个Layer是否被多个Net共享
virtual inline bool ShareInParallel() const
{
return false;
}
inline bool IsShared() const
{
return is_shared_;
}
inline void SetShared(bool is_shared)
{
CHECK(ShareInParallel() || !is_shared)<< type() << "Layer does not support sharing.";
is_shared_ = is_shared;
}
//【7】纯虚函数
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) = 0;
/****************************************************************************************************************
函数原型:
inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
函数功能:
前向传播函数,给定Bottom Blob,计算Top Blob和los,返回值为当前层Loss
函数参数:
【1】const vector<Blob<Dtype>*>& bottom------输入参数-----给定的Bottom Blob
【2】const vector<Blob<Dtype>*>& top---------输出参数-----计算出来的Top Blob
函数返回值:
Dtype------返回当前层的loss
****************************************************************************************************************/
inline Dtype Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top);
/****************************************************************************************************************
函数原型:
inline void Backward(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom);
函数功能:
【1】反向传播函数,给定Top Blob误差梯度,计算Bottom Blob误差梯度
【2】该函数会调用相应【设备包装函数】,如Backward_cpu或Backward_gpu来实现真正的计算过程,由派生类负责实现
函数参数:
【1】const vector<Blob<Dtype>*>& top--------------其diff域来自上一层的误差梯度
【2】const vector<bool>& propagate_down---多路开关,与Bottom Blob矢量维度相同,每个值表示是否将
误差梯度传递到对应的Bottom Blob
【3】const vector<Blob<Dtype>*>& bottom--------其diff域由函数计算得到
函数返回值:
Dtype------返回当前层的loss
****************************************************************************************************************/
inline void Backward(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down,
const vector<Blob<Dtype>*>& bottom);
/****************************************************************************************************************
函数说明:
返回Layer内部可训练的权值、偏置项Blob向量
*****************************************************************************************************************/
vector<shared_ptr<Blob<Dtype> > >& blobs()
{
return blobs_;
}
/****************************************************************************************************************
函数说明:
返回Layer初始化参数,由ProtoBuffer提供
*****************************************************************************************************************/
const LayerParameter& layer_param() const { return layer_param_; }
/****************************************************************************************************************
函数说明:
将Layer初始化参数写入ProtoBuffer的缓冲区
*****************************************************************************************************************/
virtual void ToProto(LayerParameter* param, bool write_diff = false);
/****************************************************************************************************************
函数说明:
返回与某个Top Blob相关的标量Loss
*****************************************************************************************************************/
inline Dtype loss(const int top_index) const
{
return (loss_.size() > top_index) ? loss_[top_index] : Dtype(0);
}
/****************************************************************************************************************
函数说明:
设置与某个Top Blob相关的标量loss值
*****************************************************************************************************************/
inline void set_loss(const int top_index, const Dtype value)
{
if (loss_.size() <= top_index)
{
loss_.resize(top_index + 1, Dtype(0));
}
loss_[top_index] = value;
}
/****************************************************************************************************************
函数说明:
返回层类型字符串,便于识别,由派生类负责实现
*****************************************************************************************************************/
virtual inline const char* type() const { return ""; }
/****************************************************************************************************************
函数说明:
返回该Layer需要的输入Blob数目,-1表示不关心,由派生类负责实现
*****************************************************************************************************************/
virtual inline int ExactNumBottomBlobs() const { return -1; }
virtual inline int MinBottomBlobs() const { return -1; }
virtual inline int MaxBottomBlobs() const { return -1; }
/****************************************************************************************************************
函数说明:
返回该Layer需要的输出的Blob数目,-1表示不关心,由派生类负责实现
*****************************************************************************************************************/
virtual inline int ExactNumTopBlobs() const { return -1; }
virtual inline int MinTopBlobs() const { return -1; }
virtual inline int MaxTopBlobs() const { return -1; }
/****************************************************************************************************************
函数说明:
返回该Layer是否具有相同的输入/输出Blob,由派生类负责实现
*****************************************************************************************************************/
virtual inline bool EqualNumBottomTopBlobs() const { return false; }
virtual inline bool AutoTopBlobs() const { return false; }
virtual inline bool AllowForceBackward(const int bottom_index) const
{
return true;
}
inline bool param_propagate_down(const int param_id)
{
return (param_propagate_down_.size() > param_id) ?
param_propagate_down_[param_id] : false;
}
inline void set_param_propagate_down(const int param_id, const bool value)
{
if (param_propagate_down_.size() <= param_id)
{
param_propagate_down_.resize(param_id + 1, true);
}
param_propagate_down_[param_id] = value;
}
inline Phase phase() { return phase_; }
virtual inline void set_phase(Phase phase)
{
phase_ = phase;
}
protected:
LayerParameter layer_param_; //【1】保存Layer参数的ProtoBuffer对象
Phase phase_; //【2】枚举类新,Layer当前所处阶段,可选TRAIN或TEST
vector<shared_ptr<Blob<Dtype> > > blobs_; //【3】Layer内部权值或偏置项,以Blob方式组织
vector<bool> param_propagate_down_; //【4】标志位,是否计算对应参数的误差梯度
vector<Dtype> loss_; //【5】标志位,在目标函数中,是否每个Top Blob都有非零权重
//【注意】:下面四个函数,我们在各个Layer派生类中会经常看到
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top) = 0;
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
{
return Forward_cpu(bottom, top);
}
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom) = 0;
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom)
{
Backward_cpu(top, propagate_down, bottom);
}
virtual void CheckBlobCounts(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
{
if (ExactNumBottomBlobs() >= 0) {
CHECK_EQ(ExactNumBottomBlobs(), bottom.size())<< type() << " Layer takes " << ExactNumBottomBlobs()<< " bottom blob(s) as input.";
}
if (MinBottomBlobs() >= 0)
{
CHECK_LE(MinBottomBlobs(), bottom.size())<< type() << " Layer takes at least " << MinBottomBlobs()<< " bottom blob(s) as input.";
}
if (MaxBottomBlobs() >= 0)
{
CHECK_GE(MaxBottomBlobs(), bottom.size())<< type() << " Layer takes at most " << MaxBottomBlobs()<< " bottom blob(s) as input.";
}
if (ExactNumTopBlobs() >= 0)
{
CHECK_EQ(ExactNumTopBlobs(), top.size())<< type() << " Layer produces " << ExactNumTopBlobs()<< " top blob(s) as output.";
}
if (MinTopBlobs() >= 0)
{
CHECK_LE(MinTopBlobs(), top.size())<< type() << " Layer produces at least " << MinTopBlobs()<< " top blob(s) as output.";
}
if (MaxTopBlobs() >= 0)
{
CHECK_GE(MaxTopBlobs(), top.size())<< type() << " Layer produces at most " << MaxTopBlobs()<< " top blob(s) as output.";
}
if (EqualNumBottomTopBlobs())
{
CHECK_EQ(bottom.size(), top.size())<< type() << " Layer produces one top blob as output for each "<< "bottom blob input.";
}
}
/**
* Called by SetUp to initialize the weights associated with any top blobs in
* the loss function. Store non-zero loss weights in the diff blob.
*/
inline void SetLossWeights(const vector<Blob<Dtype>*>& top)
{
const int num_loss_weights = layer_param_.loss_weight_size();
if (num_loss_weights)
{
CHECK_EQ(top.size(), num_loss_weights) << "loss_weight must be ""unspecified or specified once per top blob.";
for (int top_id = 0; top_id < top.size(); ++top_id)
{
const Dtype loss_weight = layer_param_.loss_weight(top_id);
if (loss_weight == Dtype(0)) { continue; }
this->set_loss(top_id, loss_weight);
const int count = top[top_id]->count();
Dtype* loss_multiplier = top[top_id]->mutable_cpu_diff();
caffe_set(count, loss_weight, loss_multiplier);
}
}
}
private:
bool is_shared_; //【1】标志位,表明该Layer是否被其他Net共享
shared_ptr<boost::mutex> forward_mutex_; //【2】如果该Layer被共享,则需要该信号量保证顺序执行
void InitMutex(); //【3】初始化forward_mutex_
void Lock(); //【4】加锁
void Unlock(); //【5】解锁
DISABLE_COPY_AND_ASSIGN(Layer);
}; // class Layer
// Forward and backward wrappers. You should implement the cpu and
// gpu specific implementations instead, and should not change these
// functions.
template <typename Dtype>
inline Dtype Layer<Dtype>::Forward(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
{
// Lock during forward to ensure sequential forward
Lock();
Dtype loss = 0;
Reshape(bottom, top);
switch (Caffe::mode())
{
case Caffe::CPU:
Forward_cpu(bottom, top);
for (int top_id = 0; top_id < top.size(); ++top_id)
{
if (!this->loss(top_id)) { continue; }
const int count = top[top_id]->count();
const Dtype* data = top[top_id]->cpu_data();
const Dtype* loss_weights = top[top_id]->cpu_diff();
loss += caffe_cpu_dot(count, data, loss_weights);
}
break;
case Caffe::GPU:
Forward_gpu(bottom, top);
#ifndef CPU_ONLY
for (int top_id = 0; top_id < top.size(); ++top_id)
{
if (!this->loss(top_id)) { continue; }
const int count = top[top_id]->count();
const Dtype* data = top[top_id]->gpu_data();
const Dtype* loss_weights = top[top_id]->gpu_diff();
Dtype blob_loss = 0;
caffe_gpu_dot(count, data, loss_weights, &blob_loss);
loss += blob_loss;
}
#endif
break;
default:
LOG(FATAL) << "Unknown caffe mode.";
}
Unlock();
return loss;
}
template <typename Dtype>
inline void Layer<Dtype>::Backward(const vector<Blob<Dtype>*>& top,const vector<bool>& propagate_down,const vector<Blob<Dtype>*>& bottom)
{
switch (Caffe::mode())
{
case Caffe::CPU:
Backward_cpu(top, propagate_down, bottom);
break;
case Caffe::GPU:
Backward_gpu(top, propagate_down, bottom);
break;
default:
LOG(FATAL) << "Unknown caffe mode.";
}
}
// Serialize LayerParameter to protocol buffer
template <typename Dtype>
void Layer<Dtype>::ToProto(LayerParameter* param, bool write_diff)
{
param->Clear();
param->CopyFrom(layer_param_);
param->clear_blobs();
for (int i = 0; i < blobs_.size(); ++i) {
blobs_[i]->ToProto(param->add_blobs(), write_diff);
}
}
} // namespace caffe
#endif // CAFFE_LAYER_H_
【深度学习】【Caffe源代码解读2】笔记20 Caffe的基本数据结构之Layer
最新推荐文章于 2019-08-14 17:40:08 发布