caffe源碼 數據結構 google protobuf
caffe的數據結構,除了使用blob作為數據塊,大部分的數據結構都用proto文件來定義。
我們為表達網絡結構所寫prototxt文件就是protobuf讀取的文件,從其中,protobuf可以獲取層、參數的設置,反饋NetParameter、LayerParameter等重要初始化信息用于網絡、層的建立和設置。
caffe編譯時,第一個編譯的就是caffe.proto,它是所有文件的基礎。
什么是protobuf,為什么選用它?
Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言數據標準,目前已經正在使用的有超過 48,162 種報文格式定義和超過 12,183 個 .proto 文件。他們用于 RPC 系統和持續數據存儲系統。
Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用于結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Python 三種語言的 API。
Protocol buffers是一種靈活,高效,自動化的方案,用于解決這類問題。有了protocol buffers, 你可以編寫.proto
文件去存儲你想描述的數據結構。Protocol buffers編譯器會創造一個類來將protocol buffer數據自動編碼和解析成高效的二進制格式。生成的類為protocol buffer中的字段提供了getters和setters接口,而且還把讀寫protocal的細節封裝成了一個單元。最重要的,protocol buffer支持擴展格式,隨著時間的推移,這樣的代碼仍然可以用舊的格式讀取數據編碼。
通過使用,我們可以直觀的了解它的作用及用法,使用方法:
1.定義你的protocol format,寫.proto:
寫.proto
文件是十分簡單的:你需要為每個你想序列化的數據結構寫一個消息message,并為每一個在消息里的字段指定名稱和類型。
1.1 *.proto文件中數據類型可以分為兩大類:
復合數據類型 + 標準數據類型
復合數據類型包括:枚舉和message類型
標準數據類型包含:整型,浮點,字符串等
1.2 數據類型前面修飾詞:
①required: 必須賦值,不能為空,否則該條message會被認為是“uninitialized”。build一個“uninitialized” message會拋出一個RuntimeException異常,解析一條“uninitialized” message會拋出一條IOException異常。除此之外,“required”字段跟“optional”字段并無差別。
②optional:字段可以賦值,也可以不賦值。假如沒有賦值的話,會被賦上默認值。對于簡單的類型, 您可以指定自己的默認值, [default=HIT]。否則,會用系統提供的值:數值類型為0,字符類型為空, 布爾類型為false。對于嵌入式消息,默認值總是“默認實例”或“原型”這些消息里沒有設置的字段。調用訪問器來獲得一個可選的(或要求)字段的值(沒有被顯式地設置),總是會返回字段的默認值的.
③repeated: 該字段可以重復任意次數,包括0次。重復數據的順序將會保存在protocol buffer中,將這個字段想象成一個可以自動設置size的數組就可以了,repeated字段將作動態數組。
1.3.每個字段要給數字:
該Number是用來標記該字段在序列化后的二進制數據中所在的field,每個字段的Number在message內部都是獨一無二的。也不能進行改變,否則數據就不能正確的解包。
1.4 數據類型
Protobuf支持的基本數據類型的表
1.5 例子
這里有一個.proto
文件已經定義好你的消息addressbook.proto,引自官方教程:
https://developers.google.com/protocol-buffers/docs/cpptutorial
syntax = "proto2";
package tutorial;//包名稱聲明,預防命名沖突,生成的充當數據結構的類會被放在相應的命名空間中。
message Person {//用message定義一個數據結構
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {//枚舉類型,豐富了數據結構
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {//嵌套數據類型
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phones = 4;//可加多個電話號碼和電話類型
}
message AddressBook {
repeated Person people = 1;//嵌套數據類型
}
消息message: 一個消息只是一些類型字段的集合。許多標準簡單的數據類型可以作為字段的類型,包括bool,int32,float,double和string。你還可以進一步構建你的消息通過使用其他消息類型作為字段類型——在上面的例子中,Person消息包含了PhoneNumber消息,而AddressBook消息也包含了Person消息。你甚至可以定義消息類型嵌套在其他消息中——如你所見,PhoneNumber類型在Person中定義。您還可以定義枚舉類型,如果你想你的一個字段有一個預定義的值列表——在這里你想指定一個電話號碼可以是MOBILE,HOME,WORK。每個元素中的“= 1”,“= 2”標記符是識別使用的二進制編碼字段的獨特的“標簽”。標簽號碼1-15要求的字節會比號碼高的少點,所以你想優化一下,你可以使用這些標簽用于常用的或repeated元素,把標簽16和更高的標簽給那些不常用或optional元素。每一個repeated的元素需要重新編碼標記號,所以repeated字段特別適合優化。
2.編譯Protocol Buffers:
現在你已經有了.proto文件,你需要做的下一件事是生成你需要read and write AddressBook(和后面的Person和PhoneNumber)消息的類。要做到這一點,您需要運行protocol buffer編譯器protoc編譯你的.proto :
現在運行編譯器, 指定源碼目錄(你的應用源碼呆的地方——如果你不提供這個值,默認為現在的目錄),目標目錄(你希望生成代碼的目錄;通常會與源碼目錄相同),和你的.proto
文件的路徑。在這種情況下,你...:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
因為你想用C++的類,所以你要寫--cpp_out
選項——類似的選項對其他受支持的語言也有提供,例如java:--java_out,python:--python_out。
這個會在你指定目標目錄下生成下列文件c++:
addressbook.pb.h,
聲明你生成的類的頭文件。//caffe編譯caffe.proto生成的caffe.pb.h被很多重要類引用,實現數據結構的創建和使用addressbook.pb.cc
, 包含你的類的實現文件。
python會生成 addressbook_pb2.py
3.Protocol Buffer的API
讓我們來看看一些生成的代碼,看看編譯器生成了哪些類和函數給你。如果你看了tutorial.pb.h,你可以看到,你有一個您在tutorial.proto中指定每個消息的類。 再看看Person類,您可以看到,編譯為每個字段生成了訪問器。比如,對于name,id,email,和phone字段,c++你會有這些方法:
// name
inline bool has_name() const;
inline void clear_name();
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline ::std::string* mutable_name();
// id
inline bool has_id() const;
inline void clear_id();
inline int32_t id() const;
inline void set_id(int32_t value);
// email
inline bool has_email() const;
inline void clear_email();
inline const ::std::string& email() const;
inline void set_email(const ::std::string& value);
inline void set_email(const char* value);
inline ::std::string* mutable_email();
// phone
inline int phone_size() const;
inline void clear_phone();
inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phone() const;
inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phone();
inline const ::tutorial::Person_PhoneNumber& phone(int index) const;
inline ::tutorial::Person_PhoneNumber* mutable_phone(int index);
inline ::tutorial::Person_PhoneNumber* add_phone();
getters有完整的名稱包含小寫的字段,setter函數始于set_。還有has_函數為每個單獨的字段(required和optional)驗證是否已經被設置。最后,每個字段都有一個clear_函數使該字段重置到空的狀態。
然而,數字類型id字段只有基本的set,而name和email字段因為是字符串,所以有一對額外的函數。——一個讓你可以得到一個直接指向字符串的指針(可修改目標)的mutable_ getter,和一個額外的setter。注意,您可以調用mutable_email(),即使email還沒有設置;它將被自動初始化為一個空字符串。如果你有一個單獨的消息字段在這個例子中,它還將會有一個mutable_函數但不會是一個set_函數。
Repeated字段也有一些特殊的函數——看repeated phone字段的函數,你可以發現
- 檢查repeated字段的_size(換句話說,有多少電話號碼與這個Person有關)。
- 得到一個使用其索引指定的電話號碼。
- 在指定的索引中更新現有的電話號碼。
- 添加另一個您可以編輯的電話號碼的消息(repeated標量類型有一個add_可以讓你使用新值)。
python比較不同,并不直接生成數據訪問代碼,而是生成特定的描述器為所有messages, enums, and fields,和一些空類,每個對應一種數據類型:
class Person(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
class PhoneNumber(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _PERSON_PHONENUMBER
DESCRIPTOR = _PERSON
class AddressBook(message.Message):
__metaclass__ = reflection.GeneratedProtocolMessageType
DESCRIPTOR = _ADDRESSBOOK
__metaclass__ = reflection.GeneratedProtocolMessageType這個很重要,可當作用于創建類的模板。加載時,GeneratedProtocolMessageType
metaclass用特定的描述器來創建所有的python方法,可以使用Person
class 當作它 定義了 each field of the Message
base class as a regular field,例如:
import addressbook_pb2
person = addressbook_pb2.Person()
person.id = 1234
person.name = "John Doe"
person.email = "[email protected]"
phone = person.phones.add()
phone.number = "555-4321"
phone.type = addressbook_pb2.Person.HOME
4.caffe.proto 中選段編譯:
取了caffe.proto 中關于Blob結構定義的部分,單獨取出來:
syntax = "proto2";
package blob_paractice;
// Specifies the shape (dimensions) of a Blob.
message BlobShape {
repeated int64 dim = 1 [packed = true];
}
message BlobProto {
optional BlobShape shape = 7;
repeated float data = 5 [packed = true];
repeated float diff = 6 [packed = true];
repeated double double_data = 8 [packed = true];
repeated double double_diff = 9 [packed = true];
// 4D dimensions -- deprecated. Use "shape" instead.
optional int32 num = 1 [default = 0];
optional int32 channels = 2 [default = 0];
optional int32 height = 3 [default = 0];
optional int32 width = 4 [default = 0];
}
然后進到目錄里編譯:
protoc --cpp_out=./ ./blob.proto
生成了blob.pb.h blob.pb.cc,其中blob.pb.h:
namespace blob_paractice {
// Internal implementation detail -- do not call these.
void protobuf_AddDesc_blob_2eproto();
void protobuf_AssignDesc_blob_2eproto();
void protobuf_ShutdownFile_blob_2eproto();
class BlobProto;
class BlobShape;
// ===================================================================
class BlobShape : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:blob_paractice.BlobShape) */ {
public:
BlobShape();
virtual ~BlobShape();
BlobShape(const BlobShape& from);
inline BlobShape& operator=(const BlobShape& from) {
CopyFrom(from);
return *this;
}
這段程序之前有一些頭文件引用,省略了,之后還有一些定義了類的功能部分也略去了,只看咱們要用的API部分:
// nested types ----------------------------------------------------
// accessors -------------------------------------------------------
// repeated int64 dim = 1 [packed = true];
int dim_size() const;
void clear_dim();
static const int kDimFieldNumber = 1;
::google::protobuf::int64 dim(int index) const;
void set_dim(int index, ::google::protobuf::int64 value);
void add_dim(::google::protobuf::int64 value);
const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
dim() const;
::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
mutable_dim();
從上面,可以清楚的看到對BlobShape結構中repeated int64 dim = 1 [packed = true];的功能:
大小、清理、查看、設置、重復定義和mutable_指針獲取
當然還有BlobProto的:
// nested types ----------------------------------------------------
// accessors -------------------------------------------------------
// optional .blob_paractice.BlobShape shape = 7;
bool has_shape() const;
void clear_shape();
static const int kShapeFieldNumber = 7;
const ::blob_paractice::BlobShape& shape() const;
::blob_paractice::BlobShape* mutable_shape();
::blob_paractice::BlobShape* release_shape();
void set_allocated_shape(::blob_paractice::BlobShape* shape);
// repeated float data = 5 [packed = true];
int data_size() const;
void clear_data();
static const int kDataFieldNumber = 5;
float data(int index) const;
void set_data(int index, float value);
void add_data(float value);
const ::google::protobuf::RepeatedField< float >&
data() const;
::google::protobuf::RepeatedField< float >*
mutable_data();
// repeated float diff = 6 [packed = true];
int diff_size() const;
void clear_diff();
static const int kDiffFieldNumber = 6;
float diff(int index) const;
void set_diff(int index, float value);
void add_diff(float value);
const ::google::protobuf::RepeatedField< float >&
diff() const;
::google::protobuf::RepeatedField< float >*
mutable_diff();
// repeated double double_data = 8 [packed = true];
int double_data_size() const;
void clear_double_data();
static const int kDoubleDataFieldNumber = 8;
double double_data(int index) const;
void set_double_data(int index, double value);
void add_double_data(double value);
const ::google::protobuf::RepeatedField< double >&
double_data() const;
::google::protobuf::RepeatedField< double >*
mutable_double_data();
// repeated double double_diff = 9 [packed = true];
int double_diff_size() const;
void clear_double_diff();
static const int kDoubleDiffFieldNumber = 9;
double double_diff(int index) const;
void set_double_diff(int index, double value);
void add_double_diff(double value);
const ::google::protobuf::RepeatedField< double >&
double_diff() const;
::google::protobuf::RepeatedField< double >*
mutable_double_diff();
// optional int32 num = 1 [default = 0];
bool has_num() const;
void clear_num();
static const int kNumFieldNumber = 1;
::google::protobuf::int32 num() const;
void set_num(::google::protobuf::int32 value);
// optional int32 channels = 2 [default = 0];
bool has_channels() const;
void clear_channels();
static const int kChannelsFieldNumber = 2;
::google::protobuf::int32 channels() const;
void set_channels(::google::protobuf::int32 value);
// optional int32 height = 3 [default = 0];
bool has_height() const;
void clear_height();
static const int kHeightFieldNumber = 3;
::google::protobuf::int32 height() const;
void set_height(::google::protobuf::int32 value);
// optional int32 width = 4 [default = 0];
bool has_width() const;
void clear_width();
static const int kWidthFieldNumber = 4;
::google::protobuf::int32 width() const;
void set_width(::google::protobuf::int32 value);
一應功能,說來就來,方便快捷,運行效率有保證。
智能推薦
Netty -08- Google Protobuf
編碼和解碼的基本介紹 編寫網絡應用程序時,因為數據在網絡中傳輸的都是二進制字節碼數據,在發送數據時就需要編碼,接收數據 時就需要解碼 codec(編解碼器) 的組成部分有兩個:decoder(解碼器)和 encoder(編碼器)。encoder 負責把業務數據轉換成字節 碼數據,decoder 負責把字節碼數據轉換成業務數據 Netty 本身的編碼解碼的機制和問題分析 Netty 自身提供了一些 ...
十一、Google Protobuf 編解碼
Protobuf是一個靈活、高效、結構化的數據序列化框架,相比于XML等傳統的序列化工具,它更小、更快、更簡單。Protobuf支持數據結構化一次可以到處使用,甚至跨語言使用,通過代碼生成工具可以自動生成不同語言版本的源代碼,甚至可以在使用不同版本的數據結構進程間進行數據傳遞,實現數據結構的前向兼容。 一、Protobuf環境搭建 開發環境:Win10 64 bit; JDK 1.8; proto...
google protobuf (c++) 學習
probuf是goole推出的微型RPC框架,這里記錄下安裝測試。 參考文章: Google Protocol Buffers淺析(一) 一、Ubuntu 18.04安裝 C++ Protocol 首先參考官方README文檔進行安裝: 前提 當然可以在如下網站進行源碼下載離線安裝。 https://github.com/protocolbuffers/protobuf/releases/late...
網頁搜索引擎中的核心索引結構 - 利用 Google Protobuf 構建
索引結構 — Index 利用 Google Protobuf 構建索引結構 —– Index 在搜索引擎中,常見的索引方法就是正排索引和倒排索引,因此我們 Index 索引結構要包括兩兩個方面: 正排索引 — ( forward_index ) 倒排索引 — ( inverted_index ) 正排索引 正排索引是根據文件的id號,得...
【C++】Google Protocol Buffer(protobuf)詳解
1、簡介 Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言數據標準, Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用于結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。目前提供了 C++、Java、Py...
猜你喜歡
使用CSharp編寫Google Protobuf插件
什么是 Google Protocol Buffer? Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言數據標準,目前已經正在使用的有超過 48,162 種報文格式定義和超過 12,183 個 .proto 文件。他們用于 RPC 系統和持續數據存儲系統。 Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用于結...
【JAVA】google protobuf 3.0 安裝使用
一開始從官網下載的protobuf 3.3.0的最新版本,但是編譯安裝的時候有bug,回退到3.0穩定版本 下載鏈接 tar.gz解壓后安裝 tar -zxvf xxx.tar.gz ./configure --prefix=/home/protobuf make make install 寫proto文件 生成java文件 序列化寫入磁盤文件并讀取出來...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...