[Amazon SageMaker] 組み込みアルゴリズム(ImageClassification)で作成したモデルをOpenVINOツールキットで使用してみました

2020.09.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部の平内(SIN)です。

OpenVINO ツールキットでは、モデルをIRと呼ばれる中間表現フォーマットにして利用します。そして、同ツールキットには、各種のフレームワークで作成したモデルを、IRに変換するツールが含まれています。

今回は、Amazon SageMaker(以下、SageMaker)の組み込みアルゴリズム(画像分類)で作成したモデルを変換して使用してみました。

IRは、以下の3種類が生成可能ですが、今回の試した作業は、FP16及びFP32となっています。

  • FP16 16ビット浮動小数点演算 (NCS)
  • FP32 32ビット浮動小数点演算 (CPU)
  • INT8 8ビット整数演算

2 モデル

変換に使用したモデルは、下記で作成したものです。

出力先に格納されている、model.tar.gzを解凍すると、3つのファイルが格納されています。

こちらは、MXNet形式のモデルです。

3 Model Optimizer

Model Optimizerは、OpenVINOツールキットをインストールする手順の中で展開されるようになっています。
参考:Model Optimizer Developer Guide

変換作業は、Model Optimizerがインストールされてたディレクトリで、mo_mxnet.py(MXNet用)を実行するだけです。

最初に、FP16用とFP32用を作成するためディレクトリを作成しておきます。

以下は、FP16で作成している例です。

$ cd /opt/intel/openvino/deployment_tools/model_optimizer

$ export WORK_DIR=/tmp/model
$ export FP_TYPE=FP16

$ python3 mo_mxnet.py --input_model $WORK_DIR/image-classification-0012.params --input_shape \[1,3,224,224\] --output_dir $WORK_DIR/$FP_TYPE --data_type $FP_TYPE

Model Optimizer arguments:
Common parameters:
    - Path to the Input Model:  /tmp/model/image-classification-0012.params
    - Path for generated IR:    /tmp/model/FP16
    - IR output name:   image-classification-0012
    - Log level:    ERROR
    - Batch:    Not specified, inherited from the model
    - Input layers:     Not specified, inherited from the model
    - Output layers:    Not specified, inherited from the model
    - Input shapes:     [1,3,224,224]
    - Mean values:  Not specified
    - Scale values:     Not specified
    - Scale factor:     Not specified
    - Precision of IR:  FP16
    - Enable fusing:    True
    - Enable grouped convolutions fusing:   True
    - Move mean values to preprocess section:   False
    - Reverse input channels:   False
MXNet specific parameters:
    - Deploy-ready symbol file:     None
    - Enable MXNet loader for models trained with MXNet version lower than 1.0.0:   False
    - Prefix name for args.nd and argx.nd files:    None
    - Pretrained model to be merged with the .nd files:     None
    - Enable saving built parameters file from .nd files:   False
    - Use the config file:  None
Model Optimizer version:

[ SUCCESS ] Generated IR version 10 model.
[ SUCCESS ] XML file: /tmp/model/FP16/image-classification-0012.xml
[ SUCCESS ] BIN file: /tmp/model/FP16/image-classification-0012.bin
[ SUCCESS ] Total execution time: 38.71 seconds.
[ SUCCESS ] Memory consumed: 944 MB.

FP32及び、FP16を作成すると、指定した出力先に、.xml.bin が作成されます。これが、IRフォーマットのモデルです。


参考: Converting a MXNet* Model

4 コード

以下は、生成したIRを使用して、MacOS上で推論しているコードです。

Syohin40クラスは、商品検出のモデルをラップしたクラスで、カメラから取得した画像を、モデルの入力形式に変換して推論しています。

CPUで実行して、推論にかかった時間は、0.1〜0.3秒程度でした。

index.py

import numpy as np
import time
import cv2
import time
from openvino.inference_engine import IECore 
from PIL import ImageFont, ImageDraw, Image
from model import Model

class Syohin40(Model):
    def __init__(self, ie, device, model):
        super().__init__(ie, device, model)
        _, _, h, w = self.input_size
        self.__input_height = h
        self.__input_width = w

    def __prepare_frame(self, frame):
        initial_h, initial_w = frame.shape[:2]
        scale_h, scale_w = initial_h / float(self.__input_height), initial_w / float(self.__input_width)
        in_frame = cv2.resize(frame, (self.__input_width, self.__input_height))
        in_frame = in_frame.transpose((2, 0, 1))
        in_frame = in_frame.reshape(self.input_size)
        return in_frame, scale_h, scale_w

    def infer(self, frame):
        in_frame, _, _ = self.__prepare_frame(frame)
        result = super().infer(in_frame)
        # [1, 41] => [41]
        return result.squeeze()

