A wrapper around SyncedMemory holders serving as the basic computational unit through which Layer's, Net's, and Solver's interact.
其实网上已经有很多源码解读了,但我还是想自己看一遍。
呃,不得不承认,我连C语言的代码都写得不好,C++也从来没有怎么学过。
所以可能我看的方式和大家不太一样,当作自己的学习记录吧。
水平有限,如果您看到了有说得不对的地方,请大方的指出来,相互交流学习,:)。
应该基本可以公认的一点:基本每个类里面所有方法都是对自己的数据进行操作,而自己的数据通常也是私有类型的。
鉴于以上这一点,所以我先直接把代码拉到最后,看blob的数据有哪些:
protected:
shared_ptr<SyncedMemory> data_;
shared_ptr<SyncedMemory> diff_;
int num_;
int channels_;
int height_;
int width_;
int count_;
int capacity_;
看起来Blob最重要的工作也就在于data, diff的操作了。
猜也能够猜到,data应该是前馈时候的数据,diff是反馈时候的数据。channels_, height_, width_这三个数据,如果理解成图像的3个通道应该也是可以的。不过由于在整过网络的传递过程中,可能最开始的1个图片到了例如conv5已经变成了几百个小图片,但是存储的时候应该是全部放在一起的,也就是全部都放在一个Blob里面,为了区分到底这个Blob有多少个图片(或者说成feature_map)也就定义了num_吧。但我们通常做模型训练的时候,都是好多个图片做批量处理的,所以这里又多了变量count_估计也就是在于这个目的吧。最后的capacity_是做什么的呢?这个还真猜不出来(可能是在进行批量处理的过程中检测是否这一批处理完没有,瞎说的哈)。
另外,其实我们可以把一个Blob理解成高维数组,不要局限于图像,那么还可以将这里的Blob用在别的地方吧。
再来看看Blob里面提供了哪些方法呢?既然操作数据的重点是data, diff,那么先来看看与这两个数据直接相关的函数:
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));
}
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 const shared_ptr<SyncedMemory>& data() const {
CHECK(data_);
return data_;
}
inline const shared_ptr<SyncedMemory>& diff() const {
CHECK(diff_);
return diff_;
}
const Dtype* cpu_data() const;
void set_cpu_data(Dtype* data);
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();
因为其中涉及到offset()函数,它也定义在Blob中的:
inline int offset(const int n, const int c = 0, const int h = 0,
const int w = 0) const {
CHECK_GE(n, 0);
CHECK_LE(n, num_);
CHECK_GE(channels_, 0);
CHECK_LE(c, channels_);
CHECK_GE(height_, 0);
CHECK_LE(h, height_);
CHECK_GE(width_, 0);
CHECK_LE(w, width_);
return ((n * channels_ + c) * height_ + h) * width_ + w;
}
offset()也就相当于是计算一个偏置(我这里说的偏置与神经网络里面的权重, 偏置不一个概念哈,你懂的)。
有了offset,前面的函数理解起来就轻松多了,data_at(n, c, h, w)也就是访问在cpu_data的第offset(n, c, h, w)的数据,diff_at类似。
很奇怪,为什么访问的是cpu_data? 不是gpu_data,也不是data!暂时我也不理解。
接着的两个函数shared_ptr开头的,大概也就是分享data和diff数据吧。注意,这里是直接返回data和diff数据的,没有cpu, gpu之分。
再接下来的几个函数,得去看看Blob.cpp了。
关于Blob里面其它的函数,感觉对后面的阅读没有太大用的样子,例如:
Blob()
: data_(), diff_(), num_(0), channels_(0), height_(0), width_(0),
count_(0), capacity_(0) {}
explicit Blob(const int num, const int channels, const int height,
const int width);
void Reshape(const int num, const int channels, const int height,
const int width);
void ReshapeLike(const Blob& other);
inline int num() const { return num_; }
inline int channels() const { return channels_; }
inline int height() const { return height_; }
inline int width() const { return width_; }
inline int count() const { return count_; }
构造函数,改变尺寸的函数,返回通道数,高宽等等。
void CopyFrom(const Blob<Dtype>& source, bool copy_diff = false,
bool reshape = false);
void Update();
void FromProto(const BlobProto& proto);
void ToProto(BlobProto* proto, bool write_diff = false) const;
/// @brief Compute the sum of absolute values (L1 norm) of the data.
Dtype asum_data() const;
/// @brief Compute the sum of absolute values (L1 norm) of the diff.
Dtype asum_diff() const;
/**
* @brief Set the data_ shared_ptr to point to the SyncedMemory holding the
* data_ of Blob other -- useful in Layer&s which simply perform a copy
* in their Forward pass.
*
* This deallocates the SyncedMemory holding this Blob's data_, as
* shared_ptr calls its destructor when reset with the "=" operator.
*/
void ShareData(const Blob& other);
/**
* @brief Set the diff_ shared_ptr to point to the SyncedMemory holding the
* diff_ of Blob other -- useful in Layer&s which simply perform a copy
* in their Forward pass.
*
* This deallocates the SyncedMemory holding this Blob's diff_, as
* shared_ptr calls its destructor when reset with the "=" operator.
*/
void ShareDiff(const Blob& other);
这几个函数,Copy的好理解,Update()?得看看Blob.cpp,FromProto,ToProto可以不用太关心,与google的protobuffer有关系,对阅读整个caffe应该不会有什么影响吧,就理解成一个拿数据出来,一个将数据放进去。
再后面的4个函数,注释得很详细。
asum_data()计算数据的一介范数,asum_diff()计算误差的一介范数。
Share_Data()的实现也只是简单的将参数中的数据指针赋值给自己而已,Share_Diff()一样。
template <typename Dtype>
void Blob<Dtype>::ShareData(const Blob& other) {
CHECK_EQ(count_, other.count());
data_ = other.data();
}
template <typename Dtype>
void Blob<Dtype>::ShareDiff(const Blob& other) {
CHECK_EQ(count_, other.count());
diff_ = other.diff();
}