参考网址:https://www.cnblogs.com/louyihang-loves-baiyan/p/5149628.html

1、caffe代码层次
熟悉blob,layer,net,solver几类,
blob:作为数据输出的媒介,无论是网络权重参数,还是输入数据,都是转化为blob数据结构来存储。
layer:作为网络的基础单元,神经网络中层与层间的数据节点、前后传递都在数据结构中被实现,
net:作为网络的整体骨架,决定了网络中的层次数目以及各个层的类别
solver:作为网络的求解策略,涉及到求解优化问题的策略选择以及参数确定方面,修改这个模块的话一般都是会研究DL的优化求解的方向。

1、1、blob的类型描述
caffe 内部采用的数据类型主要是对protocol buffer所定义的数据结构的继承,因此可以在尽可能小的内存占用下获得很高的效率,blob看成一个4维的结构体(包含数据和梯度),实际上,它们只是一维的指针而已,其4维结构通过shape属性得以计算
1、2、blob的重要成员函数和变量
shared_ptr<SyncedMemory> data_//数据
shared_ptr<SyncedMemory> diff_//梯度
void blob<Dtype>::Reshape(const int num,const int channels,const int height,const int width)
重新修改blob的形状(4维),并根据形状来申请动态内存存储数据和梯度。
inline int count(int start_axis,int end_axis)const
计算blob所需要的基本数据单元的数量。
在更高一级的layer中blob用下面的形式表示学习到的参数
vector<shared_ptr<Blob<Dtype>>> blobs_;
这里使用的是一个blob的容器是因为某些layer包含多组学习参数,比如多个卷积核的卷积层。
vector<Blob<Dtype>*> &bottom;
vector<Blob<Dtype>*> *top
2、2layer:
2、2、15大layer派生类型
caffe十分强调网络的层次性具体五大类
NeuronLayer类定义于neuron_layers.hpp中,其派生类主要是元素级别的运算(Dropout运算,激活函数ReLu,Sigmoid等),运算均为同址计算(in-place computation,返回值覆盖原值而占用新的内存)。
LossLayer定义于loss_layers.hpp中,其派生类会产生loss,
数据层定义于data_layer.hpp中,作为网络的最底层,主要实现数据格式的转换。
特征表达层 vision_layers.hpp,特征表达功能,具体包含卷积操作,pooling操作
网络连接层和激活函数 定义于common_layers.hpp,caffe提供了单个层与多个层的连接,并在这个头文件中声明。还包括了常用的全连接层innerProductLayer
####################################
2.2.2layer的重要成员函数
在layer内部,数据主要有两种传递方式,正向传导和反向传导。forward和backward有cpu和gpu两种实现。caffe中所有的layer都要用这两种方法传递数据。
virtual void Forward(const vector<Blob<Dtype>*>&bottom,vector<Blob<Dtype>*>*top)=0;
virtual void Backward(const vector<Blob<Dtype>*>&top,const vector<bool> &propagate_down,vector<Blob<Dtype>*>*bottom)=0;
layer类派生出来的层类通过实现这两个虚函数,产生了各式各样功能的层类。Forward是根据bottom计算top的过程,backward则相反(根据top计算bottom)
对于大多数layer来说输入和输出都各连接只有一个layer,对于某些layer存在一对多的情况,比如losslayer和某些连接层。在网络结构定义文件(*.proto)中每一层的参数bottom和top数目决定了vector中元素数目。
layers{
 bottom:"decode1neuron" //该层底下连接的第一个layer
 bottom:"flatdata" //该层底下连接的第二个layer
 top:"l2_error" //该层顶上连接的一个layer
 name:"loss" //该层的名字
 type:EUCLIDEAN_LOSS//该层的类型
 loss_weight:0
}
2.2.3layer的重要成员变量
loss
vector<Dtype> loss_;
每一层又有一个loss_值,大多数layer都是0,只有losslayer可能产生非0的loss_。计算loss是把所有层的loss_相加
learnable parameteres
vector<shared_ptr<Blob<Dtype>>>blobs_;
2.3.Net:
net用容器的形式将多个layer有序的放在一起,其自身功能主要是对逐层layer进行初始化,以及提供update()的接口,
vector<shared_ptr<Layer<Dtype>>> layers_;
vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>*>&bottom,Dtype* loss=NULL);
void Net<Dtype>::Backward();
2.4solver
这个类中包含一个net的指针,主要是实现了训练模型参数采用的优化算法,它派生的类就可以对整个网络进行训练了。
shared_ptr<Net<Dtype>>net_;
不同的模型训练方法通过重载函数ComputeUpdateValue()实现计算update参数的核心功能
virtual void ComputeUpdateValue() = 0;
最后当进行整个网络训练过程时候,实际上在运行caffe.cpp中的train()函数,而这个函数实际上是实例化一个solver对象,初始化后调用了solver中的solve()方法。
ComputeUpdateValue();
net_->Update();
######################################################
caffe源码解析:blob
explicit关键字的作用是禁止单参数构造函数的隐式转换。
inline的作用,将代码进行复制,扩充,可以节省调用的开销,提高执行的效率
1主要变量
shared_ptr<SyncedMemory>data_;
shared_ptr<SyncedMemory>diff_;
shared_ptr<SyncedMemory>shape_data_;
vector<int>shape_;
int count_;
int capacity_;
data_指针,指针类型是shared_ptr,属于boost库的一个智能指针,这一部分主要用来申请内存存储data,diff_用来存储偏差,update data,shape_data和shape_都是存储blob的形状,一个是老版本一个是新版本。count表示blob中的元素个数,也就是个数×通道数×高度×宽度,capacity表示当前的元素个数。

