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的使用,具体的操作可以有针对性的的理解集体的实现。