跟着m哥调了了一段时间代码,断断续续地初步用了一下caffe,但是始终有点若即若离的感觉,偶然看到了知乎的一个回答 https://www.zhihu.com/question/27982282/answer/80242005 ,决定开始系统地看一下caffe的源码,在此稍作整理备忘及供有缘人参考。今后的几篇源码系列的文章大概以这篇文章为框架陆续写下去。
我们知道caffe中的网络和优化器都是以proto协议的文件储存的。简单来说proto可看作用于数据收发的、一种优化版的xml。官网见此处,科普见此处。
Play with proto
首先从 https://www.cnblogs.com/gongxijun/p/7010641.html 中的一个例子开始吧。
编写caffe.proto文件,这个文件相当于制定了一种传输数据的协议,供发送端和接收端使用:
//caffe.proto
syntax = "proto2"; //指定协议proto2或proto3
package caffe;
message student
{
required int32 age = 1; //ID required 表示必要字段
required string name = 2; //str 必要字段
optional int32 grade = 3; //optional field 可选字段,可以有无,最多b
}
将proto转为cpp代码供收发端调用:
protoc -I=. --cpp_out=. ./caffe.proto
运行后可以看到生成caffe.pb.h和caffe.pb.cc编写读写文件caffeRead.cpp:
//caffeRead.cpp
#include"caffe.pb.h"
#include<iostream>
#include<ios>
using namespace std;
void InfoStudents(const caffe::student & stu){
cout<< "student info:"<<endl;
cout<<"name: "<<stu.name()<<endl;
cout<<"age: "<<stu.age()<<endl;
cout<<"grade: "<<stu.grade()<<endl;
}
int main(void)
{
caffe::student stu;
stu.set_age(18);
stu.set_name("gongxijun");
stu.set_grade(146);
InfoStudents(stu);
return 0;
}
编译cc文件并执行:
g++ caffeRead.cpp -o caffeReader caffe.pb.cc -I /usr/local/protobuf/include -L /usr/local/protobuf/lib -lprotobuf -pthread
看到输出:
student info:
name: gongxijun
age: 18
grade: 146
如果加入socket等收发机制,将写过程和读过程分离,则可以实现设备间的通信。
proto语法
同样基于以上的例子,首先syntax指定了语法标准,caffe是基于proto2的,后面我们默认以proto2为准。根据这里,package的作用是指定class的namespace,防止class名的冲突。后面每个message相当于一个class,proto文件里面可以同时有多个message(但是官方不推荐包含太多,因为会造成dependency bloat)。上面的student message中有3个fields(key-value对),每个field有rule、type、name和unique number。
- rule有3中形式,required表示每条打包后的信息(message实例)必须包含1个此类field,0/1个optional,若干个(包括0)repeated。
- type是一些可以和c++(或其他调用接口语言)中的数据类型相转化的数据类型,如int32等,具体见这里,也可以是某个message的type,或者预定义枚举类型enum中的一个。
- unique number(ID)可选范围是 [ 1 , 2 29 − 1 ] [1, 2^{29}-1] [1,229−1],其中 [ 1 , 15 ] [1,15] [1,15]占用1个字节, [ 16 , 2047 ] [16, 2047] [16,2047]占用2个字节,以此类推。所以应该将常用的field编成短码,用于在二进制数据中作为key,具体的value采用了msb标记扩展字节,官方文档详细介绍了译码方式,好奇心重的读者可以推一下,这部分内容不详细了解也不影响基本使用,所以这里不再赘述了。(doc已经很详细了,但是这篇文章大看一眼也还不错,我没仔细看,仅供参考吧。)
caffe.proto的组成
数据协议
message BlobShape {
message BlobProto {
message BlobProtoVector {
message Datum {
其中V1LayerParameter和V0LayerParameter用于旧版,现在已经弃用,故后文不再赘述。
其他message的一些调用结构如下所示:
message SolverState {
message SolverParameter {
|---message NetParameter {
| \
| message NetState {
| /
|---LayerParameter {
|-message NetStateRule {
|-message ParamSpec {
|-其他用于layer的 message
|-message FillerParameter {(初始化赋值)
具体的message的用途到后面变用边学吧,目前可以参考 https://blog.csdn.net/weixin_39970417/article/details/80825601