当先锋百科网

首页 1 2 3 4 5 6 7

跟着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。

  1. rule有3中形式,required表示每条打包后的信息(message实例)必须包含1个此类field,0/1个optional,若干个(包括0)repeated。
  2. type是一些可以和c++(或其他调用接口语言)中的数据类型相转化的数据类型,如int32等,具体见这里,也可以是某个message的type,或者预定义枚举类型enum中的一个。
  3. unique number(ID)可选范围是 [ 1 , 2 29 − 1 ] [1, 2^{29}-1] [1,2291],其中 [ 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