Caffe代码导读(1):Protobuf例子

Protobuf是一种可以实现内存与外存交换的协议接口。这是由谷歌开发的开源工具,目前研究Caffe源码时用到。

一个软件项目 = 数据结构 + 算法 + 参数,对于数据结构和算法我们都已经有较多研究,但不同开发者对参数管理却各有千秋。有人喜欢TXT格式化的参数文件,有人喜欢BIN简单高效,也有人喜欢图形化界面的直观。不一致的参数管理带来很多问题,例如一个项目组内不同成员必须约定一套统一的参数方案,或者称为通信协议,这样便于模块集成。而Protobuf工具就完美解决了这个问题,关键部分代码自动生成,节省了大量的开发、调试时间。


首先下载protobuf,地址(打不开?……不解释)

这里用Linux版本2.5.0

解压:

tar zxvf protobuf-2.5.0.tar.gz

切到主目录:

cd protobuf-2.5.0

编译:

./configure

make

sudo make install


添加环境变量:

export PKG_CONFIG_PATH=$(pwd)


编译examples:

cd examples/

make cpp

这里我们只编译C++代码。


编译完成,生成了以下可执行文件:

add_person_cpp

list_people_cpp

这是个通讯录的例子。我们首先运行add_person_cpp:

[plain]  view plain  copy
  1. ./add_person_cpp zyk  
  2. zyk: File not found.  Creating a new file.  
  3. Enter person ID number: 123  
  4. Enter name: zhaoyongke  
  5. Enter email address (blank for none): [email protected]  
  6. Enter a phone number (or leave blank to finish): 188188188  
  7. Is this a mobile, home, or work phone?(回车)  
  8. Unknown phone type.  Using default.  
  9. Enter a phone number (or leave blank to finish):(回车)  

然后运行list_people_cpp:

[plain]  view plain  copy
  1. ./list_people_cpp zyk  
  2. Person ID: 123  
  3.   Name: zhaoyongke  
  4.   E-mail address: [email protected]  
  5.   Home phone #: 188188188  

可见我们生成了新的通讯录zyk,里面保存了相应的信息。


例子运行结束了,我们看下代码是如何生成的。

protobuf使用前,先编写proto文件,这是描述我们需要配置参数的数据结构。这个例子里面的proto如下:

[plain]  view plain  copy
  1. // See README.txt for information and build instructions.  
  2.   
  3. package tutorial;  
  4.   
  5. option java_package = "com.example.tutorial";  
  6. option java_outer_classname = "AddressBookProtos";  
  7.   
  8. message Person {  
  9.   required string name = 1;  
  10.   required int32 id = 2;        // Unique ID number for this person.  
  11.   optional string email = 3;  
  12.   
  13.   enum PhoneType {  
  14.     MOBILE = 0;  
  15.     HOME = 1;  
  16.     WORK = 2;  
  17.   }  
  18.   
  19.   message PhoneNumber {  
  20.     required string number = 1;  
  21.     optional PhoneType type = 2 [default = HOME];  
  22.   }  
  23.   
  24.   repeated PhoneNumber phone = 4;  
  25. }  
  26.   
  27. // Our address book file is just one of these.  
  28. message AddressBook {  
  29.   repeated Person person = 1;  
  30. }  

前几行是定义包的,可以忽略。

message Person{...}定义了一个需要传输的参数结构体,可见包括这么几个单元:name(string类型)、id(int32类型)、email(string类型)、phone(PhoneNumber类型,嵌套在Person内的类)。前面标记为“required”是必须有值的,而“optional“则为可选项,”repeated“表示后面单元为相同类型的一组向量。


有了如上定义,我们可以用protobuf工具生成接口代码,命令如下:

[plain]  view plain  copy
  1. protoc --cpp_out=.  addressbook.proto  

运行后生成了两个文件:addressbook.pb.cc 和addressbook.pb.h,代码比较长就不贴了。我们的应用程序可以通过自动生成的接口实现参数的序列化/反序列化,代码如下:

