簡介 Google Protocol Buffer

Dboy Liao

前幾天朋友問了 Tensorflow 的 Graph, Session 之類的要怎麼 serialize 的問題,結果 issue 一路追就追到了 protobuf (Protocol Buffer) 這個 python 套件。玩一玩之後覺得還蠻好玩的,而且可以進一步了解 tf.train.Saver 的運作原理,想說就寫一篇簡單的 tutorial 算是記錄一下使用 protobuf 的一些基礎知識跟流程。

由於我看 protobuf 的主要目的是為了 tensorflow ,所以範例以 python 為主。在這個範例呢,假想我們有個 fully-connected 的神經網路,那我們要怎麼把這個神經網路用 protobuf 存起來呢?咱們直接來看 code!

上面主要有三個檔案:

  • fully_connected_nn.proto : .proto 檔用於定義 messagemessage 相當於你想儲存的資料結構的定義。晚點兒在細說裡面的細節。
  • write_nn.py : 我寫的一個簡單腳本用以產生隨機的一個 Neural Network 的 weights 跟 activation function 的名字。
  • read_nn.py : 跟 write_nn.py 相反,可以用來讀取 write_nn.py 產生的 binary 檔案。

你可以使用我提供的 Makefile 執行 make example 去跑過整個 Compiling、Serailize 跟 Deserialize 的流程,不過大致來說的步驟是 ( protoc 是 protobuf 的 compiler ):

  1. 使用 protoc.proto 檔編譯成目標語言。以 python 來說,protoc 會編譯出一個 .py 檔,裡面有根據 .proto 檔給的定義產生的各種 abstract class 可以使用。以我這個例子來說,我會在 python 這個資料夾下,產生一個 fully_connect_nn_pb2.py 的檔案,之後直接在 read_nn.pywrite_nn.py 裡直接 import 來用。
  2. 匯入 protoc 產生的目標語言模組進行 Deserialize/Serialize 。

大概就是這樣!

安裝

protobuf 的 runtime support 以 source build 的安裝方式請參考這裡。如果像我一樣很懶,想直接用 precompiled 的版本,可以到這裡根據你的作業系統下載相關的壓縮檔即可。

Programming Note

從這次使用 protobuf 的 python API,大致上可以歸納出以下幾點

repeated : 根據官方 tutorial 的說法,概念上來說被標示為 repeated 的 field 就像是動態陣列 (dynamically sized array)。白話一點,相對應的 python class 中會是一個像 list 的東西。

為什麼說是像 list 而不乾脆點兒說它就是 list 呢?因為它不是嘛!具體來說, layers 是一個 RepeatedCompositedFieldContainer ,而 layers 下的 weight.data 是一個 RepeatedScalarFieldContainer ,如下圖所示。

那說穿了也不難,從我的例子中可以看到,在 protobuf 裡,message 的 field 可以是另一個 message ,也可以是 Scalar Type ,你可以在 Language Guide (proto3) 找到 Scalar Type 與相對應語言中的資料型態。在我的例子裡, layers.proto 檔裡被宣告成 repeated Layer ,而 Layer 又剛好是另一個 message ,有點兒像 JSON 中包 JSON 的感覺;另一方面來說, weight.data 被宣告成 repeated float ,而 float 是一個 Scalar type,因此對應到不同的 Container 型別。

OK,說了這麼多,那又怎樣?在寫入的階段 API 使用方式不同:

  1. RepeatedCompositeFieldContainer 必須透過 add method 取得其中的 message 實例,在對此實例進行資料儲存、覆寫等作業。
  2. RepeatedScalarFieldContainer 的 API 基本上與 list 無異,可以透過 insertappendextend 等 method 進行資料儲存與覆寫。

最後,來說說每個 message 最基本的兩個 method: SerializeToStringParseFromString 。顧名思義, SerializeToString 將會把 message 實例 Serialize 成一個可寫入的字串,這時只要 open 一個 binary write mode 的檔案實例寫入這個字串即可完成 Serialization 。Serialize 好的檔案,只要再 open 一個 binary read mode 的檔案實例,把內容讀出來給 ParseFromString 即可完成 Deserialize 。很簡單吧!

好啦,目前就玩這樣。以後有機會再寫吧。

更新:2017 / 07 / 17

Tensorflow 的 repo 追著追著,發現有個常用的 protobuf text format 的 API 漏了說,索性補在這裡。

上面寫的讀寫神經網路的例子裡,我最後輸出的檔案是 binary 的格式,人是看不懂的 (我知道有些人可以,但這邊說的人指一般人好嗎…)。所以輸出文字格式的好處,大多都用在教學或 debug 的時候可以直接看,大概是它的主要用途。

照舊,以 python API 為主。

安裝 tensorflow 套件之後,應該會自動安裝 protobuffer 的 python 套件 protobuf ,如果沒有的話,就跑個 pip install protobuf 就好了。我有更新一個小小的範例在上面的 gist 裡,執行 make txt-example 應該就會看到一些結果。重點就是 protobuf.text_format

詳細的細節,你可以看我 Makefiletxt-example 這個 target 下相關的檔案,重點 API 摘要如下:

  • text_format.MessageToString(message):把一個 message 轉換成字串。把這個字串在寫入一個檔案即是 protobuf 的文字格式。
  • text_format.Merge(string, message):把一個字串讀進一個 message。在我的範例裡 (example_str_fmt.py),這個函數除了可以把讀入的字串解析進一個 message 裡之外,也可以用來把多個 pbtxt 檔合併成一個 message ,當然前提是這些檔案紀錄的是同一種 message

References

No responses yet

Write a response