【caffe源代码的梳理之二】Blob数据结构

1、Blob数据结构

  caffe使用Blob的4维数组用于存储和交换数据,维度从低到高为(width_, height_, channel_,num_)。其中width_和height_分别表示图像的宽和高,channel_表示颜色的通道如RGB,num_表示第几帧。进行网络层的计算时,每一层的输入输出都以Blob对象最为缓冲,他是caffe的基本存储单元。
  Blob是Caffe中处理和传递实际数据的数据封装包,并且在CPU与GPU之间具有数据同步处理能力。从数学意义上说,blob是按C风格连续存储的N维数组,即在内部所存储的数据是一块连续的内存。
  Blob是用来存储图像数据、网络参数(包括权值、偏置以及它们的梯度)、模型参数、学习到的参数、网络传输过程中产生的数据、网络中间的处理结果、优化过程的偏导数等各种数据。
  Blob可以动态改变数组的尺寸,当拓展数组导致原有内存空间不足以存放下数据时(count_>capacity_),就会通过Reshape函数实现重新确定空间大小。
  Blob数据可以通过Protobuf来做相应的序列化操作,ToProto和FromProto两个函数完成相应的序列化、反序列化(数据解析)操作。
  Caffe基于blobs存储和交换数据。网络各层之间的数据都是通过Blob来传递的。为了便于优化,blobs提供统一的内存接口来存储某种类型的数据,例如批量图像数据、模型参数以及用来进行优化的导数。
  blobsblob使用了一个SyncedMemory类来同步CPU和GPU上的数据,以隐藏同步的细节和最小化传送数据。可根据CPU主机与GPU设备的同步需要,屏蔽CPU/GPU混合运算在计算上的开销。主机和设备上的内存按需分配,以提高内存的使用效率。

2、Blob的消息格式

message BlobShape {  
  //来实现高维数据的封装。即vector(N)>,包含若干Int64位的类型值,分别表示Blob每个纬度的大小;packed表示这些值在内存中紧密排布,没有空隙。
  repeated int64 dim = 1 [packed = true];  
}  

//Blob在磁盘中序列化之后的形态
message BlobProto {  
  optional BlobShape shape = 7;  //可选,包含一个BlobShape对象
  repeated float data = 5 [packed = true];  //包含若干的浮点数元素,存储数据和权值,元素数目由shape或(num,channels,height,width)确定,在内存中紧密排布
  repeated float diff = 6 [packed = true];  //包含若干的浮点数用于存储增量信息,维度与data一致
  repeated double double_data = 8 [packed = true];  
  repeated double double_diff = 9 [packed = true];  

  //数据4D形状 -- 旧版本,已使用"BlobShape shape"代替:  
  optional int32 num = 1 [default = 0];   
  optional int32 channels = 2 [default = 0];  
  optional int32 height = 3 [default = 0];  
  optional int32 width = 4 [default = 0];  
}  

// The BlobProtoVector is simply a way to pass multiple blobproto instances
// around.
message BlobProtoVector {
  repeated BlobProto blobs = 1;
}

3、Blob模版类分析

位置:

include/caffe/blob.hpp

3.1、主要的依赖的文件

src/caffe/proto/caffe.pb.h  //由caffe.proto生成的头文件
include/caffe/Syncedmem.hpp //CPU和GPU共享内存类,用于数据的同步
include/caffe/common.hpp    //命名空间和随机数生成的功能类

3.3、全局常量

const int kMaxBlobAxes = 32;    //表示Blob可以支持的最高维数,目前最高为32维

3.3、主要的成员数据

shared_ptr<SyncedMemory> data_; //存放指向data的指针
shared_ptr<syncedMemory> diff_; //存放指向diff的指针
vector<int> shape_;             //blob的形状信息
int count_;                     //存放有效元素的数目信息
int capacity_;                  //存放Blob容器的容量信息
DISABLE_COPY_AND_ASSIGN(blob);  //禁用拷贝构造函数、赋值运算符重载

3.4、主要的功能和成员函数

3.4.1、Blob构造函数

//默认不带参数的构造函数,初始化count_=0,capacity_=0  
Blob() : data_(), diff_(), count_(0), capacity_(0) {}  

//带参数的显示构造函数,这两个构造函数内部会均会调用Reshape(const vector<int>)函数  
//执行这两个构造函数后,并不会真正分配内存空间,只是用来设置当前blob的shape_、count_和capacity_大小 
//声明为explicit的构造函数不能在隐式转换中使用。
explicit Blob(const int num, const int channels, const int height, const int width);  
explicit Blob(const vector<int>& shape); 

3.4.2、Blob纬度相关的函数

  Reshape系列函数通过输入参数用来设置或重新设置当前blob的shape_、count_和capacity_大小,各个类型的Reshape函数都会转化成调用带vector< int >参数的Reshape函数 。
  shapeEquals函数用来判断Blob数据的形状是否相同。

