
前幾天朋友問了 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
檔用於定義 message,message 相當於你想儲存的資料結構的定義。晚點兒在細說裡面的細節。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 ):
- 使用
protoc
把.proto
檔編譯成目標語言。以 python 來說,protoc
會編譯出一個.py
檔,裡面有根據.proto
檔給的定義產生的各種 abstract class 可以使用。以我這個例子來說,我會在python
這個資料夾下,產生一個fully_connect_nn_pb2.py
的檔案,之後直接在read_nn.py
與write_nn.py
裡直接import
來用。 - 匯入
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 使用方式不同:
RepeatedCompositeFieldContainer
必須透過add
method 取得其中的 message 實例,在對此實例進行資料儲存、覆寫等作業。RepeatedScalarFieldContainer
的 API 基本上與list
無異,可以透過insert
、append
、extend
等 method 進行資料儲存與覆寫。
最後,來說說每個 message 最基本的兩個 method: SerializeToString
與 ParseFromString
。顧名思義, 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
。
詳細的細節,你可以看我 Makefile
裡 txt-example
這個 target 下相關的檔案,重點 API 摘要如下:
text_format.MessageToString(message)
:把一個 message 轉換成字串。把這個字串在寫入一個檔案即是protobuf
的文字格式。text_format.Merge(string, message)
:把一個字串讀進一個 message。在我的範例裡 (example_str_fmt.py
),這個函數除了可以把讀入的字串解析進一個 message 裡之外,也可以用來把多個pbtxt
檔合併成一個 message ,當然前提是這些檔案紀錄的是同一種 message 。