非データサイエンティストが、機械学習を用いてiPhone上で画像認識をやってみた

非データサイエンティストが、機械学習を用いてiPhone上で画像認識をやってみた

はじめに

こんにちは、最近猛暑だけどジムに通い始めたiOSエンジニアの松山です。WWDCで話題となったVisionとCoreMLを使ってみたことについてまとめてみます。

今回、カメラで読み込んだ画像を識別し、識別した物体のラベルと確率を表示するアプリを作成しました。また、Apple提供のモデルと、Pythonを用いてcaffeモデルからMLモデルに変換したカスタムモデルを用いました。

内容に入る前に、今から書く内容がイメージしやすいようにデモを載せます。

 

Visionフレームワークとは

Visionフレームワークでは、顔認識や、テキスト検出、バーコード認識、特徴追跡などを画像解析できるフレームワークです。また、CoreMLのモデルを用いて、Visionのフレームワークで画像の識別や、オブジェクト検出などを実行することもできます。今回のようにCoreMLとVisionを掛け合わせて使うことができるわけです。

 

CoreMLとは

CoreMLとは、Appleが提供している機械学習のフレームワークです。簡単にiOSに導入することができます。大まかに以下のような特徴があります。

  • iOSのデバイス上で実行できる為、外部との通信をする必要はない。
  • デバイスのCPU、GPUを活用するので最大限のパフォーマンスが発揮でき、デバイスの性能が上がる
    とパフォーマンス向上。
  • Core ML Toolsを用いることで、Python の各種フレームワークで学習されたモデルを簡単にCore MLフォーマットに変換することが可能。サポートされているモデルのフレームワークはこちらを参照。

CoreMLで機械学習の推論を実行する手順

CoreMLで使える機械学習モデルは、Appleが配布しているモデルと、自作のカスタムモデルがあります。そこで、以下の手順でモデルをCoreMLで使えるようにします。

  1. モデルの用意(Apple提供のモデルを使用する方法と、カスタムモデルを作成する方法)
  2. プロジェクトにCoreMLのモデルを追加
  3. 読み込んだモデルを利用した推論の実行

1. モデルの用意

Apple提供のモデルを使用する方法

Appleの公式サイトのこちらから、プロジェクトで使用したいCore ML モデルを取得してください。今回のデモアプリでは、MobileNetを使用しました。

カスタムモデルを作成する方法

今回は caffe モデルからCoreMLで使えるMLモデルに変換しました。Caffeモデルは自作で作るか、公開されているモデルをダウンロードしてください。今回のデモアプリではCaffeのgithubよりSqueezeNetをダウンロードして利用しました。そして、Python製のライブラリである、coremltoolsを用いて、caffeモデルからCoreMLモデルに変換します。

サンプルレポジトリのPythonディレクトリ以下にあるCaffeModelsディレクトリの中に好きな名前でディレクトリを作成し、その中に.caffemodelや、.prototxt、 .txtのファイルを入れてください。そして、src/convert_to_ml_model.py<GitHub の Pyhthon コードはこちら>  の coremltools.converters.caffe.convert()の中身にそれら3つのファイルを指定してください。


coreml_model = coremltools.converters.caffe.convert(
('./../CaffeModels/SqueezeNet/squeezenet_v1.1.caffemodel', './../CaffeModels/SqueezeNet/deploy.prototxt'),
image_input_names = 'image',
class_labels = './../CaffeModels/SqueezeNet/imagenet1000.txt')

coreml_model.save('./../MLModels/Squeeze.mlmodel') # The name converted from caffe model.

convert()の中身のimage_input_namesは入力層の名前の定義をしています。coreml_model.save()では、作成された CoreML モデルを保存するパスを指定します。今回は、MLModelsディレクトリ内に作成されるようにしました。python3 convert_to_ml_model.pyを実行します。

 

2. プロジェクトにCore ML モデルを追加

新規プロジェクトを作成して、そのプロジェクト内に先ほどまでに用意したモデルを追加します。

3. 読み込んだモデルを利用した推論の実行

推論の実行は以下の4ステップです。

  1. VNCoreMLRequestの作成
  2. モデルのハンドラーを作成
  3. AVFoundationのセットアップ
  4. VNCoreMLRequestを用いて画像の推論の実行

1. VNCoreMLRequestの作成

VNCoreMLRequest は、VNRequestを継承したVNImageBasedRequest を継承したものです。


lazy var modelRequest: VNCoreMLRequest = {
        guard let model = try? VNCoreMLModel(for: MobileNet().model) else {
            fatalError("can't load Core ML model")
        }
        return .init(model: model, completionHandler: self.handleModel)
 }()

Visionのフレームワークで扱えるのは、VNCoreMLModelなのでVNCoreMLModel()で、MLModelからVNCoreMLModelに変換しています。そして、VNCoreMLRequest()で、先ほど変換したVNCoreMLModelと、リクエストの結果を処理する為のハンドラー、以下のhandleModelcompletionHandlerとして渡します。

2. モデルのハンドラーを作成

