ONNXを試す

前回はONNXとは何なのか、インストール方法を書いたので、 今回は実際に使ってみた、という記事です。

ONNXデータのインポート

まずはデータを取得するところからです。 サンプルとしてONNXのモデルのレポジトリに記載されているリンクから好きなモデルのファイルを取得します。ここではinception v1モデルのonnxファイルをダウンロードしたいと思います。一応以下のコマンドからも取得できます。

$ wget https://s3.amazonaws.com/download.onnx/models/opset_9/inception_v1.tar.gz

これを解凍すると、

inception_v1/
  |-model.onnx
  |-test_data_0.npz
  |-test_data_1.npz
  |-test_data_2.npz
  |-test_data_set_0/
  |-test_data_set_1/
  |-test_data_set_2/

このようなディレクトリ構成になります。 とりあえずモデルの情報を見てみましょう。

import onnx
model_path = "./model.onnx"
model = onnx.load(model_path)
for node in model.graph.node:
   print(node)

これだけです。実行結果は

input: "inception_4c/output_1"
input: "inception_4d/1x1_w_0"
input: "inception_4d/1x1_b_0"
output: "inception_4d/1x1_1"
name: ""
op_type: "Conv"
attribute {
  name: "strides"
  ints: 1
  ints: 1
  type: INTS
}
attribute {
  name: "pads"
  ints: 0
  ints: 0
  ints: 0
  ints: 0
  type: INTS
}
attribute {
  name: "kernel_shape"
  ints: 1
  ints: 1
  type: INTS
}

こういう感じの構造がいっぱい出てきます。入出力の指定、演算の指定、ストライドやパディング、カーネルのサイズなどの情報を得られるのでこのonnxファイルをもとにどのような構造のモデルなのかを解析することができますね。

Netronを試す

ONNXのモデルを上記のようにインポートできたら次はNetronというVisualizerを起動させてみます。webとGUIアプリのどちらからも選べます。今回はwebアプリを使ってみます。 https://lutzroeder.github.io/netron/ にアクセスして、"Open Model"でダウンロードしてきたonnxファイルを選択します。 そうするとしばらく待つと次のような図が出てきて、きちんとモデルに関する情報が抽出できていることがわかります。 f:id:kiizuka:20190220091409p:plain それぞれの層のブロックをクリックすると計算に用いるカーネルサイズやパディング、重みの値なども見ることができます。 f:id:kiizuka:20190220091418p:plain

ONNXへモデルをエクスポート

onnxファイルからモデルを取得することはできたので、次は自分で作成したモデルをエクスポートしてみます。

import onnx                                                                                                         
import onnx.helper as helper

export_onnx_path = "sample_model.onnx"

# define input
input_tensor = []
input_tensor.append(helper.make_tensor_value_info("input_1", onnx.TensorProto.FLOAT, [24, 24, 3]))
input_tensor.append(helper.make_tensor_value_info("input_2", onnx.TensorProto.FLOAT, [24, 24, 3]))

# define output
output_tensor = []
output_tensor.append(helper.make_tensor_value_info("output", onnx.TensorProto.FLOAT, [24, 24, 3]))

# define computational graph
nodes = []
nodes.append(helper.make_node("Add", ["input_1", "input_2"], ["output"]))

graph = helper.make_graph(nodes, "Sample graph", input_tensor, output_tensor)

model = helper.make_model(graph)

with open(export_onnx_path, "wb") as f:
    f.write(model.SerializeToString())

割とシンプルな書き方で何がしたいかがわかりやすいでしょうか? どのような計算グラフを作成したかを想像してもらった上で、 このスクリプトで作成したonnxファイルを改めて先程のNetronで見てみると、 こんな感じです。 f:id:kiizuka:20190220091449p:plain こんな小さなグラフでも可視化すると達成感があります。 実際には自分で計算グラフを構成するようなことは少なくて、ある機械学習フレームワークで作成した学習モデルを他のフレームワークに移植するというために使うことが多いです。 そのためのチュートリアルのレポジトリもあるので見てみたいと思います。