LoginSignup
9
6

More than 3 years have passed since last update.

【Core ML】YOLO v3をCoreMLに変換する方法

Last updated at Posted at 2020-10-29

YOLO v3をCoreMLに変換してみたので手順を書きます。

YOLO v3は、物体を検出するアルゴリズムです。
YOLOをiOSで動作させるのに一番簡単な方法は、Apple公式のCore MLモデルをダウンロードして使用することですが、今回は手動で変換してみました。

オリジナルのYOLOv3はDarknet構築されていますが、今回はこちらにあるKerasに変換したものを使用します。

qqwweee/keras-yolo3
https://github.com/qqwweee/keras-yolo3

手順

Google Colaboratoryでの手順を示します。

1.必要なライブラリをインストール & インポートする

Notebook
!pip install tensorflow-gpu==1.14.0
!pip install -U coremltools
!pip install keras==2.2.4

2.keras-yolo3をcloneする

まずは、リポジトリから落としてきます。

Notebook
!git clone https://github.com/qqwweee/keras-yolo3

3.keras-yolo3を動かしてみる

まずは、Pythonでそのままkeras-yolo3を動かしてみます。
このあたりはgithubのREADME.mdに書いてある手順そのままです。

最初に重み情報のファイルをダウンロードします。

Notebook
%cd keras-yolo3
#!wget https://pjreddie.com/media/files/yolov3.weights

keras版のyoloモデルに変換します。

Notebook
!python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5

適当な画像をアップロードして推論させてみます。
今回はneco.jpgというファイルをアップロードしました。

! python yolo_video.py --image --input neco.jpg

# 出力結果
# (416, 416, 3)
# Found 2 boxes for img
# bed 0.66 (11, 481) (656, 660)
# cat 1.00 (98, 17) (624, 637)
# 6.3229040969999915

ちゃんと猫を検出できているようです。

4.CoreMLに変換する

Core ML Toolsを使って変換します。
入力画像は416(width)x416(height)x3(RGB)としています。
また、image_scaleに1/255.0を設定して正規化をします。

Notebook
from keras.models import load_model
from coremltools.converters import keras as converter
mlmodel = converter.convert(keras_model,
  output_names=['grid1','grid2','grid3'], 
  input_name_shape_dict = {'input1' : [None, 416, 416, 3]},
  image_input_names='input1', 
  image_scale=1/255.0,
)

# 出力
# 0 : input_1, <keras.engine.input_layer.InputLayer object at 0x7f7e4058fa58>
# 1 : conv2d_1, <keras.layers.convolutional.Conv2D object at 0x7f7e41cffb38>
# 2 : batch_normalization_1, <keras.layers.normalization.BatchNormalization object at 0x7f7e41cc6438>
# 〜略〜
# For large sized arrays, multiarrays of type float32 are more efficient.
# In future, float input/output multiarrays will be produced by default by the converter.
# Please use, either the flag 'use_float_arraytype' during the call to convert or
# the utility 'coremltools.utils.convert_double_to_float_multiarray_type(spec)', post-conversion.

変換したCore MLのモデルを保存します。

Notebook
coreml_model_path = 'yolo.mlmodel'
mlmodel.save(coreml_model_path)

5.推論結果の表示を調べる

CoreMLへの変換はうまくいきましたが、これをXcodeのプロジェクトにコピーして、Vision Frameworkで推論させようとすると失敗します。

というのは、YOLOv3の出力は3つあり、1x1x255x13x13, 1x1x255x26x26, 1x1x255x52x52というshapeをしていますが、このままではVision Frameworkでは解釈できないためです。出力結果をデコードする必要があります。

YOLOv3の出力についてはこちらのブログがわかりやすかったです。
一般物体認識YOLOv3のモデル構造

デコードは自分で作ると大変そうなので、今回はこちらのプロジェクトを使います。

Ma-Dan/YOLOv3-CoreML
https://github.com/Ma-Dan/YOLOv3-CoreML

このプロジェクトでは、CoreMLの出力が255x13x13、255x26x26、255x52x52となることを想定しています。
この形になるように出力をReshapeする必要があります。

6.出力をreshapeする

モデルの出力を次のようにreshapeします。

1x1x255x13x13 → 255x13x13
1x1x255x26x26 → 255x13x26
1x1x255x52x52 → 255x13x52

そのためには、Core ML Toolsでreshapeするためのレイヤーを追加する必要があります。
なお、Core ML ToolsでCore MLモデルのレイヤーを編集する方法はこちらに詳しく書いてあります。

Core ML Toolsでレイヤーを編集する方法
https://qiita.com/TokyoYoshida/items/7aa67dcea059a767b4f2

reshapeするレイヤーですが、最初は次元を削減するadd_squeezeというのがあるのでを試しましたが、なぜかうまくいきませんでした。

また、add_reshapeというのもありましたが、これだと先頭の1x1の部分の次元が削減されないままでした。

いろいろ調べた結果、add_reshape_staticというのがあり、これを使うとうまくreshapeできました。

次のようにして追加します。

Notebook
from coremltools.models.neural_network import datatypes

builder.add_reshape_static(name='Reshape1', input_name='grid1', output_name='output1', output_shape=(255,13,13))
builder.add_reshape_static(name='Reshape2', input_name='grid2', output_name='output2', output_shape=(255,26,26))
builder.add_reshape_static(name='Reshape3', input_name='grid3', output_name='output3', output_shape=(255,52,52))

次に、モデル全体の出力のshapeを指定します。

Notebook
builder.spec.description.output[0].name = "output1"
builder.spec.description.output[0].type.multiArrayType.shape[0] = 255
builder.spec.description.output[0].type.multiArrayType.shape.append(13)
builder.spec.description.output[0].type.multiArrayType.shape.append(13)

builder.spec.description.output[1].name = "output2"
builder.spec.description.output[1].type.multiArrayType.shape[0] = 255
builder.spec.description.output[1].type.multiArrayType.shape.append(26)
builder.spec.description.output[1].type.multiArrayType.shape.append(26)

builder.spec.description.output[2].name = "output3"
builder.spec.description.output[2].type.multiArrayType.shape[0] = 255
builder.spec.description.output[2].type.multiArrayType.shape.append(52)
builder.spec.description.output[2].type.multiArrayType.shape.append(52)

最後にモデルを保存します。

Notebook
mlmodel_modified = coremltools.models.MLModel(spec)
mlmodel_modified.save('Yolov3.mlmodel')

7.アプリで表示させる

あとは、Core MLのモデルをYOLOv3-CoreMLのプロジェクトにDrag & Dropして実行するだけです。

試しにXcode上からモデルを表示すると、正しく認識できていることがわかります。

実行結果です。

ちゃんと認識できていますね。

最後に

NoteではiOS開発について定期的に発信していますので、フォローしていただけますと幸いです。
https://note.com/tokyoyoshida

Twitterでも発信しています。
https://twitter.com/jugemjugemjugem

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6