iOSでMLKitのカスタムモデル(MobileNetをfine-tuningして量子化)を試してみた

iOSで気軽に画像分類をやってみたかったので試してみました。
fine-tuning後のモデルはデバイス上でうまく動作しなかったので、
その直前まで参考になれば幸いです。
tf.kerasの.hdf5から.tflteをつくり量子化したのですがそこでどうやらうまく行ってないようです。


#環境
GPU:GTX Geforce 1080Ti
OS:Ubuntu 16.04 LTS
iOS:12.1

#OSでMLKitを動かす
### サンプルアプリを動かす
[ML Kit を使用して推論に TensorFlow Lite モデルを使用する（iOS）](https://firebase.google.com/docs/ml-kit/ios/use-custom-models?hl=ja)
ここのサンプルを使ってもいいのですが、iOSは初めて触るので
ファイル構成が私には難しかったので
カスタムモデルの読み込みが上手く行きませんでした。

そこで
[ML Kit on iOS and how it performs against Core ML](https://blog.xmartlabs.com/2018/06/21/ML-Kit-CoreML/)
のサンプルを使いました。
こちらの構成のほうがシンプルなのでわかりやすかったです。
最初にビルドするとエラーが何個か出ますが、ポチポチfixボタンを押していけば大丈夫です。
一箇所だけコードの修正が必要な場所が81行目の


```swift:CustomModelViewController.swift
let options = ModelOptions(
    cloudModelName: Constants.Models.cloudMobilenet,
    localModelName: Constants.Models.localMobilenet
)
interpreter = ModelInterpreter(options: options)
```
を

```swift:CustomModelViewController.swift
let options = ModelOptions(
    cloudModelName: Constants.Models.cloudMobilenet,
    localModelName: Constants.Models.localMobilenet
)
interpreter = ModelInterpreter.modelInterpreter(options: options)
```
に修正する必要があります。
アップデートで書き方が変わったのかな？


### クラウドモデルの登録をして動かす
[Firebase Console](https://console.firebase.google.com/u/0/)
からプロジェクトを追加します。
<img width="1440" alt="スクリーンショット 2019-01-08 14.43.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/0878a3c7-10ef-56e6-f537-6027759bf498.png">
ここではMLkitTestとしました
<img width="522" alt="スクリーンショット 2019-01-08 14.47.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/eadcfcbe-8e9f-2b97-c031-285b2fcdc1f7.png">
iOSを追加するためにボタンを選択し
<img width="880" alt="スクリーンショット 2019-01-08 14.48.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/9f69298f-5eeb-70a1-6a92-dfd5bc8702df.png">
この画面に移動します。
このiOSバンドルIDには
<img width="632" alt="スクリーンショット 2019-01-08 14.49.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/bdfeb3e4-3a4b-c5ad-14cd-3cca1741820c.png">
このBundle Identiferを入力します。
<img width="912" alt="スクリーンショット 2019-01-08 14.50.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/648c7531-1999-cfdd-555f-0d85e191a43c.png">

そのあとは画面に従ってGoogleService-Info.plist ファイルを Xcode プロジェクトのルートに移動します。
<img width="756" alt="スクリーンショット 2019-01-08 14.53.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/131debfe-be99-7273-4bca-c9e2765182ec.png">
そしてここからカスタムモデルをクラウド上に追加します。
<img width="1465" alt="スクリーンショット 2019-01-08 14.56.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/138063/c5502113-863e-cf16-3b2e-58d332016740.png">

ここに.tfliteが置いてあるので試しにアップロードすると簡単に試せます。
[List of Hosted Models](https://www.tensorflow.org/lite/models
)

#custom モデルを作る
KerasでMobileNetをファインチューニングして、サイズを落としてスマホで実行してみます。
###mobile net のfine tuning を行う
[Keras学習済みモデルをFine-tuningさせて精度比較](https://qiita.com/ha9kberry/items/daa2fc330c71485b2c27#mobilenet)
上記を参考にさせていただきました！
ここのモデルはうまく動作しています。

###.hdf5 から .tfliteに変換する
変換するツールがtensorflowにありました。
まだあまり使われてないようでググってもヒットがすくなかったです。

```python
import tensorflow as tf
# Convert to TensorFlow Lite model.
converter = tf.contrib.lite.TFLiteConverter.from_keras_model_file(model_dir + 'model_MobileNet.hdf5')
tflite_model = converter.convert()
open("converted_model.tflite", "wb").write(tflite_model)
```
しかし、参考のまま（コピペのまま）モデルを変換しようとするとエラーがでます。

なぜだろう？？とググっていると
同じような症状にあたったというブログが！

[みらいテックラボ:金魚って見分けられる? (3)](http://mirai-tec.hatenablog.com/entry/2018/11/04/000419)
どうやらモデルの構造がおかしいから変換がうまくいかない様子。

上記をヒントにいろいろ試行錯誤すると
どうやらinput_tensorの形が指定されていなかったので、
察しの悪いコンバータが困り果てていたようです。
そこで、ベースモデルを呼び出すときに指定します。

具体的には

```python
base_model = MobileNet(
    include_top = False,
    weights = "imagenet",
    input_shape = None
)
```
の部分を

```python
# ここが大事だったようだ
input_tensor = Input(shape=(image_size, image_size, 3))

base_model = MobileNet(
    include_top = False,
    weights = "imagenet",
    input_shape = None,
    input_tensor = input_tensor
)
```
とします。
するとコンバートがうまくいきました！
やったね

###量子化する
さて上記のモデルをスマホで実行しようとしたところ
アプリが落ちてしまいました。
どうやら重くて動作ができないようです。
そこで量子化を試みます。

量子化する方法をいろいろググっていると、
特別に他のツールを必要とせず、オプショを指定すればいいようでした。
（ドキュメントが整備されていなくて分かりづらい）
以下のコマンドを実行すると

```python
import tensorflow as tf
converter = tf.contrib.lite.TFLiteConverter.from_keras_model_file(model_dir + 'model_MobileNet.hdf5')
converter.post_training_quantize = True
tflite_quantized_model = converter.convert()
open("quantized_model.tflite", "wb").write(tflite_quantized_model)
```
17MBもあったモデルが4MBに圧縮できました！

これをスマホで実行しましょう！
....
....
....
動かない。。。。なぜだ。。。

#最後に
なんで動かないか調べいていくと、直近でこんな記事を書いている方が！
[KerasのMobileNetモデルをTensorFlow Liteモデルファイルに変換できるように書き換える](https://qiita.com/tfkeras/items/2677fd493da2b3acbc7b)
でも私の場合は変換自体は出来ているので、この方とは症状が違うような。。。？

時間切れなので今回はここまで

###所感とこれからやりたいこと
iOSでのMLKitのサンプルが少なくて困りました。
インド人youtuberがポツポツ解説してくれててるくらいで、参考になる資料が少ない印象です。
これはまだMLKiｔがベータ版で実運用されてないからでしょう。

iOS,Androidともに機械学習しかやってなかった私には難しかったです。
ファイル構成や、エラーの読み方、文法やコメントがぜんぜん違うので慣れる必要があります。

2019年はdeep learning はエッジでの運用が加速すると言われています。
しかし、組み込みでの開発はコストがかかるので、
Android Thingsなどのプラットフォームを利用した開発が進むと思われます。
deep learningに携わっている人もAndroidの知識が必要になるかもしれません。
iOSは出遅れてるかもしれないので、Androidを重点的にやろうと思います。

今後は
fine-tuningしたモデルをきっちりスマホで動かしたいです。
[iPhone/Androidアプリ開発者のための機械学習・深層学習 実践入門](https://www.amazon.co.jp/dp/4862464386/?coliid=I1LMMBOCEMU1JZ&colid=164LQM9LYVRCY&psc=0&ref_=lv_ov_lig_dp_it)
という書籍が2019/1/26に発売されるのでそれを使って学習していきたいと思います。