[iOS 11] Core MLで焼き鳥を機械学習させてみた

Core ML

Core MLは、学習モデル等をiOS / macOS上で利用する際に、開発者が専門的な知識を必要とせずに扱えるように補助するフレームワークです。WWDC2017で発表された際は、ARKitに並んで現地で反響の大きかった発表でした。

iOSでは、Core ML発表以前から機械学習を取り入れようとはしていました。ただ、そのためにはCIImageを利用した画像処理や、学習モデルへの入出力などのコーディングといった専門的な知識が必要でした。

c35ebf2d-ee94-4448-8fae-16420e7cc4ed

Core MLの登場で学習モデルを確保できれば、以下の機能を使ってiOS / macOSアプリで機械学習しやすくなります。

  • 学習モデルをiOS / macOSアプリで扱える形式に変換する(Core ML model)
  • Core ML modelへの入力と出力を補助するフレームワーク

アプリ内に組み込み、学習結果を受け取ることに特化したCore MLを利用することで、以下のメリットが出てきます。

  • ネットワークの状態に依存しない
  • デバイスのCPUに依存するため、今後のデバイスの進化でより高速に処理ができる
  • 結果をサーバーに送らないため、プライバシーを確保できる

db81e861-1e06-4d14-8915-90707d9b114c

上の図のとおり、Core MLはWWDC2017で追加された画像解析用のVisionフレームワークや、拡張されたNatural language processingから入力を受け取り、処理の結果を返すインターフェースを備えています。この記事では、関連するフレームワーク群やツールに関して紹介します。

MLMODEL

xcode model view

MLMODELは学習モデルをXcodeに組み込んで利用できるようにしたファイルの形式です。Xcodeに組み込まれた時点でMLModelのクラスとしてプログラム上で利用できるようになります。

support library
サポートされているライブラリの学習モデルを後述する変換ツールを利用してMLMODELに変換します。
Appleがサンプルで提供しているMLMODELをアプリに組み込めば、学習モデルを用意しなくても動作の確認を行うことができます。

実際に弊社のSINが試したブログもありますのでこちらも参照していただければと思います。

[iOS 11] Vision.Frameworkで画像の識別を試してみました #WWDC2017

Vision + Core ML

Vision

VisionはWWDC2017で発表された、画像解析処理に特化したフレームワークです。
顔検出、オブジェクトの追跡、バーコードの検出などをCIImageなどの画像処理を考慮せずにプログラミングできる強力なインターフェースを備えています。

Visionフレームワークでできることに関しては、 [iOS 11] 画像解析フレームワークVisionで顔認識を試した結果 を参照してください。

Visionフレームワークにはカメラからの映像や、アルバムから取得した写真を学習モデルに入力し、解析の結果を受け取るためのクラスが提供されています。
MLModelを扱いやすいようにしたVNCoreMLModelクラスや、結果のリクエストを行うVNCoreMLRequestが上記にあたります。

サンプルとして提供されている MobileNet を利用して試してみました。
MobileNetのMLMODELをXcodeに追加します。

model

Xcodeに組み込まれたMobileNetはMLModelというクラスとしてプログラム上で呼び出せるようになります。これをVisionフレームワークで扱えるVNCoreMLModelに変換します。

// MobileNet(MLModel) を VNCoreMLModelに変換
guard let coreMLModel = try? VNCoreMLModel(for: MobileNet().model) else { return }

変換したVNCoreMLModelをCore MLに渡し、結果を受け取った後の処理をハンドラーに記述します。
Visionフレームワーク → Core MLへのリクエストはVNCoreMLRequestが使用します。

let request = VNCoreMLRequest(model: coreMLModel) { request, error in
    // results は confidence の高い(そのオブジェクトである可能性が高い)
    // 順番に sort された Array で返ってきます
    guard let results = request.results as? [VNClassificationObservation] else { return }

    if let classification = results.first {
        self.identifierLabel.text = classification.identifier
        self.confidenceLabel.text = "\(classification.confidence)"
    }
}

リクエスト処理ができましたので、解析したい画像をCIImageに変換し、画像解析を行うハンドラーを実行します。
captureImageはカメラのキャプチャー画像をUIImageに変換したものを使用しました(カメラ画像のキャプチャーに関しては今回は割愛します)。

// captureImageはカメラのキャプチャー画像
guard let ciImage = CIImage(image: captureImage) else { return }

// VNImageRequestHandlerを作成し、performを実行
let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])
guard (try? handler.perform([request])) != nil else { return }

MLModelさえ準備できれば、少ないプログラムのコード量で画像解析を行うことができました。

pod

Natural language processing

NLP

Natural language processingの項目では、Core MLと連携しやすくなった自然言語処理の変更点について記します。

nlp input

NSLinguisticTagScheme というiOS 5から存在するFoundationのクラスで、iOS 11では以下の品詞分解に特化したenumerateTagsメソッドが追加されました。

  • 言語(英語・日本語)判定
  • 単語のトークン化
  • レンマ化
  • 名称分解

input

試しに、「hello nice to meet Tanaka Takaaki」という言葉を単語のトークン化してみました。

let tagger = NSLinguisticTagger(tagSchemes: [.tokenType], options: 0)
// text は入力されたString
tagger.string = text
let range = NSRange(location: 0, length: text.utf16.count)
let options: NSLinguisticTagger.Options = [.omitPunctuation, .omitWhitespace]
tagger.enumerateTags(in: range, unit: .word, scheme: .tokenType, options: options) { tag, tokenRange, stop in
    let token = (text as NSString).substring(with: tokenRange)
    print("token = \(token)")    
}

