こんにちは。技術研究所のYKです。

 

9/20(水)に公開されたiOS 11には、Core ML APIという、学習済みモデルをアプリに組み込んで利用できる機能が追加されています。
前々から自分が作った学習済みモデルを手元で使えたらなー、と思っていましたので、この機会にCore MLを試してみることにしました。

ちなみに、Appleが画像のクラス分類のサンプルとして以下のようなソースコードを提供してくれています。

今回はまずImage Classification with Vision and CoreMLを動かしてみて、それから私の手元にあるCaffeの学習済みモデルをiOSアプリに組み込んでみました。

環境

macOSのバージョン macOS High Sierra Version 10.13 Beta7
(試してみた時点では、まだ正式版にアップデートしていませんでした…)
Xcodeのバージョン Xcode 9.0
利用したデバイス iPhone 7(256GB, iOS 11)
利用したデータセット MNIST(手書き数字のデータセット)

サンプルを実機上で動かすまで

Image Classification with Vision and CoreMLには学習済みモデルが内包されているので、ビルドすれば実機上ですぐ動く状態になっています。
こちらのサンプルを動かしている記事は既に多数出ているので、本記事では詳細は省きます。

ちなみに、このプロジェクトをXcodeで読み込んだ際に3件だけエラーが出ましたが、いずれもSwift 4へのアップデートに伴いAPIが変更されたことに因るエラーのようで、全てfixボタンのクリックのみで修正することができました。

Caffeで作った学習済みモデルを利用するまで

Caffeで作った学習済みモデルをCore MLで利用する為には、coremltoolsを使ってmlmodelという形式に変換する必要があります。
coremltoolsはPython 2.7用のライブラリで、pip install coremltoolsでインストールできます。

今回は、このような流れで学習済みモデルを変換しました。

当方で作成していた学習済みモデルがcaffemodel形式ではなくHDF5形式であった為、一旦caffemodel形式に変換してからmlmodel形式に変換しています。

学習済みモデルの変換の流れ

  1. HDF5 ⇒ caffemodelへの変換
    当方の手元の学習済みモデルはHDF5形式で、caffemodel形式(Protocol Buffers)に一旦変換する必要があるようでした。 なのでこちら(stackoverflow)のように、学習済みモデルを一旦Caffeで読み込ませ、改めてcaffemodel形式で書き出しました。
import caffe
net = caffe.Net('/path/to/deploy.prototxt', '/path/to/caffemodel.h5', caffe.TEST)
net.save('/path/to/just.caffemodel')
  1. caffemodel ⇒ mlmodelへの変換
    coremltoolsのリファレンスを見たところ、caffemodelファイルだけで変換ができそうな感じでした。
    しかし当方の環境ではcaffemodelだけを渡すと入力層と出力層が不定になってしまって変換できなかったので、モデル構造の定義ファイル(prototxt形式)も一緒に渡して変換しています。
    とりあえずこちらを実行すれば、mlmodelファイルが生成されます。
import coremltools
# 実際に変換を行う部分
coremlmodel = coremltools.converters.caffe.convert(
("model.caffemodel", "model.prototxt"))
coreml_model.save("model.mlmodel")
# 変換成功時のログ
# caffemodel2mlmodel.pyは、上で示したソースコードが書かれたPythonスクリプト
python caffemodel2mlmodel.py model.caffemodel model.prototxt
================= Starting Conversion from Caffe to CoreML ======================
Layer 0: Type: 'Input', Name: 'data'. Output(s): 'data'.
Ignoring batch size and retaining only the trailing 3 dimensions for conversion.
Layer 1: Type: 'Convolution', Name: 'conv1'. Input(s): 'data'. Output(s): 'conv1'.
Layer 2: Type: 'Pooling', Name: 'pool1'. Input(s): 'conv1'. Output(s): 'pool1'.
Layer 3: Type: 'Convolution', Name: 'conv2'. Input(s): 'pool1'. Output(s): 'conv2'.
Layer 4: Type: 'Pooling', Name: 'pool2'. Input(s): 'conv2'. Output(s): 'pool2'.
Layer 5: Type: 'InnerProduct', Name: 'ip1'. Input(s): 'pool2'. Output(s): 'ip1'.
Layer 6: Type: 'ReLU', Name: 'relu1'. Input(s): 'ip1'. Output(s): 'ip1'.
Layer 7: Type: 'InnerProduct', Name: 'ip2'. Input(s): 'ip1'. Output(s): 'ip2'.
Layer 8: Type: 'Softmax', Name: 'prob'. Input(s): 'ip2'. Output(s): 'prob'.
================= Summary of the conversion: ===================================
Detected input(s) and shape(s) (ignoring batch size):
'data' : 1, 28, 28
Network Input name(s): 'data'.
Network Output name(s): 'prob'.

アプリに組み込んで使ってみる ⇒ 失敗…

作ったmlmodelファイルをXcodeのプロジェクトにAddすることで、学習済みモデルをソースコード側から利用できるようになります。

また、mlmodelにはソースコードが内包されており、このソースコード内に学習済みモデルを利用する為のクラスが実装されています。
このソースコードもcoremltoolsが生成してくれるので、非常に楽です。

しかし、上記の手順で作成したmlmodelには問題がありました。
以下の画像は、サンプルに同梱されていた学習済みモデルと、独自に作成した学習済みモデルのプロパティです。

これらを見比べてみると、inputs、outputsのTypeやパラメータの数が異なることが分かります。 特にinputsがImageになっていないところが問題で、Imageになっていない為に画像データが入力できない状態になっていました。

ソースコードではなくmlmodelの作り方が悪いのだろうと考え、mlmodelを作り直してみました。

mlmodelを作り直す ⇒ 成功!

適切なプロパティを設定する為に、改めてcaffemodel ⇒ mlmodelへの変換をやり直しました。
結論としては、coremltools.converters.caffe.convert()のオプションが適切に設定されていなかったことが原因だったようです。

以下のオプションを設定して再度変換し直したところ、iOSアプリ上で学習済みモデルを利用できるようになり、また、CaffeのPython APIを叩いて分類した際と遜色ない結果が得られることが確認できました。

オプション 概要
class_labels クラスラベルを改行区切りで記載したファイルのパスを指定
e.g. class_list.txt
image_input_names 入力層の名前を指定
モデル定義のprototxtから入力層の名称を調べ、設定
e.g. data
image_scale ピクセル値のスケーリングの設定
e.g. 1.0 / 255.0
predicted_feature_name 分類した結果に対して付けるパラメータ名を指定
e.g. classLabel
# before
# coremlmodel = coremltools.converters.caffe.convert(
# (caffemodel_path, prototxt_path))
# after
coremlmodel = coremltools.converters.caffe.convert(
(caffemodel_path, prototxt_path),
class_labels="class_list.txt",
predicted_feature_name="classLabel",
image_input_names="data",
image_scale=1.0 / 255.0)

感想

  • いくつかつまづいた箇所はありましたが、mlmodelファイルさえできてしまえば、非常に簡単に学習済みモデルを変換・利用することができました。
    また、学習済みモデルを入れ替える際はmlmodelファイルの入れ替えてソースコードを1行書き換えれば良いので、非常に楽だと感じました。
  • モバイルデバイスだけで学習済みモデルを使った画像のクラス分類を行えるので、デモとかに使うのにも良さそうです。