iOS
機械学習
DeepLearning
Swift
Keras

GestureAI ― Keras + CoreML + iOS 11を用いた RNNによるジェスチャー認識

はじめに

iOS11から登場したCoreMLフレームワークを使って、Kerasで実装したニューラルネットを動かしてみました。画像認識や自然言語処理のCoreMLのサンプルはあったのですが、iPhoneから取得したセンサーデータをRNN(Recurrent Neural Network、再帰型ニューラルネットワーク)で認識させた例がなかったようなのでトライしてみました。

以下の記事では、○・□・△・✕を描くジェスチャーの認識を行うRNNの学習とCoreMLでの活用について紹介します。

デモ

認識したジェスチャーに応じてHTTPリクエストを送ってSlackにメッセージを送る例 :

demo.gif

iOSアプリのスクショ :

真ん中のボタンを押している間(最大4秒)だけジェスチャーのセンサーデータを取得して、ボタンを話したらニューラルネットにより認識を行います。

screenshot-01.jpg screenshot-02.jpg screenshot-03.jpg

レポジトリ・学習済みモデル

  • akimach/GestureAI
    • Python2.7 + Jupyter Notebook + KerasによるRecurrent Neural Network(RNN)の実装
  • akimach/GestureAI-iOS
    • 学習済みのRNNモデルをCoreMLを使ってiOS上で動かすサンプルアプリ
  • GestureAI.mlmodel
  • GestureAI-Collector
    • ジェスチャーのセンサーデータを収集するiOSアプリ

開発環境

現段階(2017年9月)ではCoreMLを使う場合、ライブラリのバージョンを合わせることが重要で、基本的にcoremltools 0.6.3の依存パッケージのバージョンに倣いました。

  • macOS Sierra 10.12.6
  • Xcode 9.0
  • Python (2.7.10)
    • Python3.xには対応していません
  • numpy (1.13.1)
  • protobuf (3.1.0)
  • Keras (1.2.2)
    • バックエンド : TensorFlow (1.2.1)
  • Scikit-learn (0.18.0)
  • coremltools (0.6.3)

iOS11をターゲットとして機械学習モデルの学習と活用の流れ

それぞれのパートに該当するコードのリンクを添付しています。

機械学習モデルの学習と保存 :

  1. データセットの準備・整形・前処理 [link]
  2. 機械学習モデルの学習・性能評価 [Link]
  3. 機械学習モデルの保存 [Link]
  4. coremltoolsを使い保存したモデルをCore ML model形式(.mlmodel)に変換する [Link]

機械学習モデルのCoreMLによる利用 :

  1. .mlmodel形式のファイルを自分のプロジェクトに読み込む
  2. 機械学習モデルの仕様に応じて推論させるデータの整形を行う [Link]
  3. 機械学習モデルの推論結果を得る [Link]

データセット

データセットは○・□・△・✕・その他を描くジェスチャーの3軸の加速度をPhoneの加速度センサーにより取得してCSV形式のテキストとして保存したものを使いました。5クラス各200件で総件数は1,000件です。サンプリングレートは10Hz、最大取得秒数は4秒なので最大の系列長は40となります。なおジェスチャーには筆順を考慮しませんでした。

ラベル 説明 件数
0 Circle 200 circle.jpg
1 Rectangle 200 rectangle.jpg
2 Triangle 200 triangle.jpg
3 Cross 200 cross.jpg
4 Other 200

データの前処理

最大長が40の時系列データなので、長さが40未満のデータは0埋めを行いました。

timesteps = 40 # 最大系列長
input_dim = 3 # 入力次元
df = pd.read_csv('example.csv')
seq = df.as_matrix()
seq_length = seq.shape[0] # 対象となるデータの系列長
seq_0padding = np.r_[seq, np.zeros((timesteps-seq_length, input_dim))]

機械学習モデルの学習

グリッドサーチを使ってハイパーパラメータの剪定と性能評価を行いました。

# ハイパーパラメータの候補
param_grid = {
    "rnn_cell": ['LSTM', 'GRU'],
    "n_hidden": [128, 256, 512, 1024,],
}
model = KerasClassifier(build_fn=rnn, nb_epoch=epochs, batch_size=batch_size, verbose=1)
clf = GridSearchCV(estimator=model, param_grid=param_grid, cv=n_cv, scoring='accuracy')
# グリッドサーチの実行
res = clf.fit(X, y)
# 結果の表示
print(res.cv_results_)
# ベストなパラメータととその正答率
print("Hyper Parameters:", res.best_params_, "Accuracy:", res.best_score_)

グリッドサーチの結果、エポック数が50かつ4分割交差検証で、RNNセルがGRUセル、ノード数が512がベストとなり正答率は90.8%でした。

機械学習モデルの保存

学習済みのモデルをCoreML Model形式のファイルに変換する方法は2つあって、(1)モデルのオブジェクトか(2)ファイルパスのどちらかを渡して変換することができます。

import coremltools
# 保存先のパス
model.fit(...) # 学習済みのモデル

# (1) モデルのオブジェクトを渡す場合
coreml_model =coremltools.converters.keras.convert(model)
coreml_model.save('models/GestureAI.mlmodel')

# (2) ファイルパスを渡す場合
path = 'models/GestureAI.h5'
model.save(path)
coreml_model =coremltools.converters.keras.convert(path)
coreml_model.save('models/GestureAI.mlmodel')

CoreML Modelに情報を付加することもできます。他にも引数があるのでドキュメントを参照してみてください。

coreml_model.author = 'Hoge'
coreml_model.license = 'MIT'
coreml_model.short_description = 'My neural network'

CoreMLを使うためのXCodeの設定

学習済みモデルをまずXCodeのプロジェクトに読み込ませます。そのあと図1と図2のようにBuild Phases内にあるCompiles Sources+ボタンを押してxxx.mlmodelを追加します。そうすると、

001_1.png
図1 : Compiles Sourcesの追加1

002.png
図2 : Compiles Sourcesの追加2

そうすると、Swiftのモデルコードが自動で生成されるようになります(図3,4)。

003.png
図3 : モデルの情報
004.png
図4 : CoreML Modelから生成されたSwiftのコードの一部

あとは非常にシンプルです。CoreML Model形式のファイル名と同じクラス名のオブジェクトを生成して、prediction(...)に推論させるデータを渡してその結果を受け取ります。

let targetData = ... // 推論させるデータ
let gestureAI = GestureAI()
guard let output = try? gestureAI.prediction(input:
     GestureAIInput(input1: targetData)) else {
         fatalError("Unexpected runtime error.")
}
print(output)

私が用意したサンプルコードと学習済みモデルを使って実際に試してみてください。

$ git clone https://github.com/akimach/GestureAI-CoreML-iOS

躓いた点 : 入力データのShapeと次元に気をつける

今回はじめてCoreMLを使ってみて、入力データのShapeに気をつけなければいけないということに気づきました。

KerasでもTensorFlowでもいいのですが、一般的な機械学習ライブラリではモデルへの入力は、例えば画像だったら(バッチサイズ, 画像の高さ, 画像の幅, 画像のチャンネル数)のようになります。なので、1件だけのデータを推論させる場合でも1件のデータのみを持つ配列として入力させます。CoreMLの場合は1回の推論につき1件のデータを入力するようになっています。入力のShapeはXcodeからみれるモデルの情報にも記述されていますし、公式ドキュメントにも記述されています(引用 : For the purposes of this specification, batch dimension is ignored.)。

また、RNNに入力する系列データの場合、入力のShapeはバッチサイズ, 系列長, 入力次元のようになります。GestureAIだったらバッチサイズ, 40, 3です。最初、このShapeのままで学習させたらCoreMLでの入力Shapeは3だったので躓きました。入力の次元をフラットにしてあげる必要があるため、バッチサイズ, 40*3で入力されるデータをKerasのReshapeを使いバッチサイズ, 40, 3に変換してフィードフォワードさせるようにしました(以下、図5)。

shape.png
図5 : GestureAIのShape

発展

  • ちょっと複雑な剣技や魔法のアクションを認識させてゲームに使ってみたい(昔、ドラクエでロトの剣のゲームがあった)
  • iPhoneで取れるフィジカルデータを有効活用
  • アルファベットやひらがなの手書き文字ジェスチャーに挑戦

おわりに

Kerasで学習させた機械学習モデルをCoreMLで活用した例を紹介してみました。次回はCoreML+Visionにトライしてみたいと思います。

参考