private func handleModel(request: VNRequest, error: Error?) {
        guard let results = request.results as? [VNClassificationObservation] else { return }

        if let classification = results.first {
            // classification.identifier: オブジェクトのlabel(identifier)
            // classification.confidence: オブジェクトの信頼度数
            // 任意の処理
        }
 }

これは、先ほどのVNCoreMLRequestの結果を処理するためのハンドラーです。VNClassificationObservation は、VNObservationから継承されたクラスです。CoreML モデルを用いて、可能性の高いオブジェクトのlabel(identifier)とconfidenceを返しています。VNCoreMLRequestの結果は、VNObservationを継承したものが入ります。今回は画像の識別をしたいので、VNClassificationObservationでしたが、矩形認識をしたいときは、VNRectangleObservationを用います。詳しくは、こちらを参考にしてください。

3. AVFoundationのセットアップ


private lazy var previewLayer: AVCaptureVideoPreviewLayer = {
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.layer.bounds
        previewLayer.backgroundColor = UIColor.clear.cgColor
        previewLayer.videoGravity = .resizeAspectFill
        
        return previewLayer
}()

        let captureSession = AVCaptureSession()
        guard let captureDevice = AVCaptureDevice.default(for: .video) else {
            fatalError("Could not set av capture device")
        }
        
        guard let input = try? AVCaptureDeviceInput(device: captureDevice) else {
            fatalError("Could not set av capture device input")
        }
        
        let output = AVCaptureVideoDataOutput()
        output.setSampleBufferDelegate(self, queue: .init(label: "video"))
        
        captureSession.sessionPreset = .photo
        captureSession.addInput(input)
        captureSession.addOutput(output)
        self.captureSession = captureSession
        view.layer.addSublayer(previewLayer)

PreviewLayerのセットアップをlazy varで定義します。そして、CaptureSessionを設定してAVFoundationを使えるようにします。

4. VNCoreMLRequestを用いて画像の推論を実行する。

input画像を、VNImageRequestHandler の引数として、画像解析リクエストを実行します。


func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
        DispatchQueue.global(qos: .userInteractive).async {
            do {
                try VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([self.modelRequest])
            } catch let error {
                NSLog(error.localizedDescription)
            }
        }
}

captureOutputとしてsampleBufferが渡されるので、それをCMSampleBufferGetImageBufferで、CVImageBufferに変換しています。
その変換したものを、VNImageRequestHandlerと、先ほど作成したVNCoreMLRequestを用いて画像を推論します。

これだけで、与えた画像の推論の実行ができます。

 

サンプルアプリの使い方

CoreMLSample/Modelsディレクトリ以下に、追加したい任意のMLModelを追加します。

そして、以下のコードのMLModelListViewModelで、自分が用いたいモデルをenumで定義します。
MLModelsのenumのcase分を増やすだけで、使うことができます。


struct MLModelListViewModel {
    let sections: [Section] = [
                                .init(title: "Provided by Apple Inc.", items: [.mobileNet]), // Add items provided by apple.
                                .init(title: "Provided by own", items: [.squeezeNet]), // Add items created by own.
                              ]

    struct Section {
        let title: String
        let items: [MLModels]

        enum MLModels {
            case mobileNet
            case squeezeNet
            // 任意のモデルを追加

            var title: String {
                switch self {
                case .mobileNet:
                    return "Mobile Net"
                case .squeezeNet:
                    return "Squeeze Net"
                // case文で追加した任意のモデルの表示名
                }
            }

            var model: MLModel {
                switch self {
                case .mobileNet:
                    return MobileNet().model
                case .squeezeNet:
                    return SqueezeNet().model
                    // case文で追加した任意のモデル
                }
            }
        }
    }
}

追加したCoreML モデルが最初の画面に表示され、任意のモデルでの解析ができるようになります。

CoreML2について

  • WWDC2018で新しく発表のあったCoreML2ですが、すごいですね。(語彙力w)
  • Create MLというのも発表されており、手元のMacで機械学習ができてモデルを作れるらしい。
  • 30%も処理速度を改善したらしい。

終わりに

使ってみた感想は、簡単に機械学習が実装できたって感覚ですね。機械学習というと、バリバリ数学みたいなイメージがあったのですが、公開されているモデルとCoreMLを使うと簡単に実装できました。今後ますます機械学習を用いたサービスが出てくると思います。そこでCoreMLは学習済みモデルをモバイルに組み込むのに適しており、有効な手段だと思いました。また、どんどん進化していきそうなのでCoreMLは楽しみですね。

Hacarusでは、モバイルや組み込み、FPGA といった環境で機械学習を動かすことに興味があるエンジニアの方や、データサイエンティストとして機械学習の研究開発を行いたい!という方を募集しています!
興味がある方は、採用ページまで!

Takashi Someda

サンマイクロシステムズでエンジニアとしてキャリアをスタート。未踏ソフトウェア創造事業への採択をきっかけに、ベンチャーでのプロダクト開発に携わり続けている。現在はハカルスの CTO として、グローバルな開発チームとともにサービスを成長させるべく日々奮闘中。ソフトウェアとロックと家族を愛する40歳。

ニュースレター購読Newsletter

登録はこちら