2018.09.20
機械学習フレームワークTuri Createを使ってアクティビティを判別する
こんにちは。F.S.です。
今回はAppleが提供している機械学習フレームワークTuri Createで、モーションセンサーのインプットをもとに身体的なアクティビティを判別するActivity classificationを試してみます。
Activity classification · GitBook より抜粋
ユーザーガイドにしたがって、iPhoneの加速度センサーとジャイロセンサーの値をもとに「歩いてる」とか「階段を上っている」などの動作を推定するプログラムを動かしてみます。
- 開発環境
- MacBook Air (13-inch, 2017)
- macOS 10.13.4
- Python 3.6.5(Anaconda 5.2)
- Turi Create 5.0
- Xcode 9.3.1
- 実機テスト環境
- iPhone 7
- iOS 11.3
Turi Createの概要
Turiはもともと機械学習のプロダクトを手がけるシアトルのスタートアップで、Appleにより2016年に買収されました。Turi Createは彼らのプロダクトをベースにAppleがOSSとして公開したものです。
- GitHub – apple/turicreate
https://github.com/apple/turicreate
WWDC2018にてデモンストレーションが実施されています。
- A Guide to Turi Create
https://developer.apple.com/videos/play/wwdc2018/712/
Turi Createの特徴は、アルゴリズムよりもタスクベース(レコメンドや物体検出など)のモジュールを使って手短に学習モデルを作成できるというところにあります。
また、Appleから提供されているだけあってCore MLフォーマットへのエクスポートを標準でサポートしており、iOS等で容易に利用することができます。
(Turi CreateそのものはCore ML専用という訳ではなく、Pythonが動くx86_64アーキテクチャの環境であれば利用可能です)
なお、Core MLは機械学習モデルをアプリで利用するためのライブラリで、iOS 11から導入されたものですね。
- Core ML | Apple Developer Documentation
https://developer.apple.com/documentation/coreml
Activity Classification
冒頭にもあったように、Activity Classificationはモーションセンサーで取得したデータをもとにあらかじめ定義されたアクティビティ(止まってる、動いてるまたはジェスチャーなど)に振り分ける分類器です。内部的にはLSTMネットワークを用いて時系列データの依存関係でアクティビティを判別しているようです。
Activity classification · GitBook より抜粋
LSTMについてはこちらを参考ください
- Understanding LSTM Networks
http://colah.github.io/posts/2015-08-Understanding-LSTMs/ - LSTMネットワークの概要(↑の日本語訳)
https://qiita.com/KojiOhki/items/89cd7b69a8a6239d67ca
実装
こちらのAppleのガイドをもとに進めていきます。
- Activity classification · GitBook
https://apple.github.io/turicreate/docs/userguide/activity_classifier/
APIの詳細が知りたい場合はこちらのドキュメントを参照ください。
- Turi Create API Documentation
https://apple.github.io/turicreate/docs/api/index.html
モデル作成
基本、ガイド通りですので詳細は記載しませんが、大まかな流れは下記の通りです。
1. 加速度センサー、ジャイロセンサーのデータをSFrameにロードし、必要なデータに整形する
SFrameはTuri Createの表形式のデータフレームオブジェクトです。CSV、テキスト、JSONなどからデータを読み込むことができ、列指向のため列の追加削除が容易に行えます。
今回扱うデータは30人の被験者、合計61の計測(セッション)データで、1セッションの中にいくつかのアクティビティ(歩く、座る、階段を登るなど)がラベリングされています。データのアクティビティは12種類ありますが、ガイドではそのうち6種類のみをフィルタリングして使用しています。計測データは1セッションあたり1,500〜2,000フレーム、50fpsのため30〜40秒の活動記録となります。
本ガイドにおける最終的なSFrameの内容は次のようなものになります。
2. activity_classifierモジュールのユーティリティメソッドでSFrameのデータを学習用(train)、テスト用(test)に分ける
train, test = tc.activity_classifier.util.random_split_by_session(data, session_id='exp_id', fraction=0.8)
exp_idは一連のActivityを識別するIDです。
fraction=0.8により、SFrameのデータの80%をtrainデータ、残りをtestデータとして分割します。
3. activity_classifierモジュールの作成メソッドでtrainデータからActivityClassifierモデルを作成する
model = tc.activity_classifier.create(train, session_id='exp_id', target='activity', prediction_window=50)
1秒間隔で予測するために、元データが50fpsなのでprediction_window=50を指定します。
実行結果
The dataset has less than the minimum of 100 sessions required for train-validation split. Continuing without validation set Pre-processing 589440 samples... Using sequences of size 1000 for model creation. Processed a total of 48 sessions. Using CPU to create model +----------------+----------------+----------------+----------------+ | Iteration | Train Accuracy | Train Loss | Elapsed Time | +----------------+----------------+----------------+----------------+ | 1 | 0.630 | 0.974 | 1.6 | | 2 | 0.800 | 0.553 | 3.2 | | 3 | 0.850 | 0.418 | 4.4 | | 4 | 0.867 | 0.363 | 5.6 | | 5 | 0.883 | 0.323 | 6.8 | | 6 | 0.896 | 0.279 | 7.9 | | 7 | 0.903 | 0.262 | 9.1 | | 8 | 0.916 | 0.230 | 10.3 | | 9 | 0.920 | 0.215 | 11.5 | | 10 | 0.926 | 0.197 | 12.7 | +----------------+----------------+----------------+----------------+ Training complete Total Time Spent: 12.6999s
4. ActivityClassifierモデルの評価メソッドでtestデータを用いてモデルを評価する
metrics = model.evaluate(test) print(metrics['accuracy'])
精度は次の結果が得られました。
0.890511178490998
Core MLモデルへのエクスポート
モデルのエクスポートメソッドでCore MLフォーマットに書き出します。
model.export_coreml('MyActivityClassifier.mlmodel')
作成したモデルをXcodeで開くと、次のようなCore MLモデルになっているのがわかります。
アルゴリズムを気にせず、とはいえLSTMのhidden state(ワーキングメモリ)、cell state(長期記憶領域)がモデルのインターフェースに含まれています。LSTMとはなんぞやくらいの知識は持っていたいですね。実際はhiddenOut, cellOutを次の予測のhiddenIn, cellInに渡してあげるだけなんですけど。
iOSアプリの実装
作成したCore MLのモデルを使い、iOSアプリで利用してみます。ここからXcodeでの実装になります。
ガイドの実装は解説を簡単にするために加速度センサーしか取得していませんが、それだとまともな結果が得られなかったので、モデルの入力値である加速度+ジャイロのデータが使えるように下記のように一部のコードを変更します。
50fpsで加速度センサー、ジャイロセンサーの値を参照し、1秒(50フレーム)ごとに予測結果をデバッグコンソールに出力するだけのプログラムです。
override func viewDidLoad() { super.viewDidLoad() motionManager.accelerometerUpdateInterval = TimeInterval(ModelConstants.sensorsUpdateInterval) motionManager.gyroUpdateInterval = TimeInterval(ModelConstants.sensorsUpdateInterval) // モーションデータをプッシュ型ではなく、プル型で加速度・ジャイロを同時に処理するためにタイマーを使う motionManager.startAccelerometerUpdates() motionManager.startGyroUpdates() timer = Timer.scheduledTimer(timeInterval: ModelConstants. sensorsUpdateInterval, target: self, selector: #selector(ViewController.update), userInfo: nil, repeats: true) } // ガイドのaddAccelSampleToDataArrayに相当する部分 @objc func update() { guard let accelerometerData = motionManager.accelerometerData else {return} guard let gyroData = motionManager.gyroData else {return} guard let dataArray = predictionWindowDataArray else { return } dataArray[[0 , currentIndexInPredictionWindow ,0] as [NSNumber]] = accelerometerData.acceleration.x as NSNumber dataArray[[0 , currentIndexInPredictionWindow ,1] as [NSNumber]] = accelerometerData.acceleration.y as NSNumber dataArray[[0 , currentIndexInPredictionWindow ,2] as [NSNumber]] = accelerometerData.acceleration.z as NSNumber dataArray[[0 , currentIndexInPredictionWindow ,3] as [NSNumber]] = gyroData.rotationRate.x as NSNumber dataArray[[0 , currentIndexInPredictionWindow ,4] as [NSNumber]] = gyroData.rotationRate.y as NSNumber dataArray[[0 , currentIndexInPredictionWindow ,5] as [NSNumber]] = gyroData.rotationRate.z as NSNumber currentIndexInPredictionWindow += 1 if (currentIndexInPredictionWindow == ModelConstants.predictionWindowSize) { let predictedActivity = performModelPrediction() ?? "N/A" print(predictedActivity) currentIndexInPredictionWindow = 0 } }
結果
iPhoneを一定のリズムで上下させてみた際のコンソール出力結果です。
N/A laying laying laying laying laying laying laying laying laying laying laying climbing_upstairs climbing_upstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_upstairs climbing_upstairs climbing_upstairs climbing_upstairs climbing_upstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_downstairs climbing_upstairs climbing_upstairs laying laying laying
最初は動きがない状態(laying)から階段を登る動作に移っていると判別されています。
意識的に上方向に向かうように上下させたときにupstairs、逆に下方向でdownstairsとなるところが、それらしさを感じました。
まとめ
Turi CreateのActivity Classificationをガイドに沿って実装し、Core MLモデルを実機で動かしてみました。アルゴリズムやパラメータを選択することなく、やりたいことに適合するモジュールを使って学習モデルを作成するというTuri Createの特徴を理解することができました。
モデルの作成は簡潔に実現できましたが、データセットができるまでのデータ整形作業がコードの大部分を占めているというのは機械学習の宿命ですかね。
それでは、また。
次世代システム研究室では、アプリケーション開発や設計を行うリードエンジニアを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD