【CreateML】画像分類モデルを作成して、bravesoftキャラクター画像分類アプリ作成してみた！

初めまして！
bravesoft株式会社でiOSエンジニアをしている永井です。

最近、Visionフレームワークに触る機会があり、機械学習機能は「面白い！」と個人的になりました。もっと追求するべく、Appleの機械学習から画像分類というのを見つけたので今回は「CreateML」についてのモデル作成からアプリの組み込み/実装までの記事となります。

この記事では、bravesoftの自社開発にあるキャラクターを分類していきます。

## この記事で得られるもの
・CreateMLの基礎知識
・画像分類モデルについて
・モデルの作成方法
・モデルのアプリ組み込み


## CreateMLとは
macOS向けに開発された機械学習モデル構築フレームワークです。GUIベースのツールで、テキスト分類や画像分類や音声分類などのタスクを簡単に実行できます。Swiftとのシームレスな統合が特徴で、最適化されたモデルを生成し、Core MLでの統合も容易で作成したアプリに簡単に組み込むことが可能です。

公式サイト
https://developer.apple.com/jp/machine-learning/create-ml/

## Image Classifier(画像分類)モデルとは
「Image Classifier(画像分類)」とは画像を認識する機械学習モデルです。
画像を与えると、その画像のカテゴリ ラベルが返されます。
![スクリーンショット 2024-06-18 11.54.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/dce5e13b-f090-76fe-bea8-b254135ad57c.png)

画像分類をトレーニングするには、キリンやゾウなどの写真を収集することで動物を認識するようにトレーニングすることにより精度を上げていきます。
![スクリーンショット 2024-06-18 11.56.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/64fa9559-235d-5121-4d9a-1fe7fa6e4956.png)

上記のトレーニングにより、モデルの作成ができます。




## 今回用意するトレーニングデータ
![スクリーンショット 2024-06-17 19.54.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/e4b0e567-8654-d098-dea2-0f4bd4d2a303.png)

```
**フォルダ構成**
BraveCharacterTrainingData
　ー ボケイヌ
　　｜
  ------- 1~16のボケイヌ画像データ
　ー ホンネコ
　 　|
　 　------- 1~16のホンネコ画像データ
```

   :::note warn
警告
上記のようなフォルダ構成でないと上手くトレーニングできないので構成を守ってください。また、分類したい物毎にフォルダを作成してください。「ボケイヌ」と「ホンネコ」のフォルダ名がラベルになります。
:::



## MLの作成手順

### 手順1 - Image Classifierプロジェクトを作成する
![スクリーンショット 2024-06-18 12.14.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/ae6f77fb-9dca-838a-d18f-50112a0aa4a8.png)
Xcode起動 → 上のメニュー「Xcode」 → Open Developer Tool → Create MLをクリックします。
<br>

![スクリーンショット 2024-06-18 12.19.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/64c3558b-0232-7709-33d7-88be4e2a750f.png)
上記の画像にある、「New Document」をクリックします。
<br>

![スクリーンショット 2024-06-18 12.30.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/b901742b-cce0-4c2d-eda2-f2a1151631c0.png)
Allカテゴリから、「Image Classification」を選択し、「Next」を押しましょう。
<br>

![スクリーンショット 2024-06-18 12.35.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/7e4cfed7-5a61-75aa-71d9-e5510138e413.png)
ProjectNameを入力しましょう。今回は、「BraveCharacterImageClassifier」という名前にします。Author,License,Descriptionは任意です。入力できたら、「Next」で次にいきます。
<br>

![スクリーンショット 2024-06-18 12.40.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/100d7b5a-fe3d-7d53-1dee-74f1a33ddd6e.png)
プロジェクトファイルを作成すると上記の画面が表示されます。これでImage Classifierプロジェクトの作成は完了です。次はトレーニングをしていきます。

### 手順2 - 実際にトレーニングをする
![スクリーンショット 2024-06-18 12.49.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/7a4b0256-a079-7cee-8a45-ad186db8fc07.png)
Settingsタブ - DataのTraining Dataの➕ボタンをクリックしてください。
そして、今回用意したトレーニングデータフォルダを選択します。
<br>

![スクリーンショット 2024-06-18 12.52.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/8918b413-cab6-5e46-53b9-d09ddd4b3bb6.png)
トレーニングデータを入れると上記のようにItem件数が表示されます。
<br>

![スクリーンショット 2024-06-18 12.57.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/3bb901d8-55cf-80e6-fff1-10c9a76be9b8.png)
トレーニングデータがセットされた状態で「Train」ボタンを押します。
トレーニングが完了するまで待ちましょう。
<br>

![スクリーンショット 2024-06-18 15.21.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/274fe52c-fa85-20d5-93c7-9011c515aaa4.png)
上記のようにTrainingタブではトレーニング後の認識できた画像件数と精度が記載されます。


#### トレーニングしたモデルで評価してみよう
![スクリーンショット 2024-06-18 15.30.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/8a29e6bc-8c5f-29e9-7c29-ca4abeeb90f2.png)
EvaluationタブでTestingDataにトレーニングデータと同じフォルダ構成でテストデータフォルダを用意して追加します。
<br>

