ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
機械学習を組み込んだAppの開発手法
優れた機械学習(ML)によるエクスペリエンスをあなたのAppに取り入れる方法を紹介します。モデルの発見、変換、トレーニングについて解説し、MLのヒントとベストプラクティスを紹介します。さらに、MLの導入に伴う考慮すべき事項をはじめ、モデルのパフォーマンスを評価するテクニックや、モデルを調整して、デバイスでリアルタイムのパフォーマンスを実現する方法について解説します。 このセッションで取り上げたテクニックの詳細については、WWDC22の「Core MLの使用を最適化する」および「Metal Performance Shaders Graphで機械学習を促進する」をご覧ください。
リソース
- Core ML
- Core ML Converters
- Core ML Tools PyTorch Conversion Documentation
- Integrating a Core ML Model into Your App
関連ビデオ
WWDC23
WWDC22
Tech Talks
WWDC20
-
ダウンロード
(音楽)
どうもGeppy Parzialeです Appleの機械学習エンジニアです 今日 お話するのは機械学習を使って 問題を解決するAppの作成についてで その問題は 通常は専門家を要する種のものです
この機会を通してどのようにしてAppに オープンソースの機械学習モデルを追加し 素晴らしい新体験を生み出すかお見せします その中で強調したいのは 機械学習を使ってAppを作るために Appleが開発エコシステムとして提供する ツールやフレームワークやAPIです
ユーザーに最高の体験を与えることを目標に Appの作成中に様々な決定を行うことになります Appに機械学習の機能を加える時も同様です
開発中に考える例を挙げると… この機能を作るのに 機械学習を使うべきだろうか? 機械学習モデルは どうやって得られるのだろうか? Appleプラットフォームでの互換性を どうやって持たせるのだろうか? そのモデルは自分の考える 特別なユースケースで使えるだろうか? Apple Neural Engineで 実行可能なのだろうか? 一緒にこれらを見ていきましょう 私が地下室で見つけた 白黒の家族写真に リアルな色付けができる Appを作りたいと思います
もちろん専門の写真家が 手作業でできる作業ですが 写真編集ツールを使って 時間をかけて行います その代わりにこの処理を自動化し ほんの数秒で 色を加えたいとしたら どうでしょうか? 機械学習にもってこいのタスクのようですね
Appに機械学習機能を統合するための フレームワークやツールが Appleには山ほどあります モデルトレーニング用のデータ処理から 推論実行まで あらゆるものが提供されています ここではそのほんの一部を使うつもりです ですが 開発している 機械学習タスクの種類に応じて 選べる種類は非常に多いと 覚えておいてください 私がAppの機械学習機能を 開発する際のプロセスは 一連の段階を踏んで行われます
まずは 科学出版物か専門のウェブサイトで 適切な機械学習モデルを探していきます
“写真の着色”で検索して 見つけたのが Colorizerで 丁度よさそうです このモデルで行える着色の例がこちらです
別の例です
そしてもう1つ 非常に良いですね その使い方をお見せします Colorizerモデルは 白黒画像の入力を想定しています RGB画像をLAB色空間に変換する Pythonのソースコードを見つけました
この色空間には3チャンネルあり 1つは明度を表すLチャンネルで 他の2つは色成分を表します 色成分は廃棄され 明度が着色モデルの入力になります
そしてこのモデルは 新たな2チャンネルを推測し 入力のLチャンネルと合わせて 色付き画像となります
このモデルを私のAppで 使えるようにしましょう coremltoolsを使って オリジナルの PyTorchモデルを Core MLに変換します これがその変換で私が使った 簡単な Pythonスクリプトです
PyTorchモデルのアーキテクチャと ウェイトをインポートします
インポートしたモデルをトレースします 最後に PyTorchモデルを Core MLに 変換して保存します
モデルが Core MLフォーマットに変換されたら その変換が正しいかを確認する必要があります これも coremltoolsを使って 直接 Pythonで行えます しかも簡単です 画像を RGB色空間でインポートし Lab色空間に変換します
そして明度を分離後 カラーチャンネルを廃棄します
Core MLモデルを使って予測を実行します
最後に 入力の明度と 推測された色成分を合成して RGBに変換します
これで変換したモデルの機能が オリジナルの PyTorchモデルの機能と 一致しているかを確認することができます この段階をモデル検証と呼びます しかし確認すべき重要点はもう1つあります このモデルが対象デバイスで 充分な速度で動作するでしょうか? デバイス上でモデルを動作させて 最高のユーザー体験になるか 確認しなければなりません Xcode 14で提供される 新しい Core ML Performanceは Core ML modelの時間ベースの パフォーマンス解析を行います モデルを Xcodeに ドラッグ&ドロップすれば 数秒でパフォーマンスレポートが できあがります
このツールを使うと M1チップと iPadOS 16搭載の iPad Proでの推定推論時間は 約90ミリ秒だと分かります
私の写真着色Appにピッタリだと分かりました Xcodeのパフォーマンス レポートについての詳細は "Optimize your Core ML usage" のセッションをどうぞ パフォーマンスレポートで モデルの測定を行い デバイス上のユーザー体験を 最高にできるのです
モデルの機能とパフォーマンスが 確認できたので Appに統合します
統合のプロセスはこれまで Pythonで行ったのと同じですが 今回は Swiftでスムーズに Xcodeや慣れたツールを使って行っていきます
Core MLフォーマットになったこのモデルは 明度を表す単一チャンネルの 画像を入力として受け付けます
ですから Pythonの時と同じように RGB入力画像はすべて Lab色空間の画像に変換します
この変換は複数の方法で書くことができます vImageを使って Swiftで直接変換するか あるいは Metalを使うか
Core Imageのフレームワークが 役に立ちそうなものを 提供してくれそうだと ドキュメンテーションから分かりました
ここで RGBから LABに変換し Core MLモデルを使って 予測を行う方法をお見せします
これが RGB画像から明度を抽出して Core MLモデルに送るためのSwiftコードです 最初に RGB画像を LABに変換し 明度を抽出します
そして明度を CGImageに変換し Core MLモデル用の入力を準備します
最後に推論の実行です 入力 RGB画像から Lチャンネルを 抽出するために 最初に RGB画像をLAB画像に変換しますが その際に使うのが新しい CIFilterの convertRGBtoLabです 明度の値は0から100の間に設定されます
そして カラーマトリクスでLab画像を乗算し カラーチャンネルを廃棄し 明度を呼び出し元に返します 今度はモデルの出力側の様子を 分析してみましょう
Core MLモデルは推測された色成分を表す 2つのMLShapedArraysを返します
推論後 2つのMLShapedArrayを 2つのCIImageに変換します
最後にそれを モデルの入力明度と 組み合わせます これで私が RGBに変換し戻した 新しい LAB画像ができました
2つの MLShapedArrayの 2つの CIImagesへの変換では 最初にそれぞれのShapedArrayから値を抽出します 2つのカラーチャンネルを表す 2つのCore Imageを作り それを返します 明度と推論されたカラーチャンネルの合成には カスタムの CIKernelを使いますが それが 3チャンネルを入力に取り CIImageを戻してくれます
それから新しい CIFilterの convertLabToRGBを使って LAB画像を RGBに変換し呼び出し元に返します これが私の使うカスタムの CIKernelのソースコードで 計算された2つのカラーチャンネルと明度を 単一 CIImage内に統合します
RGB画像をLAB画像に変換 またはその逆の変換をする 新CIフィルターの詳細は画面左下に出ている セッションの内容を参考にしてください
これでAppへの機械学習機能の 統合が完了したので 実際に使ってみましょう いや 待ってください 古い家族写真のAppでの着色を リアルタイムで行う方法とは? 1枚ずつデジタル化して Appにインポートもできますが…
もっと良い方法があります iPadのカメラでこれらの写真をスキャンして ライブで着色してはどうでしょう? きっと楽しいでしょうし 必要な物は揃っています ですがまず 問題の解決が必要です
このモデルの1枚の画像の 処理時間は90ミリ秒です 動画の処理の場合はもっと速いものが必要です
スムーズなユーザー体験のためには 最低でも 30fpsのカメラを使いたいわけで
約30ミリ秒ごとに1フレームの 生成ということになります
ですがこのモデルは1フレームに 90ミリ秒必要なので 着色1回ごとに2つか3つの フレームを失うことになります
モデルの合計予測時間はそのアーキテクチャと マップされる単位操作の計算の 両方を表すものです パフォーマンスレポートから このモデルが実行するのは ニューラルエンジンとCPUで 合計61オペレーションという事です
予測時間を速めたいなら モデルを変えなければなりません 試しにこのモデルのアーキテクチャに手を加え もっと速い代替品を作ってみようと思います しかしアーキテクチャを変えれば ネットワークの再訓練が必要です
Appleの提供する別のソリューションでは Mac上で直接モデルに機械学習の訓練ができます
今回はオリジナルモデルが PyTorchで開発されたため Metalで新しい PyTorchを使うことにしました それにより Apple Siliconの ハードウェアアクセラレーションが 利用できるというわけです
Metalでアクセラレートされた PyTorchに関する詳細は 画面左下に出ているセッションでどうぞ
この変更により少し後戻りすることになります
再訓練の後 その結果を Core MLフォーマットに変換し もう一度それを検証しなければなりません
今回のモデル統合は 単純に古いモデルを 新しいモデルに替えるだけです 代替モデル候補のいくつかを再訓練した後で 自分の要求に合うモデルを検証しました そのパフォーマンスレポートが こちらになります すべてがニューラルエンジンで実行されていて 予測時間は今や約16ミリ秒になっているので 動画でも大丈夫です
ですがこれはAppの パフォーマンスの一面に過ぎません
事実 Appを実行するとすぐに気づいたのが 思ったほど着色がスムーズでないことです 実行時にAppに何が起こっているのでしょうか?
それを知るのに Instrumentsで 新しい Core MLテンプレートが使えます
Core MLのトレースの初期部を分析すると Appが推論を溜めていることに気づいたのです これは予想外です 1フレームに1つの予測だと予想していました
トレースを拡大して冒頭部分の予測を調べると 1つの Core MLの推論が終了する前に Appは2つ目を要求しています
ここでニューラルエンジンが 最初の要求を処理しているのに 2つ目が Core MLに付与されています
同様に 2つ目の処理中に 3つ目の予測が始まっています 4つ目の予測の後になると 要求と実行のラグは既に 20ミリ秒にまで広がってます このようなラグを避けるために 前の予測が終わってから 新しい予測が始まるように しなければなりません
また カメラのフレームレートを 30fpsの代わりに うっかり60fpsにしていたことも この問題処理中に分かりました
前の予測が完了してから 新しいフレームの処理が始まるようにし カメラのフレームレートを 30fpsに設定した後には Core MLは正確に1つの推論を Appleのニューラルエンジンに 送るようになり Appはスムーズに動作しています
これで目標達成です
私の古い家族写真を使ってAppを試しましょう
こちらは地下で見つけた白黒の写真です 昔 イタリアに行った時に撮ったものです
ローマのコロシアムが写った 素晴らしい写真です
壁や空の色がとても現実的ですね
では こちらの写真です
イタリア南部のカステル・デル・モンテです とても素晴らしい
こちらは私の故郷グロッターリエです これらの写真に着色すると 思い出が蘇ってきます
着色しているのは写真だけで他の部分はすべて 白黒のままだと分かりますね
Visionフレームワークで利用可能な 長方形検出アルゴリズムを利用しているのです VNDetectRectangleRequestで 写真だけを分離して Colorizerモデルへの入力に 使えるというわけです
では 復習します
今回は Appのための機械学習機能を 準備し統合し評価するために Appleが提供する 多くのフレームワークやAPIや ツールを検討しました 私は課題を解決できる オープンソースの機械学習モデルを 特定することから始めました
望んでいる機能を持つ オープンソースモデルを見つけ Appleのプラットフォームで 使えるようにしました 新しいパフォーマンスレポートを使用して モデルのパフォーマンスを 直接デバイスで測定しました 皆さんがご存知の ツールやフレームワークを使い モデルをAppに統合しました
新しい Core ML Templateを 使ってモデルを最適化しました これらの各開発作業は Appleのツールやフレームワークで 直接に行えます データ準備から訓練 統合 そして最適化まで全部です
本日の内容は デベロッパが Appleのフレームワークや ツールを使って達成できる ほんの一部の事です このセッションに関係する 前出のセッションから Appにおける機械学習の 画期的内容をご覧ください フレームワークやツールを 色々試してみましょう ソフトウェアとハードウェアの 大きな相乗効果を活かして 機械学習機能を加速させ Appのユーザー体験を豊かなものにしましょう それでは皆さんまたお会いしましょう
-
-
3:06 - Colorization pre-processing
from skimage import color in_lab = color.rgb2lab(in_rgb) in_l = in_lab[:,:,0]
-
3:39 - Colorization post-processing
from skimage import color import numpy as np import torch out_lab = torch.cat((in_l, out_ab), dim=1) out_rgb = color.lab2rgb(out_lab.data.numpy()[0,…].transpose((1,2,0)))
-
3:56 - Convert colorizer model to Core ML
import coremltools as ct import torch import Colorizer torch_model = Colorizer().eval() example_input = torch.rand([1, 1, 256, 256]) traced_model = torch.jit.trace(torch_model, example_input) coreml_model = ct.convert(traced_model, inputs=[ct.TensorType(name="input", shape=example_input.shape)]) coreml_model.save("Colorizer.mlpackage")
-
4:26 - Core ML model verification using Core ML Tools
import coremltools as ct from PIL import Image from skimage import color in_img = Image.open(“image.png").convert("RGB") in_rgb = np.array(in_img) in_lab = color.rgb2lab(in_rgb, channel_axis=2) lab_components = np.split(in_lab, indices_or_sections=3, axis=-1) (in_l, _, _) = [ np.expand_dims(array.transpose((2, 0, 1)).astype(np.float32), 0) for array in lab_components ] out_ab = coreml_model.predict({"input": in_l})[0] out_lab = np.squeeze(np.concatenate([in_l, out_ab], axis=1), axis=0).transpose((1, 2, 0)) out_rgb = color.lab2rgb(out_lab, channel_axis=2).astype(np.uint8) out_img = Image.fromarray(out_rgb)
-
7:11 - Colorization in Swift
import CoreImage import CoreML func colorize(image inputImage: CIImage) throws -> CIImage { let lightness: CIImage = extractLightness(from: inputImage) let modelInput = try ColorizerInput(inputWith: lightness.cgImage!) let modelOutput: ColorizerOutput = try colorizer.prediction(input: modelInput) let (aChannel, bChannel): (CIImage, CIImage) = extractColorChannels(from: modelOutput) let colorizedImage = reconstructRGBImage(l: lightness, a: aChannel, b: bChannel) return colorizedImage }
-
7:41 - Extract lightness from RGB image using Core Image
import CoreImage.CIFilterBuiltins func extractLightness(from inputImage: CIImage) -> CIImage { let rgbToLabFilter = CIFilter.convertRGBtoLab() rgbToLabFilter.inputImage = inputImage rgbToLabFilter.normalize = true let labImage = rgbToLabFilter.outputImage let matrixFilter = CIFilter.colorMatrix() matrixFilter.inputImage = labImage matrixFilter.rVector = CIVector(x: 1, y: 0, z: 0) matrixFilter.gVector = CIVector(x: 1, y: 0, z: 0) matrixFilter.bVector = CIVector(x: 1, y: 0, z: 0) let lightness = matrixFilter.outputImage! return lightness }
-
8:31 - Create two color channel CIImages from model output
func extractColorChannels(from output: ColorizerOutput) -> (CIImage, CIImage) { let outA: [Float] = output.output_aShapedArray.scalars let outB: [Float] = output.output_bShapedArray.scalars let dataA = Data(bytes: outA, count: outA.count * MemoryLayout<Float>.stride) let dataB = Data(bytes: outB, count: outB.count * MemoryLayout<Float>.stride) let outImageA = CIImage(bitmapData: dataA, bytesPerRow: 4 * 256, size: CGSize(width: 256, height: 256), format: CIFormat.Lh, colorSpace: CGColorSpaceCreateDeviceGray()) let outImageB = CIImage(bitmapData: dataB, bytesPerRow: 4 * 256, size: CGSize(width: 256, height: 256), format: CIFormat.Lh, colorSpace: CGColorSpaceCreateDeviceGray()) return (outImageA, outImageB) }
-
8:51 - Reconstruct RGB image from Lab images
func reconstructRGBImage(l lightness: CIImage, a aChannel: CIImage, b bChannel: CIImage) -> CIImage { guard let kernel = try? CIKernel.kernels(withMetalString: source)[0] as? CIColorKernel, let kernelOutputImage = kernel.apply(extent: lightness.extent, arguments: [lightness, aChannel, bChannel]) else { fatalError() } let labToRGBFilter = CIFilter.convertLabToRGBFilter() labToRGBFilter.inputImage = kernelOutputImage labToRGBFilter.normalize = true let rgbImage = labToRGBFilter.outputImage! return rgbImage }
-
9:08 - Custom CIKernel to combine L, a* and b* channels.
let source = """ #include <CoreImage/CoreImage.h> [[stichable]] float4 labCombine(coreimage::sample_t imL, coreimage::sample_t imA, coreimage::sample_t imB) { return float4(imL.r, imA.r, imB.r, imL.a); } """
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。