結果は以下のようになりました。

token = hello
token = nice
token = to
token = me
token = Tanaka
token = Takaaki

今度は名称を取得してみました。

let tagger = NSLinguisticTagger(tagSchemes:[.nameType], options: 0)
// text は入力されたString
tagger.string = text
let range = NSRange(location: 0, length: text.utf16.count)
let options: NSLinguisticTagger.Options = [.omitPunctuation, .omitWhitespace, .joinNames]
let tags: [NSLinguisticTag] = [.personalName, .placeName, .organizationName]
tagger.enumerateTags(in: range, unit: .word, scheme: .nameType, options: options) { tag, tokenRange, stop in
    if let tag = tag, tags.contains(tag) {
        let name = (text as NSString).substring(with: tokenRange)
        print("name = \(name)")
    }
}

結果は以下のようになりました。

name = Tanaka Takaaki

WWDC2017のNatural language processingの項目ではNSLinguisticTagSchemeを利用して、入力された文字列がpositiveな言葉なのか、negativeな言葉なのかを判定するSentiment Analysisのデモが行われていました。

demodemo

Sentiment Analysisと似たような動作をする SentimentCoreMLDemo を利用して NSLinguisticTagScheme が分解した文字列を、Sentiment Analysisの学習モデルへ渡した結果を表示させるコードを試してみました。

文字列のトークン化はNSLinguisticTagSchemeで行います。

var inputFeatures = [String: Double]()
tagger.string = text
let range = NSRange(location: 0, length: text.utf16.count)
// トークン化
tagger.enumerateTags(in: range, scheme: .nameType, options: options) { _, tokenRange, _, _ in
    let token = (text as NSString).substring(with: tokenRange).lowercased()
    // 短い単語はスキップ
    guard token.count >= 3 else { return }
    if let value = wordCounts[token] {
        wordCounts[token] = value + 1.0
    } else {
        wordCounts[token] = 1.0
    }
}

MLModelのpredictionへトークン化した結果を渡します。

// PipelineのMLModel
let model = SentimentPolarity()
let output = try model.prediction(input: inputFeatures)

normal

文字列を入力します。

happy

「Hollo world」はpositiveな言葉と判定されました。
では新しいガジェットに関してはどうでしょうか。

bad

negativeな判定となったようです。
どうやらお気に召さなかった…ようですね。

coremltools

convert ml

各機械学習ライブラリで作成した学習モデルをMLMODELに変換するcoremltools が提供されています。こちらはPython製のライブラリで、オープンソースとして公開されています。

今回はKerasで作成した学習モデルをMLMODELに変換し、Xcodeに組み込んで動作させるところまでやってみました。

EC2にKerasとBackendにTensorFlowが利用できるAMIが公開されていますので、今回は環境構築・学習時間短縮の観点からそちらを利用しました。

ami

p2.xlageなどのGPUが利用できるインスタンスは有料です。利用の際は注意してください。

学習プログラムは CIFAR10のサンプル を利用しました。
あとは学習させたい画像をCIFAR10の対応している32 x 32の形式に切り出しました。

yakitori

CIFAR10のサンプル を参考に作成した学習プログラムを実行します。

$ python udn_main.py

larnning

学習が終了すれば学習モデルが生成されます。
学習モデルからMLMODELへ変換する coremltools をインストールします。

$ sudo pip install coremltools

coremltoolsを使用してMLMODELへ変換するconvert.pyというPythonのプログラムを作成します。

import coremltools

model = coremltools.converters.keras.convert('model.h5',
        image_input_names = 'data',
        class_labels = 'labels.txt')

model.save('Udon.mlmodel')

convert.pyを実行し、Udon.mlmodelが作成します。

$ python convert.py

convert

作成したUdon.mlmodelをVision + Core MLの項目で記した方法と同様にXcodeに組み込み、プログラムから読み込んでみました。

// Udon(MLModel) を VNCoreMLModelに変換
guard let coreMLModel = try? VNCoreMLModel(for: Udon().model) else { return }

let request = VNCoreMLRequest(model: coreMLModel) { request, error in
    // results は confidence の高い(そのオブジェクトである可能性が高い)
    // 順番に sort された Array で返ってきます
    guard let results = request.results as? [VNClassificationObservation] else { return }

    if let classification = results.first {
        self.identifierLabel.text = classification.identifier
        self.confidenceLabel.text = "\(classification.confidence)"
    }
}

// captureImageはカメラのキャプチャー画像
guard let ciImage = CIImage(image: captureImage) else { return }

// VNImageRequestHandlerを作成し、performを実行
let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])
guard (try? handler.perform([request])) != nil else { return }

Screen Shot 2017-09-12 at 14.13.59

学習結果は別として、「学習モデル作成」 → 「MLMODELへの変換」 → 「アプリから使用」までを行うことができました。

最後に

自分は今まで機械学習に触れたことがありませんでしたが、coremltoolsの説明で触れたように、「学習モデル作成」 → 「MLMODELへの変換」 → 「アプリから使用」までが簡単に行えるようになりました。
橋渡しを行う coremltools の登場によってアプリケーションエンジニアと機械学習エンジニアの分業がより明確になったことや、学習モデルがプログラムから利用しやすくなったことで、iOSアプリにおける機械学習の機運が高まったのではないかと思いました。

関連リンク

Apple公式情報

公式情報

参考ブログ