生活随笔
收集整理的這篇文章主要介紹了
Caffe Blob Dtype理解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
http://blog.luoyetx.com/2015/10/reading-caffe-2/
關于Blob:
Blob 在 Caffe 中扮演了重要的角色,用于存儲數據和網絡參數,同時也在 CPU 和 GPU 之間做了數據同步。Blob 原本在 Caffe 中被表示為一個 4 維數組 (num x channel x height x width),現在可以表示多維數組,最高維數由宏?kMaxBlobAxes?確定,目前 blob.hpp 中設置了?const int kMaxBlobAxes = 32;。Blob 類的代碼主要集中在 blob.hpp 和 blob.cpp 中。
正常的類別和數據在聲明時候,都沒有<Dtype>這種東西,這里為了配合template <typename Dtype> 才搞出了
class LossLayer: public Layer 變成了class LossLayer: public Layer<Dtype>?
const vector<Blob*>& top 變成了?const vector<Blob<Dtype>*>& top
const vector<Blob<Dtype>*>& top 說明 top是指向一系列Blob指針的集合的一個總指針。
數據與相關操作函數
Blob 類主要包括如下成員
| 1
2
3
4
5
6
| shared_ptr<SyncedMemory> data_;
shared_ptr<SyncedMemory> diff_;
shared_ptr<SyncedMemory> shape_data_;
vector<int> shape_;
int count_;
int capacity_;
|
其中 SyncedMemory 主要用來實現數據在 CPU 和 GPU 上的管理。同時 Blob 類提供一組函數來操作這些數據。
| 1
2
3
4
5
6
7
8
9
10
| const Dtype* cpu_data() const;
void set_cpu_data(Dtype* data);
const int* gpu_shape() 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();
|
我們可以通過這些函數拿到 Blob 內部的數據包括修改 Blob 的內部數據。其中的 Dtype 是泛型類型,在定義 Blob 變量時設置的,一般為 float 或者 double。
Blob 類在內部所存儲的數據是一塊連續的內存,為了表示多維數組,shape_ 和 shapedata?記錄了每一維的大小,這樣就能夠很輕松地從給出的坐標中計算出 offset 從而得到那個點的數據。由于 Blob 主要還是用來表示 4 維數組 (最初就是這樣的),Blob 類中仍使用了?int num(); int channels(); int height(); int width();?這些函數,其實 num 等價于 shape()[0],channels 等價于 shape()[1],height 等價于 shape()[2],width 等價于 shape()[3]。計算 offset 時可以使用這四個數字或者直接給出坐標。
| 1
2
| int offset(const int n, const int c = 0, const int h = 0, const int w = 0);
int offset(const vector<int>& indices);
|
有了 Blob 提供的這組函數和上一組函數,我們就可以輕易地操作 Blob 內部的數據了。
動態多維數組
Blob 類可以動態改變數組的尺寸,當拓展數組導致原有內存空間不足以存放下數據時 (count?> capacity),就會重新分配內存。Blob 提供了一組 Reshape 函數來完成這個功能。
| 1
2
3
4
| 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);
|
Blob 類在初始化時并沒有分配內存,也是通過調用 Reshape 來分配內存的。
| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| template <typename Dtype>
void Blob<Dtype>::Reshape(const vector<int>& shape) {
CHECK_LE(shape.size(), kMaxBlobAxes); // 檢查維數
count_ = 1; // 用于計算新的多維數組的大小
shape_.resize(shape.size()); // 更新維數
if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
// shape_data_ 未初始化或者內存太小
shape_data_.reset(new SyncedMemory(shape.size() * sizeof(int)));
}
int* shape_data = static_cast<int*>(shape_data_->mutable_cpu_data());
for (int i = 0; i < shape.size(); ++i) {
CHECK_GE(shape[i], 0);
CHECK_LE(shape[i], INT_MAX / count_) << "blob size exceeds INT_MAX";
count_ *= shape[i];
shape_[i] = shape[i];
shape_data[i] = shape[i];
}
if (count_ > capacity_) {
// 內存不夠
capacity_ = count_;
data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
}
}
|
SyncedMemory
Blob 事實上是對 SyncedMemory 的封裝。SyncedMemory 完成了對內存的實際操作,包括數據在 CPU 和 GPU 上的同步。
| 1
2
3
4
5
6
7
8
9
10
| enum SyncedHead { UNINITIALIZED, HEAD_AT_CPU, HEAD_AT_GPU, SYNCED };
void* cpu_ptr_;
void* gpu_ptr_;
size_t size_;
SyncedHead head_;
bool own_cpu_data_;
bool cpu_malloc_use_cuda_;
bool own_gpu_data_;
int gpu_device_;
|
SyncedMemory 內部存放了兩份數據,分別位于 CPU 和 GPU 上,用 cpu_ptr 和 gpu_ptr 表示。同時 SyncedMemory 也給出了一組函數來獲取和設置實際數據。
| 1
2
3
4
5
6
| const void* cpu_data();
void set_cpu_data(void* data);
const void* gpu_data();
void set_gpu_data(void* data);
void* mutable_cpu_data();
void* mutable_gpu_data();
|
head_ 表示了數據的同步狀態,通過調用?to_cpu()?和?to_gpu()?來做同步。如果 head_ = UNINITIALIZED 則分配相應的內存。
| 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
| inline void SyncedMemory::to_cpu() {
switch (head_) {
case UNINITIALIZED:
CaffeMallocHost(&cpu_ptr_, size_, &cpu_malloc_use_cuda_);
caffe_memset(size_, 0, cpu_ptr_);
head_ = HEAD_AT_CPU;
own_cpu_data_ = true;
break;
case HEAD_AT_GPU:
#ifndef CPU_ONLY
if (cpu_ptr_ == NULL) {
CaffeMallocHost(&cpu_ptr_, size_, &cpu_malloc_use_cuda_);
own_cpu_data_ = true;
}
caffe_gpu_memcpy(size_, gpu_ptr_, cpu_ptr_);
head_ = SYNCED;
#else
NO_GPU;
#endif
break;
case HEAD_AT_CPU:
case SYNCED:
break;
}
}
inline void SyncedMemory::to_gpu() {
#ifndef CPU_ONLY
switch (head_) {
case UNINITIALIZED:
CUDA_CHECK(cudaGetDevice(&gpu_device_));
CUDA_CHECK(cudaMalloc(&gpu_ptr_, size_));
caffe_gpu_memset(size_, 0, gpu_ptr_);
head_ = HEAD_AT_GPU;
own_gpu_data_ = true;
break;
case HEAD_AT_CPU:
if (gpu_ptr_ == NULL) {
CUDA_CHECK(cudaGetDevice(&gpu_device_));
CUDA_CHECK(cudaMalloc(&gpu_ptr_, size_));
own_gpu_data_ = true;
}
caffe_gpu_memcpy(size_, cpu_ptr_, gpu_ptr_);
head_ = SYNCED;
break;
case HEAD_AT_GPU:
case SYNCED:
break;
}
#else
NO_GPU;
#endif
}
|
數據序列化
Blob 數據可以通過 Protobuf 來做相應的序列化操作,ToProto?和?FromProto?完成相應的序列化操作。
| 1
2
3
4
5
6
7
8
9
10
11
12
13
| message BlobProto {
optional BlobShape shape = 7;
repeated float data = 5 [packed = true];
repeated float diff = 6 [packed = true];
repeated double double_data = 8 [packed = true];
repeated double double_diff = 9 [packed = true];
optional int32 num = 1 [default = 0];
optional int32 channels = 2 [default = 0];
optional int32 height = 3 [default = 0];
optional int32 width = 4 [default = 0];
}
|
小結
Caffe 通過 SyncedMemory 和 Blob 封裝了底層數據,為 Caffe 框架上的其他組件提供最基礎的數據抽象,后面的 Layer 參數,Net 參數以及 Solver 的參數等都是 Blob 數據,所以理解 Blob 抽象和管理數據的實現方式有助于后續 Caffe 源碼的閱讀,也是閱讀 Caffe 源碼的第一步。
總結
以上是生活随笔為你收集整理的Caffe Blob Dtype理解的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。