CoreMLでハマったポイント


こんにちは。もう、寒すぎて冬だけ沖縄に帰りたい@kNagadouです。
この記事は、筋肉エンジニアアドベントカレンダー2017の17日目の記事です。
公開が遅れてしまい、申し訳ありません。お詫びに、2018元旦までにいいねされた数だけ、腕立て伏せします。

さて、今回はCaffeなどの機械学習ライブラリで作成した学習済みのモデルを、
coremltoolsでCoreML用にコンバートして、Xcodeプロジェクトにインポートするまでの手順、
そして実際にやってみてハマったポイントについて書いていきます。

学習済みモデルを用意

まずは、学習済みモデルを作成しなければいけません。

真面目に学習済みモデルを作ろうと思うと、物凄く手間と時間がかかりそうだったので、モデル作成はLabellioというサービスを利用しました。
なかなか便利なサービスで、

  • データ収集
  • ラベリング
  • 学習
  • 学習済みモデルの出力
    • Caffe形式のモデル

を全部やってくれます。すごいです。
(本当は、学習済みモデルも自力で作成する予定でしたが、「これは楽だなー」と手を抜いてしまいました。)

Labellioでモデルの出力が終わると、ZIPファイルで以下一式がDLできます。

caffemodel.binaryproto
deploy.prototxt
labellio.json
labels.json

使用するファイルは、caffemodel.binaryproto, deploy.prototxt, labels.jsonの3つです。

coremltoolsをインストール(ハマりポイント①)

ここで、1つ目のハマりポイントです。

先ほど作成したモデルを、CoreML用にコンバートするためのツール「coremltools」をインストールします。
2017年12月時点で、coremltoolsはPython2.7環境にしか対応していません。
そのため、Python2.7環境を用意する必要があります。
しかし、pyenvなどで構築したPython環境でcoremltoolsを実行すると、次のようなエラーが出ると思います。

Fatal Python error: PyThreadState_Get: no current thread
Abort trap: 6

これは恐らく、こちらの記事の事象と同じことが起きています。
上記の記事を参考に、以下の手順でcoremltoolsの依存関係を修正することでこのエラーは解決できました。

# Python2.7インストール
$ PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 2.7.13
# coremltoolsのオブジェクトの依存関係を修正
$ cd ~/.pyenv/versions/2.7.13/lib/python2.7/site-packages/coremltools
$ install_name_tool -change @rpath/libpython2.7.dylib /Users/<username>/.pyenv/versions/2.7.13/lib/libpython2.7.dylib libcaffeconverter.so
$ install_name_tool -change @rpath/libpython2.7.dylib /Users/<username>/.pyenv/versions/2.7.13/lib/libpython2.7.dylib libcoremlpython.so

モデルをCoreML用の形式にコンバート(ハマりポイント②)

coremltoolsでモデルをコンバートする際、ラベルファイルは改行区切りのテキストファイルとしなければいけないようなので、labels.jsonを以下のように修正してlabels.txtにします。

例)

{"heavy": 1, "olympia": 4, "medium": 3, "slim": 6, "physique": 5, "wrestler": 7, "macho": 2, "bodybuild": 0}

bodybuild
heavy
macho
medium
olympia
physique
slim
wrestler

labels.txtへの修正ができたら、CoreML用の形式にコンバートしましょう。
Labellioで作成されたモデルはCaffeの形式で出力されているので、coremltools.converters.caffe.convertを使ってコンバートできます。

import coremltools

if __name__ == '__main__':
    coremlmodel = coremltools.converters.caffe.convert(
        ("caffemodel.binaryproto", "deploy.prototxt", "mean.binaryproto"),
        class_labels='labels.text',
        predicted_feature_name='classLabel',
        image_input_names='data',
        image_scale=1.0/255.0
    )

    coremlmodel.save("Muslce.mlmodel")

実は、ここのコンバートもハマりポイントでした。
コンバートしたファイルを、最後のcoremlmodel.save()でファイル名を指定して保存していますが、ここでファイル名をmodel.mlmodelとしてはいけません。
保存した後にファイル名をリネームすれば問題無いのですが、model.mlmodelのままで次の手順に行くと上手くいきません。
詳しい理由は次の章で。

.mlmodelファイルをXcodeプロジェクトにインポートする

それでは、先ほど保存した.mlmodelファイルをXcodeにインポートしましょう。
.mlmodelファイルも他ファイルと同じように、「Add Files to」でプロジェクトにインポートできます。

.mlmodelをインポートすると、XcodeがModel Classを自動生成してくれます。

自動生成されたコードを見てみると、70行目あたりにモデルのファイル名がそのままクラス名になっている部分があります。(この例では、Muscle.mlmodelをインポートしたのでMuscleというクラスが生成されています。)

もしも、model.mlmodelというファイル名でXcodeにインポートしてしまうと、modelというクラスが定義されてしまい、72行目のvar model: MLModelと定義が被ってしまうことになります。
もちろん、ビルドでコケます。

model.mlmodelなんて安直なファイル名にしてはいけません。(戒め)


以上です。
また時間がある時に、インポートしたモデルを使って画像認識アプリを作るところまで書きたいと思います。