見出し画像

PyTorchのモデルをOpenVINO用に変換する方法 #Granvalley


こんにちは、gene@granvalleyです。今回はPython向け機械学習ライブラリのPyTorchのモデルをOpenVINOで扱える形式に変換する方法を紹介します。OpenVINOを使って処理をした場合に速度がどれくらい上がったか検証もしているので、ディープラーニング推論で処理速度が出なくて困っている方はぜひ最後まで読んでみてください。

OpenVINOは推論を行うためのツールである

OpenVINOツールキットはインテルより提供されている無償ソフトウェアで、ディープラーニング推論を高速化してくれるものです。OpenVINOを使うことで、インテル製プロセッサを最大限に活かして推論できるということですね。

OpenVINOは”推論高速してくれるもの、ということは、ディープラーニングの学習に関しては別のフレームワーク等を使って行わなければなりません。OpenVINOを使う際には作成(もしくは入手)したモデルをOpenVINOで使える形式に変換し、そのうえで推論を実施する、という作業が必要になります。(ちなみに、ディープラーニングにおける”学習”については弊社のJim2が記事の中で解説していますので、こちらのリンクをぜひご覧ください。)

そこで今回は他のフレームワークのモデルをどのようにOpenVINOで使うのか、一連の流れを見ていきます。

(※インテルはネット上にOpenVINOで使えるモデルをたくさん公開しているので、それらはもちろんダウンロードしてそのまま使えます。)

今回変換するモデル(VGG16)

OpenVINOは現在、TensorFlowやCaffeの他、MXNET、KALDI、ONNXといったモデルの形式に対応してますが、今回はPyTorchのモデルをOpenVINOモデルに変換し、実行してみたいと思います。

PyTorchモデルをOpenVINOで使えるモデル(OpenVINOの世界で言う"中間表現(IR)")に変換するためには、PyTorchのモデルを一旦ONNX形式に変換し、それをOpenVINOツールキットのモデル・オプティマイザーを使ってOpenVINO用のモデルに変換します。ONNXというのは”Open Neural Network Exchange”の略で、様々なフレームワークで使える共通フォーマットのようなものです。

PyTorchでは学習済みモデルを簡単に使うことができるので、ここではVGG16の学習済みモデルをONNX形式に変換してみましょう。このモデルはVGG16で画像認識用データセットのImageNetを学習したものです。

(VGG16は畳み込み層のカーネルを小さくする代わりに、畳み込み層の数を増やしたネットワークです。VGG16について詳しく知りたい人はリンクを本記事の最後に載せてあるので、そちらをご覧ください。)

では、このモデルで試しに推論を行ってみましょう。ImageNetの正解ラベルはこちらからダウンロードできます。

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn.functional as F
from PIL import Image
import json
import time as tm

# 画像の読み込みと前処理
transform = transforms.Compose(
	[transforms.Resize(224), transforms.CenterCrop(224), transforms.ToTensor(),
	  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])]) 
image_path = "unsplash.jpg" #←画像へのパス
image = Image.open(image_path)
img = transform(image).unsqueeze(0)

# ラベルの読み込み
labels = json.load(open("imagenet_class_index.json", "r"))

# モデルの読み込み
net = torchvision.models.vgg16(pretrained = True)

# 推論実施
out = net(img)

# 確信度の高い上位5クラスを表示
result = F.softmax(out, 1).data.squeeze()
probs, idx = result.sort(dim = 0, descending = True)
idx_list = idx.cpu().numpy()
for i in range(5):
   print("{} : {}".format(labels[str(idx_list[i])][1], probs[i]))

処理対象の画像としてこちら(⇩)のハチクイという鳥の画像を与えたところ、100%の確信度でハチクイ(bee eater)に分類しました(ハチクイ以外の確信度がほぼ0)。推論にかかる時間を計ったところ1.26秒でした。

[Photo by Hans van Tol on Unsplash]
hans-van-tol-P1syptb0tTw-unsplash - コピー (2)

※ログ出力

bee_eater : 1.0
coucal : 2.9533053780284035e-09
jacamar : 7.786516431629309e-10
crane : 1.7841872423929317e-10
macaw : 1.4334022857553919e-10

それでは、このモデルをOpenVINOの形式に変換していきましょう。

PyTorchモデルの出力

PyTorchからONNXへの変換にはPyTorchのONNXのライブラリを使ってexportの関数を呼ぶだけです。コードだと以下のように書きます。(先ほどのコードに続けて書けばちゃんと動きます。)

