iOSの機械学習フレームワークCore MLと最近のiPhoneなどに備わるAIプロセッサNeural Engineを用いて映画から自転車を探したところ、驚くような結果を得た。プログラミング・コードを簡潔に記述できるとともに、その処理速度が随分と速い。これが毎日使う小さなiPhoneで行えるのだから、唖然としてしまう。Neural EngineはFace IDだけでなく、汎用的なAI処理に活用できるわけだ。
これまでの自転車探索、正確にはオブジェクト検出はディープ・ラーニングのKeras-Yolo3を用いてきた。これをMacで実行すると恐ろしく時間がかかり、2時間の映画なら2〜3日はマシンを占有してしまう。 神谷典孝が示したようにGPUを活用すれば実用的な速度となり、2〜3時間で処理が完了する。ただし、GPUはタワー型の大型PCを必要とし、随分と高価であり、消費電力が馬鹿にならないほど大きい。
それでは以前と同じ2分13秒の映像の処理時間を比較してみよう。以下のグラフの最初の3項目はKeras-Yolo3のCPUでの実行、4番目はKeras-Yolo3のGPUでの実行、残る3つはCore MLのNeural Engineでの実行だ。GPUでの処理は改善の余地があるようだが、それにしても機械学習に最適化されたNeural Engineの優れた処理能力が際立っている。また、A12とA13の処理速度が僅差であることも注目される。
ちなみに、今回のCore MLによるプログラムで使用したモデルはYOLOv3.mlmodelで、Keras-Yolo3と同じdarknetのデータ・セットが元になっている。Appleはサンプル・コードとともに何種類もの特徴的なモデルを提供しており、利用し易いのが有難い。YOLOv3に関しても、精度より処理速度を重視したYOLOv3-Tinyがあり、それぞれデータ・タイプが異なるモデルも用意されている。
実際に利用するには、まず、GitHubのプロジェクト一式をダウンロードする。そして、Appleからダウンロードしたモデルと任意のムービーをプロジェトに登録して実行すれば良い(下図の(1)と(2))。名称が異なるモデルやムービを使用するには、ソースコードでのファイル名の指定を変更する(下図の(3)と(5))。これでiOSデバイスで実行ができる。極めて遅いがiOSシミュレータでも構わない。
プログラム・コードはユーザ・インターフェースやエラー・ハンドリング等を省略して簡略化している。それだけに、主要な処理コードは驚くほど短いことが分かるだろう。画像解析に基づいた処理は27〜33行で、ここでは解析結果をprint文でコンソールに出力している(上図の(4))。ファイル出力や図形描画といった更なる機能が必要であれば、このハンドラ部分に記述することになる。
// // ViewController.swift // MovieAnalyzer // // Created by aka on 2020/03/13. // Copyright © 2020 aka. All rights reserved. // import UIKit import Vision import AVFoundation class ViewController: UIViewController { private var requests = [VNRequest]() override func viewDidLoad() { super.viewDidLoad() setupVision() processMovie() } func setupVision() { // 機械学習モデルを読み込む if let modelURL = Bundle.main.url(forResource: "YOLOv3", withExtension: "mlmodelc") { let mlModel = try! VNCoreMLModel(for: MLModel(contentsOf: modelURL)) let mlRequest = VNCoreMLRequest(model: mlModel, completionHandler: { (request, error) in // 画像解析結果を処理する let results = request.results! print("Found \(results.count) objects") for item in results { let object = item as! VNRecognizedObjectObservation print("\(object.labels[0].identifier) \(object.confidence) \(object.boundingBox)") } }) self.requests = [mlRequest] } } func processMovie() { // ムービーを読み込む let url = Bundle.main.url(forResource: "test", withExtension:"mov") let asset = AVURLAsset(url: url!, options: nil) let reader = try! AVAssetReader(asset: asset) let videoTrack = asset.tracks(withMediaType: .video).first! let outputSettings = [String(kCVPixelBufferPixelFormatTypeKey): NSNumber(value: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)] let trackReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: outputSettings) reader.add(trackReaderOutput) reader.startReading() // フレームごとに画像を読み込む while let sampleBuffer = trackReaderOutput.copyNextSampleBuffer() { // CMSampleBuffer if let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) { // CVImageBuffer (CVPixelBuffer) let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: imageBuffer, options: [:]) // 画像の処理を実行する try! imageRequestHandler.perform(self.requests) } } } }
【追記】後半のプロジェクトやコードの説明を修正して、プログラムの変更方法を加筆した。(2020年4月9日)
One comment