GRANVALLEY

ブログBlog

劇的に処理速度が改善!PyTorchのモデルをOpenVINO用に変換する方法

公開日 2022年1月7日    最終更新日 2023年4月10日

本記事は、「note.com」を使って、弊社AIコンサルティンググループが運営する「AIグループ@グランバレイ」で、2020年6月に掲載された記事を元に一部修正を加え、再掲載したものです。

今回、Python向け機械学習ライブラリのPyTorchのモデルをOpenVINOで扱える形式に変換する方法についてご紹介します。
また、変換前のPyTorchのモデルとOpenVINOに変換したコードを使ってどのくらい処理速度に違いがあるかも検証しました。今、ディープラーニング推論で処理速度が出なくて困っている方はぜひとも参考にしてください。

推論を行うツール OpenVINO

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

OpenVINOは推論を高速してくれるものですので、ディープラーニングの学習に関しては別のフレームワークを使って行わなければなりません。OpenVINOを使う際には「事前に作成(もしくは入手)したモデルをOpenVINOで使える形式に変換し、そのうえで推論を実施する」という作業が必要になります。

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

ちなみに、ディープラーニングにおける“学習”については弊社Blog記事「人工知能(AI)とは ~ディープラーニング~」の中で解説していますので、ぜひご覧ください。

※インテル社ではOpenVINOで使えるモデルをたくさん公開しており、これらはどなたでもダウンロードできます。

今回変換するPyTorchのモデル:VGG16

OpenVINOは2020年6月現在、TensorFlowやCaffeの他、MXNET、KALDI、ONNXといったモデル形式に対応してます。
今回はPyTorchのモデルをOpenVINOモデルに変換し実行していきます。
PyTorchモデルをOpenVINOで使えるモデル(OpenVINOの世界で言う”中間表現(IR)”)に変換するためには、以下の手順が必要となります。

  1. PyTorchのモデルを一旦ONNX形式に変換
  2. OpenVINOツールキットのモデル・オプティマイザーを使ってOpenVINO用のモデルに変換

ONNXは”Open Neural Network Exchange”の略で、様々なフレームワークで使える共通フォーマット形式のことです。

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

※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] ハチクイ

ログ出力

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

PyTorchモデルの出力

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

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)で224×224の画像を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で使えるモデルです。

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の形式にすることで処理速度は短縮されました。この結果であれば、OpenVINOを使うメリットは十分にあります。

ログ出力

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

画像1,000枚の処理時間を二つのモデルで比較

大量の画像に処理をかけたらどれくらい時間が短縮するかを確かめるため、1,000枚の画像に対して二つのモデルで処理を行ってみました。簡単にデータを入手できるので、CIFAR100というデータセットの画像を1,000枚分処理してみます。

サンプルコード:


# テストデータの読み込みと前処理
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モデルの一覧が掲載されているので、変換を試す前に確認してみると良いかもしれません。

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

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


本記事は、弊社AIコンサルティンググループが運営している「note」内の「AIグループ@グランバレイ」の記事を一部修正を加え転載しております。
https://note.com/gvaiblog/n/nae369fa83e77

※ その他の会社名、製品名は各社の登録商標または商標です。
※ 記事の内容は記事公開時点での情報です。閲覧頂いた時点では異なる可能性がございます。