# インポート
import torch.onnx as torch_onnx
from torch.autograd import Variable

# モデル出力のための設定
model_onnx_path = "vgg16.onnx" # 出力するモデルのファイル名
input_names = [ "input" ] # データを入力する際の名称
output_names = [ "output" ] # 出力データを取り出す際の名称

# ダミーインプットの作成
input_shape = (3, 224, 224) # 入力データの形式
batch_size = 1 # 入力データのバッチサイズ
dummy_input = torch.randn(batch_size, *input_shape) # ダミーインプット生成

# 変換実行!!
output = torch_onnx.export(net, dummy_input, model_onnx_path, \
                   verbose=False, input_names=input_names, output_names=output_names)

exportする前にモデルを出力するパスと、入力層と出力層の名称を指定しています(input_namesとoutput_names)。入力層と出力層の名前は指定しなければPyTorchの方で名前を付けてくれますが、OpenVINOで推論をするときに必要になるので自分の好きな名前を付けておきます。

一つわかりづらいところがあり、PyTorchのモデルを出力する際はダミーのインプットを与える必要があります。これはPyTorchがDefine by runという仕組みを取っており、データを流しながら計算グラフ(ニューラルネットワーク)を構築していくためです。そのため、画像のサイズと推論のバッチサイズに合わせたダミーデータ(dummy_input)を準備し、そのデータをモデルに与えてやります。ここでは3チャンネル(RGB)で224x224の画像を1枚づつ処理するように設定しています。

ONNXモデルからOpenVINOのモデルへ

上のコードを実行すると、数十秒ほどで指定したパスにONNXのモデルのファイルができます。今度はそれをOpenVINOの形式に変換しましょう。OpenVINOをインストールするとdeployment_toolsというフォルダにmodel_optimizerのフォルダがあり、その中にmo.pyというファイルが入っています。変換はこれを実行するだけです。具体的にはOpenVINOの環境変数が設定されたコマンドプロンプトで以下のようにmo.pyを実行します(--input_modelという引数で変換したいモデルを渡します) 。

python "c:\Program Files (x86)\IntelSWTools\openvino_2019.3.379\deployme
nt_tools\model_optimizer\mo.py" --input_model c:\Users\gene\Documents\vg
g16.onnx

※モデル・オプティマイザーのファイルへのパスとonnxファイルのパスは適宜変更してください。

このコマンドを実行すればOpenVINO用のモデルのファイルが作業ディレクトリにできます。待ち時間は10秒ほどです。作業ディレクトリを見に行くとONNXモデルと同じファイル名で拡張子が.xmlと.binのファイルがあるはずです。これがOpenVINOで使えるモデルです。

モデルのテスト

ここまででモデルの変換が完了しました。ちゃんと変換できているのか、テストしてみましょう。先ほどPyTorchで推論を行ったのと同じ画像に処理をかけてみます。

# OpenVINO Inference Engineのインポート
from openvino.inference_engine import IENetwork, IEPlugin

# OpenVINOでモデルの読み込み
plugin = IEPlugin(device="CPU")
plugin.add_cpu_extension(r"C:\Program Files (x86)\IntelSWTools\openvino_2019.3.379\deployment_tools\inference_engine\bin\intel64\Release\cpu_extension_avx2.dll")
model_path = 'vgg16.xml'
ie_net = IENetwork(model=model_path, weights=model_path.replace("xml", "bin"))
exec_net = plugin.load(network=ie_net)

# 推論実行
out = exec_net.infer(inputs={'input': img.cpu().numpy()})['output']

result = F.softmax(torch.from_numpy(out), 1).data.squeeze()
probs, idx = result.sort(dim = 0, descending = True)
idx_list = idx.cpu().numpy()
for i in range(5):
   print("{} : {}".format(labels[str(idx_list[i])][1], probs[i]))

CPUやGPUとのインターフェイスとなるクラスを生成し、CPUで処理するのでそこにcpu_extensionを読み込みます。そこに先ほど変換したモデルを読み込んで、そのモデルで推論を行っています。ONNX形式でモデルを出力した時に入力と出力の名前を付けましたが、推論時にはその時の名前でモデルへのデータの入出力を行っています('input'と'output')。

入力画像には先ほどPyTorchで推論を行った時と同じように前処理を行っています。画像サイズが変わったら推論できませんし、正規化も同じようにします。推論結果の後処理もPyTorchの時と同じように行っています。