[cpp]  view plain  copy
  1. //add_person.c  
  2. #include <iostream>  
  3. #include <fstream>  
  4. #include <string>  
  5. #include "addressbook.pb.h"  
  6. using namespace std;  
  7.   
  8. // This function fills in a Person message based on user input.  
  9. void PromptForAddress(tutorial::Person* person) {  
  10.   cout << "Enter person ID number: ";  
  11.   int id;  
  12.   cin >> id;  
  13.   person->set_id(id);  
  14.   cin.ignore(256, '\n');  
  15.   
  16.   cout << "Enter name: ";  
  17.   getline(cin, *person->mutable_name());  
  18.   
  19.   cout << "Enter email address (blank for none): ";  
  20.   string email;  
  21.   getline(cin, email);  
  22.   if (!email.empty()) {  
  23.     person->set_email(email);  
  24.   }  
  25.   
  26.   while (true) {  
  27.     cout << "Enter a phone number (or leave blank to finish): ";  
  28.     string number;  
  29.     getline(cin, number);  
  30.     if (number.empty()) {  
  31.       break;  
  32.     }  
  33.   
  34.     tutorial::Person::PhoneNumber* phone_number = person->add_phone();  
  35.     phone_number->set_number(number);  
  36.   
  37.     cout << "Is this a mobile, home, or work phone? ";  
  38.     string type;  
  39.     getline(cin, type);  
  40.     if (type == "mobile") {  
  41.       phone_number->set_type(tutorial::Person::MOBILE);  
  42.     } else if (type == "home") {  
  43.       phone_number->set_type(tutorial::Person::HOME);  
  44.     } else if (type == "work") {  
  45.       phone_number->set_type(tutorial::Person::WORK);  
  46.     } else {  
  47.       cout << "Unknown phone type.  Using default." << endl;  
  48.     }  
  49.   }  
  50. }  
  51. // Main function:  Reads the entire address book from a file,  
  52. //   adds one person based on user input, then writes it back out to the same  
  53. //   file.  
  54. int main(int argc, char* argv[]) {  
  55.   // Verify that the version of the library that we linked against is  
  56.   // compatible with the version of the headers we compiled against.  
  57.   GOOGLE_PROTOBUF_VERIFY_VERSION;  
  58.   
  59.   
  60.   if (argc != 2) {  
  61.     cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;  
  62.     return -1;  
  63.   }  
  64.   
  65.   
  66.   tutorial::AddressBook address_book;  
  67.   
  68.   
  69.   {  
  70.     // Read the existing address book.  
  71.     fstream input(argv[1], ios::in | ios::binary);  
  72.     if (!input) {  
  73.       cout << argv[1] << ": File not found.  Creating a new file." << endl;  
  74.     } else if (!address_book.ParseFromIstream(&input)) {  
  75.       cerr << "Failed to parse address book." << endl;  
  76.       return -1;  
  77.     }  
  78.   }  
  79.   
  80.   
  81.   // Add an address.  
  82.   PromptForAddress(address_book.add_person());  
  83.   
  84.   
  85.   {  
  86.     // Write the new address book back to disk.  
  87.     fstream output(argv[1], ios::out | ios::trunc | ios::binary);  
  88.     if (!address_book.SerializeToOstream(&output)) {  
  89.       cerr << "Failed to write address book." << endl;  
  90.       return -1;  
  91.     }  
  92.   }  
  93.   
  94.   
  95.   // Optional:  Delete all global objects allocated by libprotobuf.  
  96.   google::protobuf::ShutdownProtobufLibrary();  
  97.   
  98.   
  99.   return 0;  
  100. }  

可见只需要调用addressbook.pb.h中声明的tutorial::AddressBook类、Person类中的接口(add_person(), add_phone(), set_number(), set_email()等)就能操作相应的参数,最后将内存中的参数序列化为文件只需要执行SerializeToOstream()。相应的读取参数文件的操作为ParseFromIstream()。这里贴出例子中的第二个程序如下:

[cpp]  view plain  copy
  1. // list_people.c  
  2.   
  3. #include <iostream>  
  4. #include <fstream>  
  5. #include <string>  
  6. #include "addressbook.pb.h"  
  7. using namespace std;  
  8.   
  9. // Iterates though all people in the AddressBook and prints info about them.  
  10. void ListPeople(const tutorial::AddressBook& address_book) {  
  11.   for (int i = 0; i < address_book.person_size(); i++) {  
  12.     const tutorial::Person& person = address_book.person(i);  
  13.   
  14.     cout << "Person ID: " << person.id() << endl;  
  15.     cout << "  Name: " << person.name() << endl;  
  16.     if (person.has_email()) {  
  17.       cout << "  E-mail address: " << person.email() << endl;  
  18.     }  
  19.   
  20.     for (int j = 0; j < person.phone_size(); j++) {  
  21.       const tutorial::Person::PhoneNumber& phone_number = person.phone(j);  
  22.   
  23.       switch (phone_number.type()) {  
  24.         case tutorial::Person::MOBILE:  
  25.           cout << "  Mobile phone #: ";  
  26.           break;  
  27.         case tutorial::Person::HOME:  
  28.           cout << "  Home phone #: ";  
  29.           break;  
  30.         case tutorial::Person::WORK:  
  31.           cout << "  Work phone #: ";  
  32.           break;  
  33.       }  
  34.       cout << phone_number.number() << endl;  
  35.     }  
  36.   }  
  37. }  
  38.   
  39. // Main function:  Reads the entire address book from a file and prints all  
  40. //   the information inside.  
  41. int main(int argc, char* argv[]) {  
  42.   // Verify that the version of the library that we linked against is  
  43.   // compatible with the version of the headers we compiled against.  
  44.   GOOGLE_PROTOBUF_VERIFY_VERSION;  
  45.   
  46.   if (argc != 2) {  
  47.     cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;  
  48.     return -1;  
  49.   }  
  50.   
  51.   tutorial::AddressBook address_book;  
  52.   
  53.   {  
  54.     // Read the existing address book.  
  55.     fstream input(argv[1], ios::in | ios::binary);  
  56.     if (!address_book.ParseFromIstream(&input)) {  
  57.       cerr << "Failed to parse address book." << endl;  
  58.       return -1;  
  59.     }  
  60.   }  
  61.   
  62.   ListPeople(address_book);  
  63.   
  64.   // Optional:  Delete all global objects allocated by libprotobuf.  
  65.   google::protobuf::ShutdownProtobufLibrary();  
  66.   
  67.   return 0;  
  68. }  

相信做完这个实验,你将不再对Caffe代码中的参数初始化、参数保存操作感到陌生,一切都很自然。

 除了上述简单功能,Protobuf还可以用来传递不同语言(C/C++与Java、Python)之间的参数,省去了自己手动维护数据结构的繁琐工作。也可以支持客户端/服务器模式,在主机/从机之间传递参数。

版权声明:本文为卜居原创文章,未经博主允许不得转载。卜居博客地址:http://blog.csdn.net/kkk584520 https://blog.csdn.net/kkk584520/article/details/41046827
文章标签:  参数传递 数据结构 Protobuf
个人分类:  深度学习框架
所属专栏:  Caffe 源码导读

猜你喜欢

转载自blog.csdn.net/yanhx1204/article/details/80459208