caffe-Blob-(1)

先看读lob.hpp, 再读Blob.cpp。

若要调用caffe文件,可以使用g++命令,也可以使用cmake建立一个项目:

1
2
3
4
5
.
└── test_blob
├── build
├── CMakeLists.txt
└── main.cpp

这个项目可以位于任何位置。接着编辑CMakeLists.tx,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cmake_minimum_required(VERSION 3.5)
project(test_blob)

set(Caffe_INCLUDE_DIRS ~/caffe-master/include \
/usr/local/cuda/include ~/caffe-master/build/src)

set(Caffe_LIBRARIES caffe boost_system glog)
set(CMAKE_CXX_STANDARD 11)

include_directories(${Caffe_INCLUDE_DIRS})
link_directories(~/caffe-master/build/lib)

add_executable(test_blob main.cpp)
target_link_libraries(test_blob ${Caffe_LIBRARIES})

install(TARGETS test_blob RUNTIME DESTINATION bin)

使用下面命令编译执行:

1
2
3
4
5
$ rm -rf build/*
$ cd build
$ cmake ..
$ make
$ ./test_blob

在代码文件头部添加基本内容:

1
2
3
4
5
6
#include <iostream>
#include <vector>
#include <caffe/blob.hpp>

using namespace caffe; // 代码中依旧加上这个namespace
using namespace std;

之后就可以在main函数中,进行探索测试了。

基本地,Blob在内存中是4为数组,维度从高到低为(num,channels,height,width)。并包含data(待学习参数)和diff(增量)。包含一些基本操作。

帮助函数

先在main.cpp外自定义了两个函数:向Blob中写入数据,从Blob读取数据:

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
// 向Blob中 写入数据
void writeInto(caffe::Blob<float>& a){
// 获取cpu数据data部分
float* ptr = a.mutable_cpu_data();
// 给每个位置赋值。count()表示元素个数
for(int i=0;i<a.count();i++){
ptr[i] = i;
}
}
// 打印blob信息 读取数据
void printBlob(const caffe::Blob<float>& blob){

// .shape_string() 返回一个string,包含shape和元素个数
cout<<"Size: "<<blob.shape_string()<<endl;

if (blob.shape_string() == "(0)") {
cout<<"No data initialized"<<endl;
return;
}
// 从内向外打印数据
for(int u=0; u<blob.num(); u++){
for(int v=0; v<blob.channels(); v++){
for(int w=0; w<blob.height(); w++){
for(int x=0; x<blob.width(); x++){
// .data_at(,,,): 访问某个位置数据
cout<<"blob: "<<u<<v<<w<<x<<"->"<<blob.data_at(u,v,w,x)<<endl;
}
}
}
}
// 打印l1范数(绝对值之和) l2范数(平方和)
cout<<"ASUM = "<<blob.asum_data()<<endl;
cout<<"SUMQ = "<<blob.sumsq_data()<<endl;
return;
}

定义Blob

Blob.hpp中声明了构造函数,其用法见下例:

如果没有特殊指明,以下代码均在main.cpp中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 默认构造函数
caffe::Blob<float> a;

//指明shape,1*2*3*4=24 共有24个数值,初始值为0
caffe::Blob<float> b(1,2,3,4);

// 传入Shape每个维度为元素的vector
int shape_c[] = {1,3,2,4};
caffe::Blob<float> c(vector<int>(shape_c, shape_c+4));

writeInto(c);

// 将c大小从(1,3,2,4)变为(1,2,3,4)
c.Reshape(1,1,1,1);
printBlob(c);

// 将c变为与相同大小
c.ReshapeLike(b);
printBlob(c);

如果只打印最后一次数的c:

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
Size: 1 2 3 4 (24)
Position: 0000-> value: 0
Position: 0001-> value: 1
Position: 0002-> value: 2
Position: 0003-> value: 3
Position: 0010-> value: 4
Position: 0011-> value: 5
Position: 0012-> value: 6
Position: 0013-> value: 7
Position: 0020-> value: 8
Position: 0021-> value: 9
Position: 0022-> value: 10
Position: 0023-> value: 11
Position: 0100-> value: 12
Position: 0101-> value: 13
Position: 0102-> value: 14
Position: 0103-> value: 15
Position: 0110-> value: 16
Position: 0111-> value: 17
Position: 0112-> value: 18
Position: 0113-> value: 19
Position: 0120-> value: 20
Position: 0121-> value: 21
Position: 0122-> value: 22
Position: 0123-> value: 23
ASUM = 276
SUMQ = 4324

从上可以明显看出,(num,channels,height,width)最右端变化最快,逐渐向左。

更新参数data

更新参数由Blob的Update()成员函数实现。具体是data=data-diff。所以要测试Updata(),首先就需要向data和diff分别读入数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 定义blob
caffe::Blob<float> a;
a.Reshape(1,2,3,4);

// 获取cpu数据data部分 和diff部分
float* dataPtr = a.mutable_cpu_data();
float* diffPtr = a.mutable_cpu_diff();

// 分别为data和diff写入值
for(int i=0;i<a.count();i++){
dataPtr[i] = i;
diffPtr[i] = a.count()-1-i;
}

// 执行更新操作
a.Update();

// 打印更新后的结果
printBlob(a);

更新后的data(待学习参数)。

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
Size: 1 2 3 4 (24)
Position: 0000-> value: -23
Position: 0001-> value: -21
Position: 0002-> value: -19
Position: 0003-> value: -17
Position: 0010-> value: -15
Position: 0011-> value: -13
Position: 0012-> value: -11
Position: 0013-> value: -9
Position: 0020-> value: -7
Position: 0021-> value: -5
Position: 0022-> value: -3
Position: 0023-> value: -1
Position: 0100-> value: 1
Position: 0101-> value: 3
Position: 0102-> value: 5
Position: 0103-> value: 7
Position: 0110-> value: 9
Position: 0111-> value: 11
Position: 0112-> value: 13
Position: 0113-> value: 15
Position: 0120-> value: 17
Position: 0121-> value: 19
Position: 0122-> value: 21
Position: 0123-> value: 23
ASUM = 288
SUMQ = 4600

由结果可看出,其实计算的是data=data-diff模型就是正向传播求diff,反向传播更新data

保存Blob数据到磁盘,或从磁盘再如数据到Blob

从Blob写入protobuff:.ToProto();从protobuff写入Blob:.FromProto

需要添加头文件#include <caffe/util/io.hpp>从而调用函数WriteProtoToBinaryFile()ReadProtoFromBinaryFileOrDie()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//g++编译时加上 -lglog -lboost_system

// 实例化一个BlobProto对象
caffe::BlobProto bp;
// 将a序列化,包括diff,写入protobuff
a.ToProto(&bp, true);
// 将bp对象写入磁盘文件“a.blob”
WriteProtoToBinaryFile(bp, "a.blob");


caffe::BlobProto bp2;
// 将磁盘文件“a.blob”内容读入protobuff对象bp2
ReadProtoFromBinaryFileOrDie("a.blob", &bp2);
caffe::Blob<float> b;
// 将protobuff中内容 写入b
b.FromProto(bp2, true);

printBlob(b);

上述过程从读取Blob,写入磁盘,后从磁盘读取文件,写入里一个Blob。这对于加载,保存模型参数(权值)很实用。


接下来从caffe.proto文件,读关于Blob的描述。