wjmishuai / blog_20160323_1_6323935

来自CSDN博客:caffe源码解析之blob.hpp或blob.cpp http://blog.csdn.net/wjmishuai/article/details/50961471#

  深度学习 caffe 源码 最后更新时间 2016-03-23 11:19:54
blog_20160323_1_6323935 225行 C++
Raw
   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
#ifndef CAFFE_BLOB_HPP_//防止头文件重复引用
#define CAFFE_BLOB_HPP_

#include <algorithm>
#include <string>
#include <vector>

/*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 <typename Dtype>
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<int>& 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<int>& 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<int>& 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<int>& 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<Dtype>& 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<int>& index) const {
return cpu_data()[offset(index)];
}
//从cpu访问数据diff
inline Dtype diff_at(const vector<int>& index) const {
return cpu_diff()[offset(index)];
}
//从cpu访问数据data
inline const shared_ptr<SyncedMemory>& data() const {
return data_;
}
//从cpu访问数据diff
inline const shared_ptr<SyncedMemory>& 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<SyncedMemory> data_;// 存放数据
shared_ptr<SyncedMemory> diff_;//存放梯度
vector<int> shape_;//存放形状
int count_;//数据个数
int capacity_;//数据容量

DISABLE_COPY_AND_ASSIGN(Blob);
}; // class Blob

} // namespace caffe

#endif // CAFFE_BLOB_HPP_