生活随笔
收集整理的這篇文章主要介紹了
caffe blob操作
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
http://blog.luoyetx.com/2015/10/reading-caffe-2/
Blob 在 Caffe 中扮演了重要的角色,用于存儲(chǔ)數(shù)據(jù)和網(wǎng)絡(luò)參數(shù),同時(shí)也在 CPU 和 GPU 之間做了數(shù)據(jù)同步。Blob 原本在 Caffe 中被表示為一個(gè) 4 維數(shù)組 (num x channel x height x width),現(xiàn)在可以表示多維數(shù)組,最高維數(shù)由宏?kMaxBlobAxes?確定,目前 blob.hpp 中設(shè)置了?const int kMaxBlobAxes = 32;。Blob 類的代碼主要集中在 blob.hpp 和 blob.cpp 中。
數(shù)據(jù)與相關(guān)操作函數(shù)
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 主要用來(lái)實(shí)現(xiàn)數(shù)據(jù)在 CPU 和 GPU 上的管理。同時(shí) Blob 類提供一組函數(shù)來(lái)操作這些數(shù)據(jù)。
| 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();
|
我們可以通過這些函數(shù)拿到 Blob 內(nèi)部的數(shù)據(jù)包括修改 Blob 的內(nèi)部數(shù)據(jù)。其中的 Dtype 是泛型類型,在定義 Blob 變量時(shí)設(shè)置的,一般為 float 或者 double。
Blob 類在內(nèi)部所存儲(chǔ)的數(shù)據(jù)是一塊連續(xù)的內(nèi)存,為了表示多維數(shù)組,shape_ 和 shapedata?記錄了每一維的大小,這樣就能夠很輕松地從給出的坐標(biāo)中計(jì)算出 offset 從而得到那個(gè)點(diǎn)的數(shù)據(jù)。由于 Blob 主要還是用來(lái)表示 4 維數(shù)組 (最初就是這樣的),Blob 類中仍使用了?int num(); int channels(); int height(); int width();?這些函數(shù),其實(shí) num 等價(jià)于 shape()[0],channels 等價(jià)于 shape()[1],height 等價(jià)于 shape()[2],width 等價(jià)于 shape()[3]。計(jì)算 offset 時(shí)可以使用這四個(gè)數(shù)字或者直接給出坐標(biāo)。
| 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 提供的這組函數(shù)和上一組函數(shù),我們就可以輕易地操作 Blob 內(nèi)部的數(shù)據(jù)了。
動(dòng)態(tài)多維數(shù)組
Blob 類可以動(dòng)態(tài)改變數(shù)組的尺寸,當(dāng)拓展數(shù)組導(dǎo)致原有內(nèi)存空間不足以存放下數(shù)據(jù)時(shí) (count?> capacity),就會(huì)重新分配內(nèi)存。Blob 提供了一組 Reshape 函數(shù)來(lái)完成這個(gè)功能。
| 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 類在初始化時(shí)并沒有分配內(nèi)存,也是通過調(diào)用 Reshape 來(lái)分配內(nèi)存的。
| 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); // 檢查維數(shù)
count_ = 1; // 用于計(jì)算新的多維數(shù)組的大小
shape_.resize(shape.size()); // 更新維數(shù)
if (!shape_data_ || shape_data_->size() < shape.size() * sizeof(int)) {
// shape_data_ 未初始化或者內(nèi)存太小
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_) {
// 內(nèi)存不夠
capacity_ = count_;
data_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
diff_.reset(new SyncedMemory(capacity_ * sizeof(Dtype)));
}
}
|
SyncedMemory
Blob 事實(shí)上是對(duì) SyncedMemory 的封裝。SyncedMemory 完成了對(duì)內(nèi)存的實(shí)際操作,包括數(shù)據(jù)在 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 內(nèi)部存放了兩份數(shù)據(jù),分別位于 CPU 和 GPU 上,用 cpu_ptr 和 gpu_ptr 表示。同時(shí) SyncedMemory 也給出了一組函數(shù)來(lái)獲取和設(shè)置實(shí)際數(shù)據(jù)。
| 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_ 表示了數(shù)據(jù)的同步狀態(tài),通過調(diào)用?to_cpu()?和?to_gpu()?來(lái)做同步。如果 head_ = UNINITIALIZED 則分配相應(yīng)的內(nèi)存。
| 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
}
|
數(shù)據(jù)序列化
Blob 數(shù)據(jù)可以通過 Protobuf 來(lái)做相應(yīng)的序列化操作,ToProto?和?FromProto?完成相應(yīng)的序列化操作。
| 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];
}
|
小結(jié)
Caffe 通過 SyncedMemory 和 Blob 封裝了底層數(shù)據(jù),為 Caffe 框架上的其他組件提供最基礎(chǔ)的數(shù)據(jù)抽象,后面的 Layer 參數(shù),Net 參數(shù)以及 Solver 的參數(shù)等都是 Blob 數(shù)據(jù),所以理解 Blob 抽象和管理數(shù)據(jù)的實(shí)現(xiàn)方式有助于后續(xù) Caffe 源碼的閱讀,也是閱讀 Caffe 源碼的第一步。
總結(jié)
以上是生活随笔為你收集整理的caffe blob操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。