# MacOS
ie = IECore()
device = "CPU"

syohin40 = Syohin40(ie, device, "image-classification-0012")
CLASSES = ["ポリッピー(GREEN)","OREO","カントリーマム","ポリッピー(RED)","柿の種(わさび)"
          ,"通のとうもろこし","CHEDDER_CHEESE","ピーナッツ","ストーンチョコ","PRETZEL(YELLOW)"
          ,"海味鮮","柿の種","カラフルチョコ","フルグラ(BROWN)","NOIR"
          ,"BANANA(BLOWN)","チーズあられ","俺のおやつ","PRIME","CRATZ(RED)"
          ,"CRATZ(GREEN)","揚一番","ポリッピー(YELLOW)","こつぶっこ","アスパラガス"
          ,"海苔ピーパック","いちご","梅しそチーズあられ","通のえだ豆","柿の種(梅しそ)"
          ,"PRETZEL(BLACK)","辛子明太子","CRATZ(ORANGE)","チョコメリゼ","フライドポテト(じゃがバター味)"
          ,"BANANA(BLUE)","でん六豆","パズル","フルグラ(RED)","PRETZEL(GREEN)"
          ,"フライドポテト(しお味)",]

def putText(image, text, point, size, color):
    font = ImageFont.truetype("./GenShinGothic-Bold.ttf", size)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    image = Image.fromarray(image)
    draw = ImageDraw.Draw(image)
    draw.text(point, text, color, font)
    image = np.asarray(image)
    return cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

def main():

    # OpenCV
    WIDTH = 640
    HEIGHT = 480
    FPS = 24
    cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
    cap.set(cv2.CAP_PROP_FPS, FPS)

    while(True):
        _, frame = cap.read()

        start = time.time() # 時間計測
        out = syohin40.infer(frame)
        processing_time = time.time() - start
        print("processing_time {}sec".format(processing_time))

        # 表示
        prob = np.max(out)
        index = np.argmax(out)
        print("Class: %s, probability: %f" % (CLASSES[index], prob))

        if(0.4<prob):
            text = "{} {}".format(CLASSES[index], prob)
            frame = putText(frame, text, (20, int(HEIGHT) - 120), 60, (255, 255, 255))
        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()

main()

LD_LIBRARY_PATHに設定されているはずなのですが、何故か、IECore() からモデル生成すると、libmyriadPlugin.dylibが読み込めないというエラーとなってしまたので、ctypes.cdll.LoadLibraryで一旦読み込んでいます。

model.py

import ctypes

class Model:
    def __init__(self, ie, device, model):

        modelPath = "./FP32/{}".format(model)
        if(device=="MYRIAD"):
            modelPath = "./FP16/{}".format(model)

        net = ie.read_network(modelPath + ".xml", modelPath + ".bin")

        # LD_LIBRARY_PATHは、遠ているはずなのに、なぜか読み込みに失敗するので、一回読み込んでおく
        ctypes.cdll.LoadLibrary('/opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libinference_engine_lp_transformations.dylib')
        ctypes.cdll.LoadLibrary('/opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libmyriadPlugin.dylib')

        self.exec_net = ie.load_network(network=net, device_name=device, num_requests=2)

        self.input_name = next(iter(net.input_info))
        self.output_name = next(iter(net.outputs))

        self.input_size = net.input_info[self.input_name].input_data.shape
        self.output_size = net.outputs[self.output_name].shape

    def infer(self, data):
        input_data = {self.input_name: data}
        infer_result = self.exec_net.infer(input_data)
        return infer_result[self.output_name]

動画は、上記のコードを実行してる様子です。

5 最後に

今回は、SageMakerで作成したモデルをOpenVINOツールキットで利用してみました。

OpenVINOツールキットでは、モデルごとにCPU、GPUを振り分けて使用したり、USBで追加可能なNCSなどがあります。

SageMakerで作成したモデルも問題なく変換して利用可能ということで、一段と、利用範囲が広がるかも知れません。