2、主要函数
template<typename Dtype>
class Blob{
  public:
   Blob()
     :data_(),diff_(),count_(0),capacity_(0){}
     explicit Blob(const int num,const int channels,const int height,const int width);
     explicit Blob(const vector<int>& shape);
     void Reshape(const int num,const int channels,const int height,const int width);     
}
blob是一个最基础的类,其中构造函数开辟一个内存空间来存储数据,reshape函数在Layer中的reshape或者forward操作中adjust dimension。同时在改变blob大小时,内存将会被重新分配如果内存大小不够,额外的内存将不会被释放。对input的blob进行reshape,如果立马调用Net::Backward是会出错的,因为reshape之后,要么Net::forward或者Net::Reshape就会被调用来将新的input shape传播到高层。
blob类里面有重载很多个count()函数,主要还是为了统计blob的容量(volume),或者是某一片(slice),从某个axis到具体某个axis的shape乘积
inline int count(int start_axis,int end_axis)
并且blob的index是可以从负坐标开始读
inline int CanonicalAxisIndex(int axis_index)
对于blob中的4个基本变量num,channel,height,width可以直接通过shape(0),shape(1),shape(2),shape(3)来访问
计算offset
inline int offset(const int n,const int c=0,const int h=0,const int w=0)
inline int offset(const vector<int>& indices)
offset计算的方式也支持两种方式,一种直接指定n,c,h,w,或者放到一个vector中进行计算,偏差是根据对应的n,c,h,w,返回的offset是(((n*channels())+c)*height()+h)*width()+w

void CopyFrom(const Blob<Dtype>& source,bool copy_diff=false,bool reshape=false);
从一个blob中copy数据,通过开关控制是否copy_diff,如果是false则copy data,
inline Dtype data_at(const int n,const int c,const int h,const int w)
inline Dtype diff_at(const int n,const int c,const int h,const int w)
inline Dtype data_at(const vector<int>& index)
inline Dtype diff_at(const vector<int>& index)
inline const shared_ptr<SyncedMemory>& data()
inline const shared_ptr<SyncedMemory>& diff()
这一部分函数主要通过给定的位置访问数据,根据位置计算与数据起始的偏差offset,在通过cpu_data*指针获得地址。
const Dtype* cpu_data() const;
void set_cpu_data(Dtype* data);
const int* gpu_shape() const;
const Dtype* cpu_diff() const;
const Dtype* gpu_diff() const;
Dtype* mutable_cpu_data();
Dtype* mutable_gpu_data();
data主要是存储前向传递的数据,后者存储的是后向传播中的梯度。
void Update();
看到update里面调用了caffe_axpy<float>(const int N,const float alpha,const float* X,float* Y){cblas_saxpy(N,alpha,X,1,Y,1);}
void FromProto(const BlobProto& proto,bool reshape=true);
void ToProto(BlobProto* proto,bool write_diff =false) const;
这两个函数主要是将数据序列化,存储到blobproto,这里说到proto是谷歌的一个数据序列化的存储格式,可以实现语言、平台无关、可扩展的序列化结构数据格式。
Dtype asum_data() const;//计算data的L1范数
Dtype asum_diff() const;//
Dtype sumsq_data() const;//计算data的L2范数
Dtype sumsq_data() const;
void scale_data(Dtype scale_factor);//将data部分乘以一个因子
void scale_diff(Dtype scale_factor);

这几个函数是一些零散的功能,
void ShareData(const Blob& other);
void ShareDiff(const Blob& other);
这两个函数是共享data,具体就是将别的blob的data和响应的diff指针给这个blob,实现数据的共享。这个操作会引起blob里面的SyncedMemory被释放,因为shared_ptr指针重置的时候回调用响应的析构器。

#caffe源码解析2:SyncedMem
SyncedMem是内存同步操作
首先是两个全局的内联函数,通过t粗大cudaMallocHost分配的host memory将会被pinned,pinned的意思是内存不会被paged out,内存里是由页作为基本的管理单元。分配的内存可以常驻在内存空间中,空间不会被别的进程所抢占。对多个gpu的并行可以提高稳定性。
这里两个封装过的函数,内部通过cuda来分配主机和释放内存的接口
inline void CaffeMallocHost(void** ptr,size_t size,bool* use_cuda){
#ifndef CPU_ONLY
   if(Caffe::mode()==Caffe::GPU){
     CUDA_CHECK(cudaMallocHost(ptr,size));//GPU模式下cuda分配内存
     *use_cuda =true;
     return;
   }
#endif
  *ptr =malloc(size);//如果没有cuda则通过c的malloc分配
  *use_cuda=false;
  CHECK(*ptr)<<"host allocation of size"<<size<<"failed";
}
inline void CaffeFreeHost(void* ptr,bool use_cuda){
#ifndef CPU_ONLY
  if(use_cuda){
    CUDA_CHECK(cudaFreeHost(ptr));//cuda的主机内存释放操作
    return ;
  }
#endif
 free(ptr);//c的释放操作
}
SyncedMemory类,首先是构造函数和析构函数
class SyncedMomory{
  public:
    SyncedMemory() //参数构造函数,负责初始化
       :cpu_ptr_(NULL),gpu_ptr_(NULL),size_(0),head_(UNINITIALIZED),
        own_cpu_data_(false),cpu_malloc_use_cuda_(false),own_gpu_data_(false),gpu_device_(-1){}
    explicit SyncedMemory(size_t size)//带explicit关键字的,单个参数构造函数,explicit禁止单参数构造函数的隐式转换。
   :cpu_ptr_(NULL),gpu_ptr_(NULL),size_(0),head_(UNINITIALIZED),
        own_cpu_data_(false),cpu_malloc_use_cuda_(false),own_gpu_data_(false),gpu_device_(-1){}
    ~SyncedMemory();//其在析构时调用的也是CaffeFreeHost
}

这几个函数分别是
const void* cpu_data();
void set_cpu_data(void* data);
const void* gpu_data();
void set_gpu_data(void* data);

cpu_data()主要是获得cpu上data的地址,set_cpu_data是将cpu的data指针指向一个新的区域由data指针传入,并且将原来的申请的内存释放。

void* mutable_cpu_data();
void* mutable_gpu_data();
enum SyncedHead{UNINITIALIZED,HEAD_AT_CPU,HEAD_AT_GPU,SYNCED};
SyncedHead head(){return head_;}
size_t size(){return size_;}

前两个分别是返回cpu和gpu上的data指针,并且状态为head_=HEAD_AT_CPU和响应的gpu版本。SyncedHead主要是个枚举类型,用来设定head_的状态,head()函数返回相应的数据状态,size()函数返回数据大小。

#ifndef CPU_ONLY
  void async_gpu_push(const cudaS& stream);
#endif
cuda拷贝的异步传输,数据从cpu拷贝到gpu,异步传输是已经假定caller会在使用之前做操作。

private:
 void to_cpu();
 void to_gpu();
 void* cpu_ptr_;
 void* gpu_ptr_;
 size_t size_;
 SyncedHead head_;
 bool own_cpu_data_;
 bool cpu_malloc_use_cuda_;
 bool own_gpu_data_;
 int gpu_device_;
 DISABLE_COPY_AND_ASSIGN(SyncedMemory);//禁止该类的拷贝与赋值
 
 cpu_ptr和gpu_ptr分别是cpu和gpu的数据指针。
 
 #######################caffe:Layer
 
layer必须实现一个forward function,caffe网络的前一层叫bottom,从bottom中获取blob,并且计算输出blob,根据input的blob以及output blob的error gradient梯度误差计算得到该层的梯度误差。
template<typename Dtype>
class Layer{
  public:
    explicit Layer(const LayerParameter& param)
     : Layer_param_(param),is_shared_(false){
     //set phase and copy blobs(if there are any).
     phase_=param.phase();
     if(layer_param_.blobs_size()>0)
       blobs_.resize(layer_param_.blobs_size());
       
     }

}

