Caffe學習(十)protobuf及caffe.proto解析
本文轉自:https://blog.csdn.net/u012177034/article/details/53944901
使用protobuf的原由
一個好的軟體框架應該要有明確的輸入和輸出,對於CNN網路而言,其主要有兩部分組成:網路具體結構和網路的具體優化演算法及引數。對於框架的使用者而言,使用者只需輸入兩個描述檔案即可得到對該網路的優化結果,這無疑是非常方便的。
caffe框架選擇使用谷歌的開源protobuf工具對這兩部分進行描述,解析和儲存,這一部分為caffe的實現節省了大量的程式碼。
如前面講述的目標檢測demo,py-faster-rcnn,其主要分為訓練和測試兩個過程,兩個過程的核心檔案都是prototxt格式的文字檔案。
如訓練過程
輸入:
(1)slover.prototxt。描述網路訓練時的各種引數檔案,如訓練的策略,學習率的變化率,模型儲存的頻率等引數
(2)train.prototxt。描述訓練網路的網路結構檔案。
(3)test.prototxt。描述測試網路的網路結構檔案。
輸出:
VGG16.caffemodel:儲存的訓練好的網路引數檔案。
protobuf的使用流程
protobuf工具主要是資料序列化儲存和解析。在實際使用的時候主要是作為一個程式碼自動生成工具來使用,通過生成對所定義的資料結構的標準讀寫程式碼,使用者可以通過標準的讀寫介面從檔案中進行資料的讀取,解析和儲存。
目前proto支援C++,python,java等語言,這裡主要演示caffe中使用的C++呼叫。
主要使用過程為:
(1)編寫XXX.proto檔案。該檔案裡主要定義了各種資料結構及對應的資料型別,如int,string等。
(2)使用protoc對XXX.proto檔案進行編譯,生成對應的資料結構檔案的讀取和寫入程式,程式介面都是標準化的。生成的檔案一般名為XXX.pb.cc和XXX.pb.h。
(3)在新程式中使用XXX.pb.c和XXX.pb.h提供的程式碼。
簡易caffe.proto編寫解析示例
為了後面更加清楚的理解protobuf工具,這裡一個簡單的caffe.proto為例進行solver.prototxt和train.prototxt的解析
caffe.proto檔案編寫:
syntax = "proto2"; package caffe;//c++ namespace message NetParameter { optional string name = 1; // consider giving the network a name repeated LayerParameter layer = 2; // ID 100 so layers are printed last. } message SolverParameter { optional string train_net = 1; optional float base_lr = 2; optional string lr_policy = 3; optional NetParameter net_param = 4; } message ParamSpec { optional string name = 1; optional float lr_mult = 3 [default = 1.0]; optional float decay_mult = 4 [default = 1.0]; } // LayerParameter next available layer-specific ID: 147 (last added: recurrent_param) message LayerParameter { optional string name = 1; // the layer name optional string type = 2; // the layer type repeated string bottom = 3; // the name of each bottom blob repeated string top = 4; // the name of each top blob repeated ParamSpec param = 6; // Layer type-specific parameters. optional ConvolutionParameter convolution_param = 106; optional PythonParameter python_param = 130; } message ConvolutionParameter { optional uint32 num_output = 1; // The number of outputs for the layer // Pad, kernel size, and stride are all given as a single value for equal // dimensions in all spatial dimensions, or once per spatial dimension. repeated uint32 pad = 3; // The padding size; defaults to 0 repeated uint32 kernel_size = 4; // The kernel size repeated uint32 stride = 6; // The stride; defaults to 1 } message PythonParameter { optional string module = 1; optional string layer = 2; // This value is set to the attribute `param_str` of the `PythonLayer` object // in Python before calling the `setup()` method. This could be a number, // string, dictionary in Python dict format, JSON, etc. You may parse this // string in `setup` method and use it in `forward` and `backward`. optional string param_str = 3 [default = '']; }
編譯生成caffe.pb.cc與caffe.pb.h檔案
protoc caffe.proto --cpp_out=.//在當前目錄生成cpp檔案及標頭檔案
編寫測試檔案main.cpp
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include "caffe.pb.h"
using namespace caffe;
using namespace std;
using google::protobuf::io::FileInputStream;
using google::protobuf::Message;
bool ReadProtoFromTextFile(const char* filename, Message* proto) {
int fd = open(filename, O_RDONLY);
FileInputStream* input = new FileInputStream(fd);
bool success = google::protobuf::TextFormat::Parse(input, proto);
delete input;
close(fd);
return success;
}
int main()
{
SolverParameter SGD;
if(!ReadProtoFromTextFile("solver.prototxt", &SGD))
{
cout<<"error opening file"<<endl;
return -1;
}
cout<<"hello,world"<<endl;
cout<<SGD.train_net()<<endl;
cout<<SGD.base_lr()<<endl;
cout<<SGD.lr_policy()<<endl;
NetParameter VGG16;
if(!ReadProtoFromTextFile("train.prototxt", &VGG16))
{
cout<<"error opening file"<<endl;
return -1;
}
cout<<VGG16.name()<<endl;
return 0;
}
編寫solver與train網路描述檔案
solver.prototxt內容
train_net: "/home/bryant/cuda-test/train.prototxt"
base_lr: 0.001
lr_policy: "step"
train.prototxt內容:
name: "VGG_ILSVRC_16_layers"
layer {
name: 'input-data'
type: 'Python'
top: 'data'
top: 'im_info'
top: 'gt_boxes'
python_param {
module: 'roi_data_layer.layer'
layer: 'RoIDataLayer'
param_str: "'num_classes': 2"
}
}
layer {
name: "conv1_1"
type: "Convolution"
bottom: "data"
top: "conv1_1"
param {
lr_mult: 0
decay_mult: 0
}
param {
lr_mult: 0
decay_mult: 0
}
convolution_param {
num_output: 64
pad: 1
kernel_size: 3
}
}
編譯連結,生成main
g++ caffe.pb.cc main.cpp -o main -lprotobuf
執行結果
[email protected]:~/cuda-test/src$ ./main
hello,world
/home/bryant/cuda-test/train.prototxt
0.001
step
VGG_ILSVRC_16_layers
[email protected]:~/cuda-test/src$