iOS11のCoreMLでMNISTをやってみた


## これはなに？

iOS11で機械学習(CoreML)をやってみたい方向けの初学者用メモ。備忘録。

## ここで押さえている内容

* PythonでKerasを使って.mlmodelを作成し、iPhoneの手書き文字で数字認識
* 写真から数字認識

内容はハックするだけで、特に説明はない。

## ポイント

* iPhoneがなくてもMacのシュミレータで試せる
* Xcode9が必要
  * [Apple Develop](https://developer.apple.com/download/)の無料アカウントで落とせるGM版で作業は可能

![スクリーンショット 2017-09-17 22.52.20.png](https://qiita-image-store.s3.amazonaws.com/0/35973/7e342f10-3809-83fb-e102-ecebbbb906a5.png "スクリーンショット 2017-09-17 22.52.20.png")

Xcodeのダウンロードはとても根気がいるので先に落としておくのがいい

## PythonでKerasを使って.mlmodelを作成し、iPhoneの手書き文字で数字認識

[COMPUTER VISION IN IOS – COREML+KERAS+MNIST](https://sriraghu.com/2017/07/06/computer-vision-in-ios-coremlkerasmnist/)の流れ通りにすることで.mlmodelが作れる

* ページ下部にgithubに全コードがあるので困ることはない  
[r4ghu/iOS-CoreML-MNIST](https://github.com/r4ghu/iOS-CoreML-MNIST)

### トラブルシューティング 
* [Support exports for Apple's new mlmodel format in Keras 2 #7007](https://github.com/fchollet/keras/issues/7007)  
coremltoolsをインストールするとv0.3.6(2017/9/16時点)が入る
keras==2.0.4ではなくkeras==2.0.6を入れること


* Xcodeで.mlmodelを認識
Xcodeでmlmodelを取り込んだ際は、Target Membershipにチェックを入れること
なかなか認識しなかったりはする
![スクリーンショット 2017-09-17 22.48.12.png](https://qiita-image-store.s3.amazonaws.com/0/35973/d92aa84d-ae5e-3395-6fac-1767d889c549.png "スクリーンショット 2017-09-17 22.48.12.png")

## こんな感じで動作

|<img width="150" alt="スクリーンショット 2017-09-16 19.35.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/35973/ef56a6d8-6bd4-6062-80a0-e306b1b6c2e0.png">|<img width="150" alt="スクリーンショット 2017-09-16 19.40.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/35973/1f0b28a0-4be0-91ce-1bff-8b01739b3bac.png">|

処理としては、DrawView.swiftで28x28のグレースケール画像を作成して、CoreMLに放り込むだけ。
.mlmodelの使い方を見ておけばそれで良さそう。


## 写真から数字認識

Appleの[Vision Framework: Building on Core ML
](https://developer.apple.com/videos/play/wwdc2017/506/)のページ下部にある「Image Classification with Vision and CoreML」からダウンロードする

<img width="411" alt="スクリーンショット 2017-09-17 22.55.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/35973/202f3ebb-ec0e-26e9-33d4-a5b97417272d.png">


* Swift4の変更によるエラーが出るのでコードの修正が必要。下記はパッチ

```diff:
@@ -43,13 +43,13 @@
         guard let ciImage = CIImage(image: uiImage)
             else { fatalError("can't create CIImage from UIImage") }
         let orientation = CGImagePropertyOrientation(uiImage.imageOrientation)
-        inputImage = ciImage.applyingOrientation(Int32(orientation.rawValue))
+        inputImage = ciImage.oriented(orientation)
 
         // Show the image in the UI.
         imageView.image = uiImage
 
         // Run the rectangle detector, which upon completion runs the ML classifier.
-        let handler = VNImageRequestHandler(ciImage: ciImage, orientation: Int32(orientation.rawValue))
+        let handler = VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
         DispatchQueue.global(qos: .userInteractive).async {
             do {
                 try handler.perform([self.rectanglesRequest])
@@ -105,18 +105,18 @@
         let bottomLeft = detectedRectangle.bottomLeft.scaled(to: imageSize)
         let bottomRight = detectedRectangle.bottomRight.scaled(to: imageSize)
         let correctedImage = inputImage
-            .cropping(to: boundingBox)
-            .applyingFilter("CIPerspectiveCorrection", withInputParameters: [
+            .cropped(to: boundingBox)
+            .applyingFilter("CIPerspectiveCorrection", parameters: [
                 "inputTopLeft": CIVector(cgPoint: topLeft),
                 "inputTopRight": CIVector(cgPoint: topRight),
                 "inputBottomLeft": CIVector(cgPoint: bottomLeft),
                 "inputBottomRight": CIVector(cgPoint: bottomRight)
             ])
-            .applyingFilter("CIColorControls", withInputParameters: [
+            .applyingFilter("CIColorControls", parameters: [
                 kCIInputSaturationKey: 0,
                 kCIInputContrastKey: 32
             ])
-            .applyingFilter("CIColorInvert", withInputParameters: nil)
+            .applyingFilter("CIColorInvert", parameters: [:])
 
         // Show the pre-processed image
         DispatchQueue.main.async {
```

## シュミレータで見てみる

実行するとシュミレータではこんな感じで動く、
使っている画像はシュミレータのSafariで落としたもの

<img width="150" alt="スクリーンショット 2017-09-17 22.04.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/35973/0f4d449e-9847-bf78-68e6-32ee689c0fa3.png">

DLページのPDFにあるとおり、Vision Frameworkが四角の部分を認識するので、加工を施し、CoreMLで認識の流れになっている


![スクリーンショット 2017-09-17 22.05.33.png](https://qiita-image-store.s3.amazonaws.com/0/35973/3a3e4684-3194-3c2d-9017-13cc4ca949d3.png "スクリーンショット 2017-09-17 22.05.33.png")

## Kerasで作ったモデルと比較

AppleからDL: 0.999993
Kerasで自作: 0.975051

Appleで落としたモデルのが高性能(ノ∀｀)アチャー

<img width="150" alt="スクリーンショット 2017-09-17 22.22.21x.png" src="https://qiita-image-store.s3.amazonaws.com/0/35973/0d5802bd-76a8-4ae6-b80e-96061423e3f8.png">


## Next Step

### Apple [Core ML](https://developer.apple.com/documentation/coreml)ページのサンプル

* ダウンロード先  
[Classifying Images with Vision and Core ML](https://developer.apple.com/documentation/vision/classifying_images_with_vision_and_core_ml)

<img width="200" alt="スクリーンショット 2017-09-18 7.50.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/35973/dcf30f05-a888-ad07-3cdb-2dee7ce8bf4d.png">

写真や静止画から分類、シュミレータに入っていた写真を利用

### Azureの[Custom Vision Service](http://www.customvision.ai/)

[Custom Vision Service](http://www.customvision.ai/)ではCoreML用に学習結果の出力ができる
また、リアルタイムに推論するコードも公開している。

* [Real time image classification](https://github.com/Azure-Samples/cognitive-services-ios-customvision-sample)
* [記事](https://azure.microsoft.com/en-us/blog/custom-vision-service-introduces-classifier-export-starting-with-coreml-for-ios-11/)

※　試すにはiOS11の入ったiPhoneが必要

![IMG_1533.PNG](https://qiita-image-store.s3.amazonaws.com/0/35973/5ab50a18-6c7e-7b68-bcbe-ba2e2b952643.png)
サンプルではフルーツを推論する学習結果が利用されている。
画像は手近にフルーツがないので適当な評価結果。

## 所感

機械学習の難しいことを知らなくてもやってみれば出来る感半端ない。
ひととおり流れをやると「ふーん、なるほどよくわからん」くらいにはなる