![スクリーンショット 2024-06-18 15.43.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/1f70d9ae-f04a-7de1-6536-4ef3a17719fb.png)
Metricsタブでは、様々な下記情報見ることできます。

<table>
  <caption>Metrics</caption>
  <thead>
    <tr>
      <th>名前</th> <th>説明</th>
    </tr>
  </thead>
  <tr>
    <td> Class </td> <td>モデルが分類するカテゴリ</td>
  </tr>
  <tr>
    <td> Count </td> <td>テストデータの各クラスの合計枚数</td>
  </tr>
  <tr>
    <td> Correct </td> <td>モデルが正しく分類された合計枚数</td>
  </tr>
  <tr>
    <td> False Positives </td> <td>偽陽性 - モデルがクラスとして誤って分類してしまった合計枚数</td>
  </tr>
  <tr>
    <td> False Negatives </td> <td>偽陰性 - モデルがクラスとして分類すべきだったが誤って他のクラスに分類した合計枚数</td>
  </tr>
  <tr>
    <td> Precision </td> <td>適合率 - モデルがそのクラスとして予測したうち、実際にクラスに属する画像の割合</td>
  </tr>
  <tr>
    <td> Recall </td> <td>再現率 - そのクラスに属する画像のうち、モデルが正しく予測した割合</td>
  </tr>
   <tr>
    <td> F1 Score </td> <td>スコアで0から1の範囲で表示され、1が最高スコア</td>
  </tr>
</table>

Exploreタブでは、結果をフィルタリングして対象の画像を見ることができます。

![スクリーンショット 2024-06-18 16.00.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/7f08067f-4c5f-815e-cc32-32038223ff77.png)


#### トレーニングしたモデルでプレビューしてみよう
**方法1**
画像を直接アップする方法
![スクリーンショット 2024-06-18 16.07.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/cd239458-e41f-1a7a-5636-4ac1736a231b.png)
左下の➕ボタン→ Add filesで直接画像をアップできます。
アップが完了すると、クラス分類を瞬時にしてくれます。


**方法2**
Live PreviewでMacのカメラを通す方法
![スクリーンショット 2024-06-18 16.13.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/8d466097-610b-7677-4b83-941eeeeaa818.png)
リアルタイムでカメラに画像分類させたいものを映すことで、その場でクラス分類してくれます。



### 手順3 -  作成したモデルを保存する
![スクリーンショット 2024-06-18 16.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/cbf7a0b6-5d6c-02e6-c2ec-dd0155ea44c6.png)

Class Labelsに分類予定のものが記載されていることを確認して、添付の赤枠部分「Get」を押してモデルを保存しましょう。これでモデルの作成は完了しました。続いて、モデルをアプリに組み込んでいきましょう。


## MLモデルのアプリ組み込み
今回は、[Classifying Images with Vision and Core ML](URL "https://developer.apple.com/documentation/coreml/model_integration_samples/classifying_images_with_vision_and_core_ml")のサンプルアプリを使用して、組み込んでいきます。まずは、上記のリンクからサンプルをダウンロードしてください。


![スクリーンショット 2024-06-18 16.24.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/4cdc7899-48e8-49c4-0d94-7be325c028bb.png)
Xcodeでダウンロードしたプロジェクトを開くと、上記と同じようになります。
Modelsディレクトリがあるので、そこに先ほど作成したモデルをドラック&ドロップで組み込んでいきます。
<br>

![スクリーンショット 2024-06-18 16.27.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/d59e294c-e024-431e-ec14-c9264da739dd.png)
既存にある「MobileNet」の下あたりにドロップして、モデルを入れてください。
<br>
![スクリーンショット 2024-06-18 16.37.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/06ac8a1a-cc73-3c4d-fb69-df19f9184e24.png)

上記のようになれば、組み込み完了で準備は整いました。
次は実際にサンプルアプリで動かしていきます。


## サンプルアプリで試してみよう！(UIKit)
先ほどサンプルアプリを公式からダウンロードしたプロジェクトを使います。
ImagePredictor.swiftファイルを選択して、下記コードを探してコードを変更してください。
```
// 既存のモデル組み込み
let imageClassifierWrapper = try? MobileNet(configuration: defaultConfig)
↓
// 作成したモデルを組み込み
let imageClassifierWrapper = try? BraveCharacterImageClassifier(configuration: defaultConfig)

```
変更後に、ビルドしてみましょう！

### 実機画面
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/e1ecb05e-29dc-6582-5159-dabf350b1996.png" width="350" ><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/cd140f7c-d322-5b98-7c5a-e2f2774b9aea.jpeg" width="350" >
こんな感じでボケイヌとホンネコの画像分類ができましたね！



## SwiftUIでリアルタイムに画像分類できるアプリを作成しよう！
先ほどのサンプルはUIKitだったので、SwiftUIではトレーニングモデルのプレビューと同様にLive PreviewでiPhoneカメラを使ったリアルタイム画像分類アプリを作成を紹介します。

#### ファイル構成
![スクリーンショット 2024-06-18 17.18.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/df0583b2-c607-3ab2-7113-45db53d02e41.png)

