caffe源码解析之blob.hpp或blob.cpp
作者:wjmishuai
出处:http://blog.csdn.net/wjmishuai/article/details/50961471 声明:版权所有,转载请注明出处
caffe可以分为三层:Blob、Layer、Net
Blob是一个四维的数组,用于存储数据,包括输入数据、输出数据、权值; Layer层则是神经网络中具体的各层结构,主要用于计算,在根据配置文件初始化网络结构后,前向计算结果,反向更新参数,而它的输入和输出都是Blob数据; Net的层就是多个Layer组合而成的有向无环图结构,也就是具体的网络了。 Layer和Net的代码有待深入,尤其是Layer的代码,caffe实现了差不多40种不同的Layer层,里面有不同的激活函数,这个要好好研究下。
【caffe源码解析之blob.hpp或blob.cpp】关于Blob就这么多内容,毕竟就是一个统一的数据存取接口,学到了一些封装的手法,可以看看CPU和GPU一些接口的封装;另一方面是对于Protocol Buffer有了一些了解。
#ifndef CAFFE_BLOB_HPP_//防止头文件重复引用
#define CAFFE_BLOB_HPP_#include
#include
#include /*common.hpp主要用来单例化Caffe类,
*并封装了boost和CUDA随机数生成的函数,
*提供了统一的接口
*/
#include "common.hpp"
/*caffe.pb.h是google protocol buffer根据caffe.proto自动生成的。
*使用protocol buffer有这些好处,一方面可以用文本文件定义结构化的数据类型,
*另一方面可以生成查询效率更高、占空间更小的二进制文件
*/
#include "caffe.pb.h"
//syncedmem主要用于分配内存和释放内存
#include "syncedmem.hpp"
//math_functions里面封装了很多cblas矩阵运算
#include "util/math_functions.hpp"const int kMaxBlobAxes = INT_MAX;
namespace caffe {//命名空间为caffe/*
*主要数据有两个data和diff,用num、channels、height和width
*这四个维度来确定数据的具体位置,做一些数据查询和Blobreshape的操作
*/
template
class Blob {
public:
Blob()//blob的构造函数
: data_(), diff_(), count_(0), capacity_(0) {}//data_(), diff_()是用于存放数据的指针,/*num_, channel_, height_, width_主要用来做定位offset和reshape处理。
*对于输入(n, c, h, w)位置的数据位置为((n*channels_+c)*height_+h)*width_+w,
*可以依据位置取data_()或diff_()中的数据。
*/
explicit Blob(const int num, const int channels, const int height,
const int width);
explicit Blob(const vector& shape);
/*Reshape函数的作用是改变一个blob的大小
*1.读入num_,channels_,height_,width_的大小
*2.计算count_:count_ = num_ * channels_ * height_ * width_;
*3.如果count_不为0,则重新为data_和diff_分配一块空间
*如果count为0,则都初始化为NULL
*/
void Reshape(const int num, const int channels, const int height,
const int width);
void Reshape(const vector& shape);
void Reshape(const BlobShape& shape);
//ReshapeLike的作用是为data_和diff_ 重新分配一块空间,大小和另一个blob的一样
void ReshapeLike(const Blob& other);
inline string shape_string() const {
ostringstream stream;
for (int i = 0;
i < shape_.size();
++i) {
stream << shape_[i] << " ";
}
stream << "(" << count_ << ")";
return stream.str();
}
inline const vector& shape() const { return shape_;
}//返回shape//返回第i个索引的shape,index可以是负数,
inline int shape(int index) const {
return shape_[CanonicalAxisIndex(index)];
}
inline int num_axes() const { return shape_.size();
}//返回shape的大小
inline int count() const { return count_;
}//返回参数count//计算一个slice的体积
inline int count(int start_axis, int end_axis) const {int count = 1;
for (int i = start_axis;
i < end_axis;
++i) {
count *= shape(i);
}
return count;
}
//计算从从一个特定的axis到最后一个axis的slice的体积。
inline int count(int start_axis) const {
return count(start_axis, num_axes());
}//对负数(index可能是负数)规范化的一个函数
inline int CanonicalAxisIndex(int axis_index) const {if (axis_index < 0) {
return axis_index + num_axes();
}
return axis_index;
}/// 功能是返回一些成员变量,比如,num,channels,height,width等
inline int num() const { return LegacyShape(0);
}
inline int channels() const { return LegacyShape(1);
}
inline int height() const { return LegacyShape(2);
}
inline int width() const { return LegacyShape(3);
}
inline int LegacyShape(int index) const {if (index >= num_axes() || index < -num_axes()) {
/*如果index超出索引范围,但是在范围 [0, 3] 或[-4, -1]内,
*这种特殊的情况下,模拟一个填充值,用来填补axes 。
*/
return 1;
}
return shape(index);
}
//计算偏移量,因为数据在内存是以一维数组形式的,所以需要计算偏移量来访问
inline int offset(const int n, const int c = 0, const int h = 0,
const int w = 0) const {return ((n * channels() + c) * height() + h) * width() + w;
}inline int offset(const vector& indices) const {int offset = 0;
for (int i = 0;
i < num_axes();
++i) {
offset *= shape(i);
if (indices.size() > i) {offset += indices[i];
}
}
return offset;
}
/**
*从source拷贝数据。copy_diff作为标志来区分是拷贝data还是拷贝diff
*1.如果是GPU: 如果是拷贝diff:调用cudaMemcpy函数将source的diff拷贝过来,否则拷贝data
*2.如果是CPU: 如果是拷贝diff:调用memcpy函数将source的diff拷贝过来 否则拷贝data
*/
void CopyFrom(const Blob& source, bool copy_diff = false,
bool reshape = false);
//从cpu访问数据data
inline Dtype data_at(const int n, const int c, const int h,
const int w) const {
return cpu_data()[offset(n, c, h, w)];
}
//从cpu访问数据diff
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)];
}
//从cpu访问数据data
inline Dtype data_at(const vector& index) const {
return cpu_data()[offset(index)];
}
//从cpu访问数据diff
inline Dtype diff_at(const vector& index) const {
return cpu_diff()[offset(index)];
}
//从cpu访问数据data
inline const shared_ptr& data() const {
return data_;
}
//从cpu访问数据diff
inline const shared_ptr& diff() const {
return diff_;
}
/**调用SyncedMemory的函数,来返回数据的指针;
前两个调用to_cpu(),返回cpu_ptr;
*第一个对于data对象,第二个对于diff对象
*后两个调用to_gpu(),返回gpu_ptr;第一个对于data对象,第二个对于diff对象
*/
void set_cpu_data(Dtype* data);
const Dtype* cpu_data() const;
const Dtype* gpu_data() const;
const Dtype* cpu_diff() const;
const Dtype* gpu_diff() const;
Dtype* mutable_cpu_data();
Dtype* mutable_gpu_data();
Dtype* mutable_cpu_diff();
Dtype* mutable_gpu_diff();
/**更新data_的数据,就是减去diff_的数据。
*1.判断blob的位置
*2.调用caffe_axpy:在math_functions.cpp可以找到该函数的实现,其实这函数也是封装了mkl的函数。这里调用是为了实现了两个向量的减法。
*3.调用caffe_gpu_axpy:在math_functions.cpp可以找到该函数的实现,其实这函数也是封装了cublas的函数。这里调用是为了实现了两个向量的减法。
*/
void Update();
/**功能:从proto读数据进来,其实就是反序列化
*1.先把blob的大小改变一下
*2.得到cpu中数据的地址
*3.用proto中的data覆盖blob中的data
*4.用proto中的diff覆盖blob中的diff
*/
void FromProto(const BlobProto& proto, bool reshape = true);
//把blob数据保存到proto中
void ToProto(BlobProto* proto, bool write_diff = false) const;
//计算绝对值的data总和(L1范数)。
Dtype asum_data() const;
//计算绝对值的diff总和(L1范数)。
Dtype asum_diff() const;
//计算绝对值的data总和(L2范数)。
Dtype sumsq_data() const;
//计算绝对值的diff总和(L2范数)。
Dtype sumsq_diff() const;
//通过常量因子测量blob data
void scale_data(Dtype scale_factor);
通过常量因子测量blob diff
void scale_diff(Dtype scale_factor);
//从other的blob复制data和diff的值
void ShareData(const Blob& other);
void ShareDiff(const Blob& other);
bool ShapeEquals(const BlobProto& other);
protected:
shared_ptr data_;
// 存放数据
shared_ptr diff_;
//存放梯度
vector shape_;
//存放形状
int count_;
//数据个数
int capacity_;
//数据容量DISABLE_COPY_AND_ASSIGN(Blob);
};
// class Blob}// namespace caffe#endif// CAFFE_BLOB_HPP_
推荐阅读
- Android事件传递源码分析
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- Java内存泄漏分析系列之二(jstack生成的Thread|Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析)
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Android系统启动之init.rc文件解析过程
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- 小程序有哪些低成本获客手段——案例解析
- Vue源码分析—响应式原理(二)
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)