OpenVINOで処理を行うと、PyTorchで処理した時と同じく100%の確信度でハチクイと予想しました。処理時間の方は0.42秒でOpenVINOの形式にすることで3分の近くでした。この結果であれば、今回はOpenVINOを使うメリットは十分にありますね。

※ログ出力

bee_eater : 1.0
jacamar : 1.6307298567053152e-10
crane : 7.571399757155817e-11
bittern : 4.868838318627944e-11
coucal : 4.520278595876448e-11

画像1000枚の処理時間

大量の画像に処理をかけたらどれくらい時間が短くなるのだろうか、ということでためしに1000枚の画像に対して処理を行ってみました。簡単にデータを入手できるので、CIFAR100というデータセットの画像を1000枚分処理してみます。

# テストデータの読み込みと前処理
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=1, shuffle=False, num_workers=0)

# 時間計測用
time_openvino = 0
time_pytorch = 0

i = 0
with torch.no_grad():
   for data in testloader:
       i += 1
       if i % 10 == 0:
           print(i)
       if i == 1000:
           break
       image, label = data
       
       # OpenVINO処理実行
       start_time = tm.time()  
       outputs = exec_net.infer(inputs={'input': image.cpu().numpy()})['output']
       time_openvino += tm.time() - start_time

       # PyTorch処理実行
       start_time = tm.time()  
       out = net(image)
       time_pytorch += tm.time() - start_time

結果はPyTorchで869秒、OpenVINOで190秒でした。さすが、OpenVINO。4倍以上のスピードが出ています。

※ログ出力

openvino:190.89426159858704
pytorch:869.7181072235107

単純にモデルを置き換えるだけでもこれだけの速度差があるので、エッジデバイスでの推論で処理速度が求められる時にはOpenVINOの使用を検討する価値は十分あると思います。

注意点

ここまでPyTorchからOpenVINOにモデル変換をする方法を見てきました。上述のようにPyTorch→ONNX→OpenVINOというモデルの変換はとても簡単にできます。

しかし、すべてのモデルで同じように変換がうまくいくわけではないことには注意が必要です。例えば私が試した限り、PyTorchのMask-RCNNのモデルは上記の方法ではうまくOpenVINOのモデルに変換できませんでした。OpenVINOのサイトには変換を正式サポートしているPyTorchモデルの一覧が掲載されているので、変換を試す前に確認してみると良いかもしれません。(Mask-RCNNについても参考サイトへのリンクが本記事下部に貼ってあります。)

ただ、一覧に変換したいモデルが載っていないからと言って、変換できないというわけではありません。このブログで今回試した学習済みモデルも、公式サポートのリストには入っていませんが、問題なく動いています。OpenVINOで使いたいモデルがあるなら、試しに変換に挑戦してみるのが良いと思います。

以上のように、OpenVINOで使えるようにモデルを変換するのはとても簡単です。この記事を読んでOpenVINOを使ってみようと思った人がいれば嬉しいです。最後まで読んでくださりありがとうございました。

最後に本記事に関連するサイトへのリンクを載せておきます。

リンク集

PyTorchの公式チュートリアル:PyTorchの一連の処理の仕方については公式のチュートリアルがわかりやすいです。

TORCH.ONNX:PyTorchのONNXライブラリの公式ページです。ONNX形式での出力方法について詳しく載っています。

TORCHVISION.MODELS:PyTorchで用意されているモデルの説明のページです。学習済みモデルの正規化方法についても説明されています。

Install Intel® Distribution of OpenVINO™ toolkit:OpenVINOのインストールはこちらからできます。

Converting a ONNX* Model:ONNXからのモデルの変換について書かれているOpenVINOのページです。公式サポートされているPyTorchのモデルもここにリスト化されています。

VGGネットワーク(VGGNet)とは?要点を爆速rikai入門!:VGG16についてはこちらページの説明がわかりやすいので、興味のある人は参照ください。

Mask-RCNNで細胞画像のインスタンスセグメンテーション:Mask-RCNNについてはこちらのサイトがわかりやすいです。


「こういうディープラーニングを使ったシステムを自分で作ってみたい!」という方はぜひこちらのリンクより弊社までご連絡ください。

※ Intel、インテル、Intel ロゴ、Intel Core、OpenVINOは、米国およびその他の国におけるインテル コーポレーションの商標です。

PyTorch, the PyTorch logo and any related marks are trademarks of Facebook, Inc.



この記事が気に入ったらサポートをしてみませんか?