TensorFlowで作ったモデルをCore MLで利用する

[tfcoreml](https://github.com/tf-coreml/tf-coreml)（リポジトリ名は "tf-coreml"）という、TensorFlowで学習したモデルを、Core MLモデル（`.mlmodel`）に変換するツールがあります。

TensorFlow LiteもCore MLモデルへの変換をサポートしているみたいな[記事](https://developers.googleblog.com/2017/12/announcing-core-ml-support.html)もありましたが、まだLiteがどういうものなのかよくわかってないのと、ググッてtfcoremlが一番上に出てきたし、メンテもアクティブにされてるようなのでこちらを試してみた次第です。


## tfcoremlのインストール

```
$ pip install -U tfcoreml
```

でいけます。

が、僕はしばらくTensorFlowを触ってなかったので、次のエラーに遭遇しました。

> Could not find a version that satisfies the requirement tensorflow>=1.1.0 (from tfcoreml) (from versions: )
No matching distribution found for tensorflow>=1.1.0 (from tfcoreml)

tfcoremlは**要TensorFlow 1.1.0以上**です。

というわけで最新版（2018.3.9現在、v1.6）にアップグレード。その手順は記事もたくさん出ているので省略します。

再度インストールを実行すると、

```
(tensorflow) $ pip install -U tfcoreml
```

無事入りました。

> Successfully installed coremltools-0.8 six-1.10.0 tfcoreml-0.1.1


ちなみに現状ではtfcoremlはpython v2.7版のみです。[3.x版はありません](https://pypi.python.org/pypi/tfcoreml/0.1.1)。


## 変換対象のモデルを用意する

READMEに

> frozen .pb graph file to be converted

とあるので、変換対象としてfrozenな`.pb`フォーマットのモデルファイルを用意します。

すぐに試せるものとして、READMEに

> Below is a list of publicly available TensorFlow frozen models that can be converted with this converter:

と、**公開されているTensorFlowのfrozenモデルのうち、本コンバータで変換可能なもののリスト**が掲載されています。

本記事では "Inception v3 (non-Slim)" を使うことにします [^1]。

[^1]: 「それ、Appleが配布してるのがもうあるよ」というツッコミがありそうなので念のため注釈入れておくと、Inception v3を使うのはあくまで記事用です。実際の目的としては自分たちがTensorFlowを使って独自に学習させたモデルを利用することです。

## 変換スクリプトを書く

READMEには

```python
import tfcoreml as tf_converter
tf_converter.convert(tf_model_path = 'my_model.pb',
                     mlmodel_path = 'my_model.mlmodel',
                     output_feature_names = ['softmax:0'],
                     input_name_shape_dict = {'input:0' : [1, 227, 227, 3]})
```

というサンプルが載っています。これを自分が変換したいモデルに合わせて書き換えるわけですが、本記事で使おうとしているInception v3のサンプルが、リポジトリ内の`tf-coreml/examples/inception_v3.ipynb`にあるので、これにしたがって書き換えます。

```python:converter.py
import tfcoreml as tf_converter

tf_converter.convert(tf_model_path = 'tensorflow_inception_graph.pb',
                     mlmodel_path = 'tensorflow_inception_graph.mlmodel',
                     output_feature_names = ['softmax/logits:0'],
                     input_name_shape_dict = {"Mul:0":[1,299,299,3]})
```

## 変換スクリプトを実行

```
(tensorflow) $ python converter.py 
```

> NotImplementedError: Unsupported Ops of type: DecodeJpeg. Kindly refer to the "examples/inception_v3.ipynb" notebook on how to strip input pre-processing from the TF graph before conversion to CoreML.

エラーが出てしまいました。input pre-processingをstripする方法が`examples/inception_v3.ipynb`にあるから参照しろと。

なんやかんや変換スクリプトを追加・修正して、`〜.mlmodel`が生成されるようになります。（修正後のスクリプトは諸事情により割愛）

あと、CoreML+Visionで使うときの入力の型をimageにするために、変換スクリプト内で呼んでいる`tf_converter.convert`で、引数`image_input_names`を追加する必要もありました。

- [Converting UIImage to MLMultiArray for Keras Model - Stack Overflow](https://stackoverflow.com/questions/44529869/converting-uiimage-to-mlmultiarray-for-keras-model)

## iOSで使う

こちらの記事に書きました:

- [Core ML＋Visionを用いた物体認識の最小実装 - Qiita](https://qiita.com/shu223/items/e200e379e34629018dca)


## トラブルシューティング: 変換実行時にImportErrorが発生する

こういうやつ：

> ImportError: dlopen({path-to-tensorflow}/lib/python2.7/site-packages/tensorflow/python/pywrap_tensorflow_internal.so, 6): Symbol not found: _PyUnicodeUCS2AsASCIIString

詳細は別記事に書きました：

- ["Symbol not found: _PyUnicodeUCS2_AsASCIIString"エラーの解決法 - Qiita](https://qiita.com/shu223/items/0e9b5236d565907d5a4a)


## 関連記事

- [iOSの機械学習フレームワークの比較 - Core ML / Metal Performance Shaders (MPSCNN) / BNNS - Qiita](https://qiita.com/shu223/items/33774d896b7142fcfc1c)