caffe源码阅读的更多相关文章

  1. Caffe源码阅读(1) 全连接层

    Caffe源码阅读(1) 全连接层 发表于 2014-09-15   |   今天看全连接层的实现.主要看的是https://github.com/BVLC/caffe/blob/master/src ...

  2. caffe源码阅读(1)-数据流Blob

    Blob是Caffe中层之间数据流通的单位,各个layer之间的数据通过Blob传递.在看Blob源码之前,先看一下CPU和GPU内存之间的数据同步类SyncedMemory:使用GPU运算时,数据要 ...

  3. caffe源码阅读(3)-Datalayer

    DataLayer是把数据从文件导入到网络的层,从网络定义prototxt文件可以看一下数据层定义 layer { name: "data" type: "Data&qu ...

  4. caffe源码阅读(一)convert_imageset.cpp注释

    PS:本系列为本人初步学习caffe所记,由于理解尚浅,其中多有不足之处和错误之处,有待改正. 一.实现方法 首先,将文件名与它对应的标签用 std::pair 存储起来,其中first存储文件名,s ...

  5. caffe源码阅读(2)-Layer

    神经网络是由层组成的,深度神经网络就是层数多了.layer对应神经网络的层.数据以Blob的形式,在不同的layer之间流动.caffe定义的神经网络已protobuf形式定义.例如: layer { ...

  6. caffe源码阅读(1)_整体框架和简介(摘录)

    原文链接:https://www.zhihu.com/question/27982282 1.Caffe代码层次.回答里面有人说熟悉Blob,Layer,Net,Solver这样的几大类,我比较赞同. ...

  7. caffe 源码阅读

    bvlc:Berkeley Vision and Learning Center. 1. 目录结构 models(四个文件夹均有四个文件构成,deploy.prototxt, readme.md, s ...

  8. caffe中batch norm源码阅读

    1. batch norm 输入batch norm层的数据为[N, C, H, W], 该层计算得到均值为C个,方差为C个,输出数据为[N, C, H, W]. <1> 形象点说,均值的 ...

  9. 源码阅读经验谈-slim,darknet,labelimg,caffe(1)

    本文首先谈自己的源码阅读体验,然后给几个案例解读,选的例子都是比较简单.重在说明我琢磨的点线面源码阅读方法.我不是专业架构师,是从一个深度学习算法工程师的角度来谈的,不专业的地方请大家轻拍. 经常看别 ...

随机推荐

  1. ubuntu 17.10 安装mvn

    首先,我用的系统是Ubuntu17.10,编辑器用的 vim ,Maven以 3.5.0为例 第一步,去官网下载maven. 官网下载页面.png 第二步,解压到/opt/maven目录(我安装在这个 ...

  2. MySQL5.7 JSON类型及其相关函数的学习

    mysql> CREATE TABLE `json_table` ( `id` int(11) NOT NULL AUTO_INCREMENT, `info` json NOT NULL, PR ...

  3. 软件包管理(rpm,yum)

    软件包管理相关软件: 软件包管理器的核心功能: .制作软件包 .安装,卸载,升级,查询,效验 Redhat ,SUSE : RPM Debian :dpt 依赖关系; 前端工具;yum ,apt-ge ...

  4. oracle网络服务之beq协议和SDU优化(性能提升可达30%)

    oracle网络服务之beq协议和SDU优化(性能提升可达30%) 12.3.1  BEQ协议 如果Oracle数据库服务端和客户端在同一台机器上,可以使用BEQ连接,BEQ连接采用进程间直接通信,不 ...

  5. 利用Android-FingerprintManager类实现指纹识别

    安卓指纹识别 利用FingerprintManager主类进行指纹识别. Github项目地址 在安卓6.0中新增了API,FingerprintManager类,它是Google提供的帮助访问指纹硬 ...

  6. 【Django模板进阶007】

    本节主要讲 Django模板中的循环,条件判断,常用的标签,过滤器的使用 列表,字典,类的实例的使用 循环:迭代显示列表,字典等中的内容 条件判断:判断是否显示该内容,比如判断是手机访问,还是电脑访问 ...

  7. SpringAOP的xml实例、注解形式实例、概念理解 以及execution表达式实例与概念说明

    (1)Spring AOP的简单应用: -->AOP:(Aspect Orinted Programming)面向切面编程,用于具有横切逻辑的场合,如:访问控制,事务管理,性能检测,由切入点和增 ...

  8. node中redis重连

    项目node中用到redis ,做了的moudle,但是有个问题,两台redis,一台挂了,redis能自动切换,我的项目却不会自动重连: 查了资料,redis本身是实现了重连机制啊,为什么不自动重连 ...

  9. 安装卡巴 OFFICE链接 出现这个过程被中断,由于本机的限制

    今天 安装了卡巴后 office 超链接功能不能使用了,一点击超链接,就会发出警报,说”由于本机的限制,此操作已被取消,请与系统管理员联系“ 解决办法:1打开注册表2到这个位置:HKEY_CURREN ...

  10. 修改 jenkins 主目录

    说明 Jenkins有时需要进行迁移,主目录会发生改变,本文主要讲解如何更改主目录.由于jenkins安装方式的不同,主目录也不一样.本测试环境:Centos6.8 X64.注意:在更改主目录之前,请 ...