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();
}