一、Blob到底是什么东西?
可以理解Blob就是个4维数组,n*c*h*w;每个维度分别表示批处理数量,通道个数,高度和宽度;
这个4维数组的存在形式是以类的形式存在,可以理解为blob就是个数据存储容器。
二、Blob的数据成员
protected:
shared_ptr<SyncedMemory> data_;
shared_ptr<SyncedMemory> diff_;
shared_ptr<SyncedMemory> shape_data_;
vector<int> shape_;
int count_;
int capacity_;
Blob的数据成员很简单,就上面的6个。
其中:
SyncedMemory是内存管理的类,现在先不管它。
data_是存储内容的指针(boost库的shared_ptr共享智能指针),比如存储前向传播的图像数据;
diff_也是存储数据的指针, 但它存储的是反向传播的梯度数据;
shape_data_和shape_都是表示blob的形状, 即n,c,h,w的大小;
count_表示blob的元素数量,即:n*c*h*w;
capacity_表示当前元素容量, 因为blob可能会reshape,当count_比capacity大时,capacity会更新成count_;
三/Bolb的成员函数
3.1 构造函数:
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);
3个构造函数,都是操作这个blob的4维元素的大小;
3.2 Reshape函数:
void Reshape(const int num, const int channels, const int height,
const int width);
void Reshape(const vector<int>& shape);
void Reshape(const BlobShape& shape);
void ReshapeLike(const Blob& other);
Reshape函数,用来重新指定Blob的4维元素大小,将每一维度的大小存放在成员变量shape_中,count_和capacity_也都赋值了,
创建SyncedMemory类, (data_和diff_在这并没有赋值,也未分配内存)
3.3 与矩阵结构有关的方法
inline const vector<int>& shape() const { return shape_; } //返回4维数组的结构
inline int shape(int index) const {
return shape_[CanonicalAxisIndex(index)]; // 返回某一维度的大小;
}
inline int num_axes() const { return shape_.size(); } //返回维度值
inline int count() const { return count_; } //返回元素数量
inline int count(int start_axis, int end_axis) const { //返回某些维度的元素数量
inline int count(int start_axis) const { //返回从某一维度到结束的元素数量
return count(start_axis, num_axes());
}
inline int CanonicalAxisIndex(int axis_index) const { } //返回数组的下标
inline int num() const { return LegacyShape(0); } //返回n维度的元素个数
inline int channels() const { return LegacyShape(1); } //返回c维度的元素个数
inline int height() const { return LegacyShape(2); }
inline int width() const { return LegacyShape(3); }
inline int LegacyShape(int index) const { //返回某一维度的个数
inline int offset(const int n, const int c = 0, const int h = 0) //返回指定元素相对首地址的偏移量
inline int offset(const vector<int>& indices) const {
}
可以发现 这些方法都是获得blob结构, 元素个数, 及维度信息等方法的。
3.4 与数据访问相关的方法
void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false, //从一个Blob中复制数据到另一个blob
bool reshape = false);
inline Dtype data_at(const int n, const int c, const int h, //通过offset的偏移量,访问指定位置的数据
const int w) const {
return cpu_data()[offset(n, c, h, w)];
}
inline Dtype diff_at(const int n, const int c, const int h, //访问指定位置的梯度值
const int w) const {
return cpu_diff()[offset(n, c, h, w)];
}
inline Dtype data_at(const vector<int>& index) const { //返回指定元素的数据
return cpu_data()[offset(index)];
}
inline Dtype diff_at(const vector<int>& index) const { //返回指定元素的梯度
return cpu_diff()[offset(index)];
}
inline const shared_ptr<SyncedMemory>& data() const { //返回data_地址
CHECK(data_);
return data_;
}
inline const shared_ptr<SyncedMemory>& diff() const { //返回diff_地址
CHECK(diff_);
return diff_;
}
const Dtype* cpu_data() const; //分配内存,并返回首地址
void set_cpu_data(Dtype* data); //设置数据在cpu上
const int* gpu_shape() const; //内存分配,反回在gpu上的地址,share_ptr调用
const Dtype* gpu_data() const; //内存分配,反回在gpu上的地址 ,data_调用
void set_gpu_data(Dtype* data);
const Dtype* cpu_diff() const;
const Dtype* gpu_diff() const;
Dtype* mutable_cpu_data(); //返回可以改写的data地址
Dtype* mutable_gpu_data();
Dtype* mutable_cpu_diff();
Dtype* mutable_gpu_diff();
void Update(); //data-diff
这一部分是Blob的核心, 包括cpu和gpu的内存分配及访问。
3.5 Blob的其它数学计算
Dtype asum_data() const; //求data的l1范数
Dtype asum_diff() const; //求diff的l1范数
Dtype sumsq_data() const; //求data的l2范数
Dtype sumsq_diff() const; //求diff的l2范数
void scale_data(Dtype scale_factor); //缩放
void scale_diff(Dtype scale_factor);
void ShareData(const Blob& other); //data共享
void ShareDiff(const Blob& other);
bool ShapeEquals(const BlobProto& other); //判断是否相等。
这部分的方法设计到部分数学计算。
四、总结
Blob仅仅是一个数据结构,4维矩阵, 它还同时提供了本身结构、 内存分配、访问数据、及其它一些方法。 这部分仅仅是程序相关,与算法还没有多大关系。
五、Blob在Python接口中的调用
以VGG16的模型来分析,假设预测的图片为1张,则最后输出的Blob结构为(1,1000),1 表示第一张图,而1000表示1000个分类的概率值。
output=net.forward()
output_prob=output['prob'][0]
output 表示前向传播结束后最后一层返回的Blob, 它是python的字典结构, 通过关键字可以访问。
即: output["prob"][0] 注意关键字prob 是网络最后一层softmax层的 名称, 而【0】 表示第一张图片的各个概率值, 从而output_prob变成了一维结构,表示第一张图片的1000个概率分布。
六、Blob在C++接口中的调用
net->Forward();
const float *out_probs = net->output_blobs()[0]->cpu_data();
net是深度学习的网络, 前向传播计算,结束后, 调用output_blobs()方法, 返回net网络中的 net_output_blobs_成员变量,这是一个vector<Blob<Dtype> *>类型的, 返回的是所有网络输出的blob动态数组, 通过下标【0】 找到输出的第一个blob, 然后调用blob的cpu_data()方法(上面讲过),返回blob中data数据的指针, 将此指针赋值给out_probs, 然后利用out_probs就可以对输出的结果进行操作了。
关于其中Net网络的细节部分,等解析玩layer网络后, 在解析net网络, 请持续关注。。。