作成したモデルはMLModelsディレクトリに入れてください。

#### ContentView
```swift:ContentView
import SwiftUI

struct ContentView: View {
    @State private var classificationLabel: String = ""
    
    var body: some View {
        VStack {
            CameraView(classificationLabel: $classificationLabel)
                .edgesIgnoringSafeArea(.all)
            Text(classificationLabel)
                .padding()
                .background(Color.white)
                .cornerRadius(10)
                .shadow(radius: 10)
        }
    }
}
```

#### CameraView
カメラプレビューを表示させるmakeUIViewControllerの処理は割愛します。
```swift: CameraView
import SwiftUI
import AVFoundation
import Vision
import CoreML

struct CameraView: UIViewControllerRepresentable {
    // 画像分類結果のラベル
    @Binding var classificationLabel: String
    
    class Coordinator: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
        var parent: CameraView
        var imageClassifierVisionModel: VNCoreMLModel?
        
        init(parent: CameraView) {
            self.parent = parent
            
            // CoreMLモデルのロード
            let defaultConfig = MLModelConfiguration()
            let imageClassifierWrapper = try? BraveCharacterImageClassifier(configuration: defaultConfig)
            guard let imageClassifier = imageClassifierWrapper else {
                fatalError("App failed to create an image classifier model instance.")
            }
            let imageClassifierModel = imageClassifier.model
            self.imageClassifierVisionModel = try? VNCoreMLModel(for: imageClassifierModel)
        }
    
        // カメラフレームがキャプチャされたときに呼び出されるデリゲートメソッド
        func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
            guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
                  let imageClassifierVisionModel = imageClassifierVisionModel
            else { return }
            
            // 画像分類リクエストを作成
            let request = VNCoreMLRequest(model: imageClassifierVisionModel) { (request, error) in
                if let results = request.results as? [VNClassificationObservation] {
                    if let firstResult = results.first {
                        let confidence = firstResult.confidence
                        let identifier = firstResult.identifier
                        DispatchQueue.main.async {
                            self.parent.classificationLabel = "\(identifier) (信頼度: \(Int(confidence * 100))%)"
                        }
                    }
                }
            }
            
            // リクエストハンドラを作成して画像分類を実行
            let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
            try? handler.perform([request])
        }
    }
```

captureOutputに画像分類のリクエストを投げることでフレーム毎に分析することができます。これにより、分析結果がClassificationLabelに文字列が入っていきます。
全体のコードは以上になるのでビルドしてみましょう！


### 実機画面
![Videotogif (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796594/ebc1c85e-b38d-fcd8-0998-e6578e63b468.gif)



このようにSwiftUIでリアルタイム画像分析できましたね！
以上になります。お疲れ様でした。

## 終わりに
今回の記事では、CreatMLでの画像分類モデルの作成からアプリの組み込み、実際の使用例などを紹介させていただきました。

画像分類モデル以外にも、ビデオや音声など様々な機械学習ができるようにAppleが提供しているので他にも試してみたいですね。

機械学習によるオンデバイスAPI「Vision」「Natural Language」「Speech」「Sound Anlysis」と幅広くあり、どれもワクワクするような技術とAPIによってはアプリをさらに強化してくれるAPIが揃っているので是非、試しに作ってみてください！
#


<br>
bravesoftではiOSアプリやAndroidアプリの開発を行っています。
アプリ開発に興味がある方は是非是非、採用ページをご確認ください👀

https://recruit.jobcan.jp/bravesoft/show/b001/386578

https://www.bravesoft.co.jp/

## 参考資料

https://developer.apple.com/jp/machine-learning/create-ml/

https://developer.apple.com/jp/machine-learning/api/

https://developer.apple.com/jp/documentation/createml/

https://developer.apple.com/documentation/createml/creating-an-image-classifier-model


https://developer.apple.com/documentation/coreml/model_integration_samples/classifying_images_with_vision_and_core_ml

## アプリ

**AppleStore**

https://apps.apple.com/jp/app/%E5%86%99%E7%9C%9F%E3%81%A7%E4%B8%80%E8%A8%80%E3%83%9C%E3%82%B1%E3%81%A6-bokete-%E9%9D%A2%E7%99%BD%E3%83%8D%E3%82%BF%E3%82%92%E4%BD%9C%E3%82%8B%E5%A4%A7%E5%96%9C%E5%88%A9%E3%82%A2%E3%83%97%E3%83%AA/id563446587

https://apps.apple.com/jp/app/honne-%E6%9C%AC%E9%9F%B3%E3%81%8C%E8%A8%80%E3%81%88%E3%82%8B%E5%8C%BF%E5%90%8D%E3%81%A4%E3%81%B6%E3%82%84%E3%81%8D%E3%82%A2%E3%83%97%E3%83%AA/id899538562


**GooglePlayStore**

https://play.google.com/store/apps/details?id=jp.bokete.app.android&hl=ja&pli=1

https://play.google.com/store/apps/details?id=jp.co.bravesoft.honne&hl=ja






## LINEスタンプ

https://store.line.me/stickershop/product/1945741/ja

https://store.line.me/stickershop/product/10982661/ja