// 推荐使用带vector<int>参数的Reshape函数  
// 内部会调用SyncedMemory的构造函数,但不会真正分配内存空间  
// 通过num/channes/height/width参数设置shape_、count_和capacity_大小  
void Reshape(const int num, const int channels, const int height, const int width);  
// 通过vector<int>参数设置shape_、count_和capacity_大小  
void Reshape(const vector<int>& shape);  
// 通过类BlobShape参数设置shape_、count_和capacity_大小  
// BlobShape是定义在caffe.proto中的一个message,其字段有dim  
void Reshape(const BlobShape& shape);  
// 通过外部的blob参数来设置shape_、count_和capacity_大小  
void ReshapeLike(const Blob& other); 

//判断blob的形状是否相同
bool ShapeEquals(const BlobProto& other); 

//从另一个Blob对象拷贝data(也可选diff),必要时进行变维
void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false, bool reshape = false);  

3.4.3、data和diff数据访问函数

Blob的数据访问函数,包括CPU和GPU上的数据和梯度值。

//带mutable_前缀的函数是可以对Blob数据进行改写的;其它不带的是只读的,不允许改写数据 

//调用SyncedMemory::cpu_data()函数:只读获取CPU_data指针  
const Dtype* cpu_data() const; 
//调用SyncedMemory::gpu_data()函数:只读获取GPU_data指针 
const Dtype* gpu_data() const; 
//调用SyncedMemory::cpu_data()函数:只读获取CPU_diff指针  
const Dtype* cpu_diff() const;
//调用SyncedMemory::gpu_data()函数:只读获取GPU_diff指针   
const Dtype* gpu_diff() const;

//调用SyncedMemory::mutable_cpu_data()函数:读写获取CPU_data指针 
Dtype* mutable_cpu_data();
//调用SyncedMemory::mutable_gpu_data()函数:读写获取GPU_data指针 
Dtype* mutable_gpu_data(); 
//调用SyncedMemory::mutable_cpu_data()函数:读写获取CPU_diff指针
Dtype* mutable_cpu_diff(); 
//调用SyncedMemory::mutable_gpu_data()函数:读写获取GPU_data指针  
Dtype* mutable_gpu_diff();

//调用SyncedMemory::set_cpu_data(void*)函数:读写获取GPU_diff指针 
void set_cpu_data(Dtype* data); 
//将外部指定的blob的data_指针指向给当前blob的data_,以实现共享data_  
void ShareData(const Blob& other);  
// 将外部指定的blob的diff_指针指向给当前blob的diff_,以实现共享diff_  
void ShareDiff(const Blob& other);  
// 比较两个blob的shape是否相同。BlobProto是定义在caffe.proto中的一个message,其字段有shape(BlobShape)、data、diff、num、channels、height、width  
bool ShapeEquals(const BlobProto& other);   

3.4.4、Blob网络参数的更新

void Update();

//Blob.cpp中的三个更新网络参数的函数,其中int和unsigned int类型的处理没有实现,只有第三个函数实现了
template <> void Blob<unsigned int>::Update() {NOT_IMPLEMENTED; }
template <> void Blob<int>::Update() {NOT_IMPLEMENTED; }
template <typename Dtype>void Blob<Dtype>::Update() {}

3.4.5、Blob数据L1-范数、L2-范数和幅度的缩放

//计算data_的L1范式:向量中各个元素绝对值之和  
Dtype asum_data() const;   
//计算diff_的L1范式:向量中各个元素绝对值之和  
Dtype asum_diff() const;  
//计算data_的L2范式平方:向量中各元素的平方和  
Dtype sumsq_data() const;  
//计算diff_的L2范式平方:向量中各元素的平方和  
Dtype sumsq_diff() const;

// 将data_数据乘以一个因子:X = alpha*X  
void scale_data(Dtype scale_factor);  
// 将diff_数据乘以一个因子:X = alpha*X  
void scale_diff(Dtype scale_factor);  

3.4.6、Blob的数据持久化函数

  Blob的数据持久化函数,通过Protobuf来做相应的序列化/反序列化操作。BlobProto是定义在caffe.proto中的一个message,其字段有shape(BlobShape)、data、diff、num、channels、height、width 。

// 将BlobProto的shape/data/diff分别copy给当前blob的shape_/data_/diff_完成数据解析(反序列化);若reshape参数为true,则会对当前的blob重新进行reshape  
void FromProto(const BlobProto& proto, bool reshape = true); 

// 将Blob的shape_/data_/diff_(如果write_diff为true)分copy BlobProto的shape/data/diff完成序列化  
void ToProto(BlobProto* proto, bool write_diff = false) const; 

这里只是从函数功能的角度大体上介绍了Blob所能对数据data以及diff执行的操作,函数内部的实现细节可以暂时不要完全的关注,在后续的layer和Net中会有更多的对Blob的使用,具体的操作可以有针对性的的理解集体的实现。

猜你喜欢

转载自blog.csdn.net/u013108511/article/details/79470794