インテリジェントなアプリ

Google Play 開発者サービスの ML Kit と LiteRT でオンデバイス インテリジェンスをマスターする

スピーカー

  • Chintan Parikh

    リード プロダクト マネージャー

  • Fergus Henderson

    スタッフ ソフトウェア エンジニア

概要

講演について

プラグアンドプレイ AI の利便性とカスタムモデルの柔軟性のどちらかを選ぶ必要はもうありません。メガバイト単位のバイナリ肥大化を招くことなく、1 つの超軽量アプリに両方をデプロイできます。

このセッションでは、このデュアルスタック アーキテクチャを 1 つの Android アプリに構築する方法をデモします。ML Kit を活用してすぐに使える従来の ML タスク(ドキュメント スキャンなど)を実行する方法と、Google Play 開発者サービスの LiteRT スタンドアロンを使用してカスタムのコンピュータ ビジョンやオーディオ ワークロードを実行する方法を説明します。このセッションでは、両方の実装パスが内部でどのように統合され、最新の LiteRT ランタイムを活用しているかを詳しく説明します。これにより、アプリは CPU と GPU でシームレスなハードウェア アクセラレーション パフォーマンスを実現し、ダウンロード フットプリントは実質的にゼロバイトになります。

デベロッパー向けの重要なポイント:

- 妥協のないパフォーマンス: ターンキー モデルとカスタムモデルの両方が、統合された CompiledModel API を使用して自動的にハードウェア アクセラレーションされます。

- マイクロ APK: ML Kit と LiteRT の両方のランタイムを Play 開発者サービスにオフロードすることで、アプリのダウンロード サイズを大幅に削減します。

- ハイブリッド統合: リソースの競合なしに、本番環境で複数の ML ワークフローを並行して実行する方法を習得します。

概要

  • Google Play 開発者サービスの LiteRT は、アプリ間で 1 つのメンテナンスされたランタイムを共有するため、APK サイズを約 8 MB 削減できます。
  • ターンキー ML Kit 機能とカスタム LiteRT モデルは、同じランタイム上の単一のアプリケーションで一緒に実行できます。
  • CompiledModel API は GPU アクセラレーションを簡素化し、従来の TFLite GPU 推論よりも最大 1.4 倍高速なパフォーマンスを実現します。
  • プラットフォームの提供範囲は、スマートフォンだけでなく、Wear OS 4 以降、Android 11 以降の Android Automotive OS にも拡大しています。
  • Google Play 開発者サービスで LiteRT を採用するには、Maven 依存関係を play-services-litert に更新し、LiteRt.initialize を呼び出して環境を取得し、それを CompiledModel.create に渡します。
  • 1.x はメンテナンス モードであり、NNAPI は廃止されるため、2.x CompiledModel API への移行をおすすめします。

スキル

リソース

Gemini Notebook

トピック

  • on-device-ai
  • ml-kit
  • litert
  • google-play-services
  • gpu-acceleration
  • 機械学習

文字起こし

LiteRT の概要

  • 皆さん、こんばんは。

  • まもなくトークが始まります。

  • そのため、現在「オンデバイス AI」と言う場合、多くは LLM を指しています。

  • また、本日の前半では、ML Kit、Firebase、その他多くの Google プロダクトでカバーされている Gemini Nano に関する講演がいくつかありました。

  • このセッションでは、もう半分の、従来の ML と、お客様が独自に用意するモデルについて説明します。

  • おそらく、今日もそれとは知らずに数回使用しているはずです。

  • 背景をぼかしたり、バーコード スキャナを使用したり、何らかの画像編集を行ったりしたアプリを思い浮かべてください。

  • その多くは従来の ML であり、通常はオンデバイスで実行されます。

  • これらの機能をアプリや APK に追加したことがある方なら、ターンキー API を使用するか、独自のモデルを使用するかというトレードオフに直面したことがあるでしょう。

  • アプリのサイズにどのような影響があるか、

  • 本日は、ターンキー モデルと独自モデルのどちらかを選択する必要はないことをご紹介します。

  • 実際には、1 つのリーン ランタイムで両方を行うことができます。

  • Chintan Parikh と申します。

  • 私は LiteRT のプロダクト マネージャーを務めています。本日は、Android ML サービスのエンジニアリング リードである Fergus も同席しています。

  • こんにちは。

  • 最後に、1 つのアプリを構築して、すべてのカメラフレームで 2 つのモデルを 1 秒あたり約 30 フレームで実行する方法を説明します。

  • この講演は 4 つの部分で構成されています。

  • パート 1 では、従来の ML(ML Kit と LiteRT)に焦点を当てた Google のオンデバイス AI ソリューションについて説明します。

  • 次に、LiteRT と Google Play 開発者サービスの最新情報についても説明します。

  • また、LiteRT の新しい CompiledModel API と、サポートされているデバイスについても説明します。

  • 3 つ目は、新しいアプリを構築する方法です。

  • Fergus が、ML Kit とカスタムモデルを使用してアプリを構築し、すべてを 1 つの APK にまとめる方法について説明します。

  • 最後に、エージェント モードを使用して既存のアプリをアップグレードする方法をご紹介します。

  • また、このエージェントのサポートを利用して、新しいアプリをスキャフォールディングすることもできます。

  • それでは

  • まず、LiteRT とは何かについて説明します。

  • LiteRT は、独自のモデル用の Google のオンデバイス推論ランタイムです。

  • 以前に TensorFlow Lite で使用したことがある場合は、同じ .tflite ファイル形式を使用します。

  • パフォーマンスを重視して再設計されており、マルチフレームワークもサポートしています。

  • つまり、PyTorch、TensorFlow、JAX からモデルを取り込んで、LiteRT 用のフラット バッファ形式に変換できます。

  • 2022 年以降、Google Play 開発者サービスの TensorFlow Lite は、カスタムモデル用の Android の公式オンデバイス ランタイムとなっています。

  • 本日、このプログラムが大幅にアップグレードされます。今回の講演では、このアップグレードについて詳しく説明します。

  • このアップグレードにより、最新の LiteRT CompiledModel API が導入されます。これには、1.4 倍高速になった新しい GPU 推論が含まれており、より小さな APK サイズでより小さなアプリを構築できるようになります。

  • では、なぜ Play 開発者サービスを通じて提供するのでしょうか。

  • いくつかのメリットがあります。これは、私が聞いている多くの講演で目にしている大きな課題です。アプリのサイズと APK の肥大化が、実際の課題になっています。

  • そのため、ランタイムが小さくなります。

  • ランタイムは APK に含まれていないため、シン クライアントに依存することになり、APK のサイズが約 8 MB 縮小されます。

  • 2 つ目のメリットは効率性です。

  • ランタイムのコピーは 1 つであるため、LiteRT ランタイムであるランタイムはすべてのアプリで共有されます。

  • 多数のアプリがある場合、それぞれで 8 MB 節約できることになります。

  • ディスク容量を節約してアプリを軽量化しようとしている場合、これはすぐに大きな問題になります。

  • 2 つ目は、メンテナンスされていることです。

  • Google がバグの修正をリリースし、GPU ドライバの課題に対処するため、お客様が対応する必要はありません。また、その他のセキュリティ アップデートもすべて自動アップデートで提供されます。

  • また、先ほどお話ししたように、GPU は高速化しており、1.4 倍高速な GPU を利用できます。これについても詳しく説明します。

  • また、カバレッジの観点から、古い Android デバイスにも提供できます。

  • Google Play 開発者サービスの LiteRT は、2016 年の Android 7 までさかのぼってサポートしています。

  • また、Fergus が紹介するように、今後スライドで紹介する他の新しいデバイスにも拡大していきます。

  • これは配送車両です。次のステップであるランタイムについて説明します。

  • LiteRT は新しいものではなく、試験運用版でもありません。

  • TensorFlow Lite と LiteRT は、現在すでに 25 万以上のアプリで実行されています。

  • 現在、38 億台以上のデバイスで動作しています。

  • 皆さんが使っているアプリの多くは、知らず知らずのうちに、この仕組みで動いているかもしれません。

  • これは、毎日 1 兆回以上の推論に相当します。

  • その規模では、あらゆるチップセットの問題が発生しています。ROM の問題や GPU ドライバの問題に対処でき、お客様に合わせた回避策をご用意しています。

  • LiteRT は、Google の主力アプリでも、アプリ、プラットフォーム、さらには複数のデバイスにわたって実行されています。

  • YouTube ショートのエフェクト、Meet の背景セグメンテーション、ボケ補正などは、リアルタイム動画と重い画像モデルがすべて LiteRT で実行される例です。

  • これは、ユースケースを迅速かつ魅力的なものにすることが非常に重要になるため、非常に重要です。

  • これは、これらのフラグシップ機能向けに強化されたものと同じランタイムであり、Google Play 開発者サービスを通じて提供されます。

デバイス上で AI を実行する理由

  • では、なぜオンデバイスにする必要があるのかについても触れておきましょう。

  • レイテンシ、プライバシー、オフラインでの作業、低コストなど、これらのいずれかが重要である場合は、オフラインで実行します。

  • 他にも理由はありますが、これらはオンデバイスで実行する理由としてよく耳にするものです。

  • また、デバイスでの実行が常に無料であるとは限りません。

  • これにはいくつかのニュアンスと課題があります。

  • また、こうした課題のいくつかについても説明し、今後の道筋についても説明します。

  • パフォーマンスについてですが、先ほどのアプリの例で見たように、レイテンシとメモリの予算は常に厳しいものです。

  • リアルタイムの動画と音声には、極めて高い効率性が求められます。

  • 単純なインタラクティブ機能でも、迅速に動作する必要があります。

  • そうしないと、ユーザー エンゲージメントが低下します。

  • ハードウェアの多様性: CPU 構成や GPU 構成が異なる SoC が数百種類あります。

  • これらすべてに対応するコードを記述することはできません。

  • こうした課題を解決するにはどうすればよいでしょうか。

  • APK サイズ: ネイティブ ランタイムをバンドルすると、ダウンロード サイズが大きくなり、コンバージョン率に影響する可能性があります。

  • カスタマイズ: モデルのトレーニング、品質、モデルサイズは、デプロイする前に非常に重要になります。

  • 優れたオンデバイス アプリを構築するのは大変なことです。

  • しかし、これらの状況のすべてにおいて、プラットフォームがユーザーに代わって作業を行うことを望んでいるのであって、その逆ではありません。

  • Google のオンデバイス ポートフォリオは、こうした課題に対処するために存在しています。これについては、次に説明します。

  • そこで、今日は 2 つのシンプルなソリューションに焦点を当てます。

  • Google Play 開発者サービスを介してオンデバイス AI を追加するには、2 つの簡単な方法があります。

  • パス 1 は ML Kit を使用するターンキーパスですね。

  • これらはすぐに使用できるビジョン API と言語 API で、ML の専門知識はほとんど必要ありません。多くのモデルが提供され、すべて Google Play 開発者サービスを通じて提供されます。

  • 次のスライドで、これらの例をいくつか紹介します。

  • カスタムパス。これは LiteRT を介して行われます。

  • LiteRT では、独自のモデルを導入して、CPU、GPU で実行できます。

  • ランタイムも Google Play 開発者サービスから提供されます。

  • つまり、APK にバンドルする必要はありません。

  • これらは二者択一ではありません。

  • また、これらを組み合わせることで、さらに効果を高めることができます。

  • これから数分で構築するのは、まさにそのようなシステムです。

  • まず、ML Kit の対象範囲について説明します。

  • ML Kit がすぐに利用できる機能は次のとおりです。

  • これらの機能の一部がニーズを満たしているとお考えであれば、それは素晴らしい出発点であり、すでに一歩先を行っていることになります。

  • ビジョン、バーコード、ドキュメント、テキスト、ポーズなど、多くの機能に対応しています。翻訳、スマート リプライ、エンティティ抽出などの言語 API もあります。

  • 重要なのは、これらのビジョン API や言語 API の多くが、内部で LiteRT を使用して実行されているということです。

  • ML Kit で必要な処理を行える場合は、ここから始めることができます。

  • また、そうでない場合は、独自のモデルを使用することもできます。

  • ここで重要なのは、前述のとおり、ML Kit とカスタムモデルは 2 つのランタイムではないということです。

  • これらは、内部で実行される同じ LiteRT につながる 2 つの正面玄関のようなものと考えてください。

  • LiteRT は、これら 2 つの機能をオーケストレートするランタイムとなります。

  • そのため、アプリですでに Google Play ストアを使用している場合は、ML Kit を使用することをおすすめします。

  • それ以外の場合は、LiteRT パスも確認します。

LiteRT の新機能

  • LiteRT の新機能についても触れておきましょう。

  • 先ほど、LiteRT は TensorFlow Lite の進化形だとお話ししました。

  • 最大の改善点は、GPU の高速化です。

  • 高速な GPU は OpenCL、OpenGL をサポートし、Metal バックエンドでもクロスプラットフォームをサポートしています。

  • そのため、必要に応じて複数のプラットフォームで使用できます。

  • また、よりシンプルです。

  • CPU、GPU、NPU の高速化に対応した統合ワークフローが 1 つあります。

  • CPU と GPU は現在 Play 開発者サービスに含まれていますが、NPU は現在 LiteRT を通じてバンドルされており、現在開発中です。

  • 以前は、アクセラレータごとに異なるデリゲート設定が必要だったということですね。

  • この操作は 1 回だけ行う必要があります。

  • つまり、CPU への自動フォールバックを備えた 1 つの API があり、専用の委任設定を行う必要はありません。

  • アクセラレータ呼び出しを使用すると、より少ないコードで、より簡単に管理できます。

  • また、前述のように、同じモデルが複数のプラットフォームで動作するため、強力です。

  • この柔軟性の部分が重要になります。

  • そのため、.tflite 形式は変わりません。

  • 既存のアプリがある場合は、引き続きこの形式を使用できます。

  • 新しいアプリを構築する場合は、このファイル形式を再度引き継ぐことができます。

  • また、以前は TensorFlow Lite に含まれていた Interpreter API もサポートしています。

  • 段階的な移行を検討している場合、Interpreter API は新しい LiteRT にも含まれているため、簡単に移行できます。

  • これがランタイムです。

  • これを使って何ができるかについて説明します。

  • 現在、Android アプリは次のように構築されています。

  • カメラ エフェクト、写真編集、スマート タイピング、ゲーム、アバターなど、あらゆる機能が対象です。

  • これらのアプリの多くは、リアルタイム機能を必要とします。

  • GPU はカメラから直接フレームを送信しているため、処理を迅速かつリアルタイムに行うことが重要になります。

  • これらの写真編集モデルの一部は、より重くなっています。

  • また、メガネを使ったデモでご覧いただいた AR / VR 機能も同様で、その多くは内部で LiteRT を実行しています。

  • これは、Google の主要な機能や Google Play 開発者サービスで使用されているものと同じランタイムです。そのため、APK に何もバンドルすることなく、これらすべての機能を利用できます。

  • では、アプリを構築してみましょう。Fergus が方法を説明します。

  • 再起動してインストールを完了しましょう。

  • Chintan さん、ありがとうございました。

  • そこで、Google Play 開発者サービスの LiteRT の新機能について少しお話しします。

  • ビルドする前に、もう一つお知らせがあります。今ご覧いただいたものはすべて、スマートフォンだけでなく、Wear OS と Android Automotive OS でも Google Play 開発者サービスの LiteRT を利用できます。

  • そのため、スマートウォッチなどのウェアラブル デバイスや車自体でオンデバイス モデルを実行できます。

  • 以前は、Wear のコンパニオン アプリとしてスマートフォンでアプリを使用することができましたが、現在はウェアラブル デバイス自体で実際にアプリを実行できるようになりました。Android Automotive も同様です。

  • 同じ API、同じ .tflite モデルを使用しながら、より多くのサーフェスに対応できます。

  • フォーム ファクタごとに個別に実装する必要はなく、これらの異なるデバイスすべてに同じ実装を使用できます。

  • そして、Play 開発者サービスの配布パラダイムの優れた点は、これをバックポートして古いデバイスでも実行できることです。

  • Android Automotive OS では、2020 年にリリースされた Android 11 までサポートしています。Wear OS では、Wear OS 4 以降をサポートしています。

  • 違いますね。

  • ここでは、新しいアプリを構築する場合と、既存のアプリをアップグレードする場合の 2 つの異なるパスについて説明します。

  • この点について、この後のプレゼンテーションで説明します。

  • そこで、ML Kit と LiteRT で実行されるカスタム GPU モデルを組み合わせて使用するサンプルアプリから始めます。

  • 既存のアプリをアップグレードする場合は、3 つの小さな変更が必要です。依存関係を更新するための Gradle の変更、Google Play 開発者サービスで LiteRT を有効にするための初期化の変更、Interpreter API から CompiledModel API への切り替えです。

  • ただし、アップグレード後のアプリを確認してからアップグレードする方が理にかなっているため、まずは新しいアプリを作成します。

サンプル アプリケーションの概要

  • 新しいアプリを構築する。

  • これから作成するアプリは次のとおりです。

  • カメラのライブビューが表示され、被写体は鮮明に、背景は芸術的なスタイルになります。

  • そこで、カメラフレームを取得します。

  • カメラフレームからの画像は、ML Kit の自撮りセグメンテーションを介してストリーミングされます。

  • これは、既存の ML Kit ターンキー API の 1 つです。

  • これにより、画像内のどのピクセルが画像内の人物に対応するかを基本的に示す前景マスクが生成されます。

  • 次に、スタイル変換タイプのモデルのようなカスタムモデルを使用して、画像にスタイルを適用します。

  • これをカメラフレーム全体に適用しますが、関心があるのは背景部分だけです。最終的には、元のカメラフレーム、マスク、スタイルの背景を取得し、簡単なロジックでそれらを組み合わせて、被写体を維持し、背景にスタイルを適用した合成フレームを作成します。

  • そこで、ターンキー API があればそれを利用し、アプリを差別化できるカスタム API も利用したいと考えています。

  • これは、ML Kit Selfie Segmentation API を呼び出す最初の部分です。

    // 1. Configure and create segmenter client
    val segmenter = Segmentation.getClient(
        SelfieSegmenterOptions.Builder()
            .setDetectorMode(
                SelfieSegmenterOptions.STREAM_MODE)
            .build())
    
    // 2. Prepare image & run inference
    fun processFrame(mediaImage: Image) {
      val image = InputImage.fromMediaImage(
          mediaImage, /* rotationDegrees= */ 0)
      segmenter.process(image)
          .addOnSuccessListener { mask -> … }
    }
  • これは通常の API と同じですね。

  • これは、Android デベロッパーにとっての基本中の基本です。

  • API ドキュメントを確認するか、Gemini に相談して構築を依頼するだけです。

  • でも、とても簡単です。

  • クライアントを作成するときに、いくつかのオプションを渡します。

  • この場合、動画フレームのストリームを処理できるように、ストリーミング モードを指定します。

  • そして、そこでこの process メソッドを呼び出すと、そのコールバックが、自撮りセグメンテーションによって計算されたマスクを返します。

  • 入力はカメラからの画像です。

  • これらの API はすべて、生のテンソルではなくアプリケーション レベルの型を使用するため、非常に高いレベルでプログラミングでき、非常に簡単です。

LiteRT のカスタムモデル

  • 2 つ目の部分であるカスタムモデルでは、PyTorch モデルを使用します。

  • Chintan は Kaggle や Hugging Face などからこれを取得したのだと思います。

  •  

  • そうですね。また、独自のものを変換することもできます。

  • 独自のモデルを構築できます。

  • すみません、戻ります。

  • スタックはこのような構成になっています。

  • PyTorch モデルを変換して量子化すると、LiteRT モデルが得られます。

  • その後、アプリと一緒に自由に配布できます。

  • アセットとしてアプリの一部にバンドルすることも、個別にダウンロードすることもできます。

  • どちらの方法でもかまいません。

  • そして、それが Google Play 開発者サービスの LiteRT ランタイム上で動作します。

  • また、CPU で最適化するための CPU カーネルがあり、GPU アクセラレータの名前は ML Drift です。

  • はい。先ほど申し上げたように、TFLite モデルを取得して、自分でモデルをトレーニングすることも、既存のモデルを見つけることもできます。どちらの方法でも機能します。

  • PyTorch からの変換は、わずか数行の Python コードで済みます。

    import litert_torch
    
    model = my_models.Stylizer().eval()
    
    # Convert with torch.export under the hood
    edge_model = litert_torch.convert(
        model, sample_inputs)
    edge_model.export("stylizer_f32.tflite")
  • つまり、LiteRT-Torch を pip インストールし、litert_torch.convert を呼び出して、ランダムな入力となる sample_inputs を渡し、TFLite ファイルをエクスポートします。

  • なお、LiteRT-Torch は ai-edge-torch の新しい名前です。

  • 量子化も非常に簡単です。

    # Python
    # previously ai_edge_quantizer
    from litert_quantizer import quantizer, recipe
    
    qt = quantizer.Quantizer("stylizer_f32.tflite")
    
    # int8 weights, float activations
    qt.load_quantization_recipe(
        recipe.dynamic_wi8_afp32())
    
    qt.quantize().export_model("stylizer_int8.tflite")
  • LiteRT Quantizer から量子化器とレシピをインポートします。

  • 量子化器を構築する際に、元の浮動小数点モデルを渡します。GPU に推奨される標準レシピを渡します。このレシピでは、8 ビット整数重みを使用しますが、アクティベーションは 32 ビット浮動小数点として保持されます。

  • 次に、quantize を呼び出し、export_model を呼び出して出力モデルを保存します。

  • 量子化されたモデルは、重みがはるかに小さく、より効率的に実行できます。

CompiledModel API を使用した GPU アクセラレーション

  • CompiledModel API と GPU の組み合わせは、まさにそのためにあります。

  • Chintan が述べたように、従来の TFLite GPU と比較して大幅に高速化されています。

  • 正確な数値はモデルによって多少異なりますが、1.4 倍は妥当な期待値です。

  • CPU 推論よりも最大 10 倍高速です。これは大きなメリットです。

  • 現在モデルを使用しているほとんどのユーザーは、おそらく CPU でモデルを実行しており、GPU で得られるパフォーマンスの 10 倍程度のパフォーマンスを逃しています。

  • これは大きなインセンティブです。

  • 高速化の仕組み

  • サポートされている機能はたくさんあります。たとえば、非同期実行では、AHardwareBuffer を取得して直接渡すことができます。また、カメラのレンダリング パイプラインから OpenGL バッファを取得して、テンソル バッファに直接渡すこともできます。

  • ゼロコピーは、Google がサポートするゼロコピーを最適化します。基本的に、低レイテンシの予算で処理しようとする場合、たとえば、数ミリ秒でフレームを処理する必要がある場合、メモリバッファを単純にコピーするだけでも、無視できないオーバーヘッドになる可能性があります。

  • そのため、リアルタイム要件が非常に高いアプリケーションでゼロコピー最適化を使用したい場合は、それに対応しています。

  • そして、Chintan が述べたように、GPU バックエンドとして OpenGL、OpenCL、Metal をサポートするクロスプラットフォームです。

  • 最後になりますが、ML Drift Optimizer はオープンソースです。

  • Apache ライセンスで使用できます。

  • 最近、Google によってオープンソース化されました。

  • 次に、アプリのコードです。

    // Kotlin
    // Build once, off the main thread.
    private val model: CompiledModel =
        CompiledModel.create(
            context.assets,
            "stylizer_int8.tflite",
            // Simple GPU setup!
            CompiledModel.Options(Accelerator.GPU),
            environment
        )
    
    // Reuse buffers across frames.
    private val inputBuffers  = model.createInputBuffers()
    private val outputBuffers = model.createOutputBuffers()
    
    fun stylize(input: FloatArray): FloatArray {
        inputBuffers[0].writeFloat(input)
        model.run(inputBuffers, outputBuffers) // sync
        return outputBuffers[0].readFloat()
    }
  • ご覧のとおり、非常に小さく、シンプルです。

  • そのため、モデルを一度作成したら、すべてのフレームで再利用します。

  • そこで、CompiledModel.create は .tflite ファイルを渡します。

  • GPU で実行することを指定し、入力と outputBuffers を作成するだけです。

  • そして、実際にすべてのフレームを処理するメインループで、入力と出力を渡してこの model.run を呼び出します。

  • inputBuffers に書き込んだ後、model.run を呼び出し、outputBuffers から読み取ります。

  • 違いますね。

  • はい。そこで GPU を指定するだけで、GPU で実行されます。

  • そこで CPU と GPU も指定すると、GPU の実行が試行されます。

  • 何らかの理由で GPU を使用できない場合は、CPU にフォールバックされます。

  • そのため、場合によっては、CPU フォールバックがアプリにとって十分に効率的でないことがあります。そのような場合は、このように GPU のみを指定します。

  • より一般的には、CPU フォールバックを使用します。

Google Play 開発者サービスで LiteRT を使用する

  • 違いますね。

  • そこで、バンドルされた LiteRT を使用してビルドしました。これはまだ Google Play 開発者サービスを使用していません。

  • そこで、Google Play 開発者サービスで LiteRT を使用する方法をご紹介します。

  • 変更は 3 つあります。1 つ目は依存関係の変更です。

    // Easier with CompiledModel API: 3 steps
    
    // 1. app/build.gradle: replace litert with play-services-litert
    - implementation 'com.google.ai.edge.litert:litert:...'
    + implementation 'com.google.android.gms:play-services-litert:17.0.0'  // also pulls in litert-api
  • Gradle ファイルやその他の場所、使用しているビルドシステム、Maven パッケージの依存関係を指定する場所など、変更する場所はどこでも構いません。

  • litert 依存関係または古い TensorFlow Lite Maven パッケージの依存関係を削除し、play-services-litert に置き換えます。

  • これにより、litert-api パッケージも取り込まれます。

  • LiteRT に依存するだけで、ランタイムがアプリから提供されるか、Google Play 開発者サービスから提供されるかを気にしない中間レイヤを作成する場合は、LiteRT API に依存し、環境を入力パラメータとして受け取ることができます。

  • 2 つ目のステップは環境パラメータです。

  • ここで依存関係の挿入を行い、Play 開発者サービスの LiteRT を使用することを指定します。

  • そこで、gms.litert.LiteRt をインポートし、初期化関数を呼び出して、コンテキスト(Android アクティビティ)を渡します。

    // 2. Initialize the LiteRT Environment from Play services (context = your Activity)
    - val environment = Environment.create(...)
    + import com.google.android.gms.litert.LiteRt
    + val environment = LiteRt.initialize(context).await()
    
    // 3. Pass environment whenever you call CompiledModel.create
    
    // That's it! The rest of your app uses the normal CompiledModel APIs from litert-api.
  • これは非同期処理であるため、.await を呼び出すか、残りの処理をコールバックに基づいて行う必要があります。

  • 最後に、環境を取得したら、CompiledModel.create を呼び出すたびにその環境を渡します。

  • これにより、Play 開発者サービスの Play 開発者サービス LiteRT ランタイムが使用されるようになります。

  • 以上です。

  • つまり、アプリの残りの部分では、LiteRT API の LiteRT CompiledModel API とまったく同じ API を使用します。

  • 推論コードの残りの部分は、Play 開発者サービスを使用する場合でも変更されません。

  • この移行は段階的に行うことができます。

  • 一度にすべてを行う必要はなく、コンポーネントを 1 つずつ移行できます。

  • 大規模なアプリの場合は、LiteRT を使用している各コンポーネントを移行するのが理にかなっています。1 つずつ移行して、1 つずつテストできます。

  • 唯一の注意点は、サイズ削減のメリット、つまりスペースの節約は、バンドルされたランタイムへの最後の依存関係を削除したときにのみ得られるということです。

  • また、その場合、もう 1 つの注意点として、Google Play 開発者サービスの LiteRT とバンドルされた LiteRT の間でオブジェクトを混在させないようにする必要があります。

  • そのため、Google Play 開発者サービスの LiteRT からテンソル、コンパイル済みモデル、インタープリタを取得する場合は、それらをバンドルされた LiteRT に渡さないでください。逆の場合も同様です。

  • それ以外の場合は、これらを組み合わせても問題ありません。

  • 違いますね。

  • 以上が、新しいアプリの作成方法です。

  • 次に、既存のアプリをアップグレードする方法について説明します。

  • そのため、Interpreter API を使用する既存のアプリがあるかもしれません。

  • これは、インタープリタをメインクラスとして使用する、以前の TFLite API と呼ばれるものです。

  • 左側が Interpreter API を使用したコードで、右側が新しい CompiledModel API コードです。

    // Dependencies (Gradle):
    // play-services-tflite-java and/or litert (bundled)
    
    // Init
    TfLite.initialize(context).await()
    
    // Load
    val interpreterOptions =
        InterpreterApi.Options().apply {
          runtime = TfLiteRuntime.FROM_SYSTEM_ONLY
        }
    
    val interpreter =
        InterpreterApi.create(modelFile, interpreterOptions)
    
    // Prepare inputs/outputs
    val inputs = …
    val outputs = …
    
    // Execute
    interpreter.run(inputs, outputs)
    // Dependencies (Gradle):
    // play-services-litert
    
    // Init
    val environment =
      LiteRt.initialize(context).await()
    
    // Load
    val modelOptions = CompiledModel.Options(Accelerator.CPU)
    
    val model =
        CompiledModel.create(modelPath, modelOptions, environment)
    
    // Prepare inputs/outputs
    val inputBuffers = model.createInputBuffers()
    val outputBuffers = model.createOutputBuffers()
    
    // Execute
    model.run(inputBuffers, outputBuffers
  • つまり、TfLite.initialize を LiteRt.initialize に変更するだけで、環境が返されるようになります。

  • 次に、環境を CompiledModel.create に渡します。

  • これは小さな変更であり、APK のサイズは増加しません。

  • 次のスライドでは、GPU を使用してこれを行う方法を説明します。これは、もう 1 つのオプションです。

  • GPU を有効にするために必要な追加のコードはこれだけです。

    // Dependencies (Gradle):
    // play-services-tflite-java and/or litert (bundled)
    
    // Init
    val initOptions = TfLiteInitializationOptions.builder()
        .setEnableGpuDelegateSupport(true).build()
    TfLite.initialize(context, initOptions).await()
    val useGpu =
        TfLiteGpu.isGpuDelegateAvailable(context).await()
    
    // Load
    val interpreterOptions =
        InterpreterApi.Options().apply {
          runtime = TfLiteRuntime.FROM_SYSTEM_ONLY
        }
    if (useGpu) {
        interpreterOptions.addDelegateFactory(
            GpuDelegateFactory());
    }
    val interpreter =
        InterpreterApi.create(modelFile, interpreterOptions)
    
    // Prepare inputs/outputs
    val inputs = …
    val outputs = …
    
    // Execute
    interpreter.run(inputs, outputs)
    // Dependencies (Gradle):
    // play-services-litert
    
    // Init
    val initOptions = LiteRtInitializationOptions.builder()
        .setEnableGpu(true).build()
    val environment = LiteRt.initialize(context, initOptions).await()
    
    // Load
    val modelOptions =
        CompiledModel.Options(Accelerator.CPU, Accelerator.GPU)
    
    val model =
        CompiledModel.create(modelPath, modelOptions, environment)
    
    // Prepare inputs/outputs
    val inputBuffers = model.createInputBuffers()
    val outputBuffers = model.createOutputBuffers()
    
    // Execute
    model.run(inputBuffers, outputBuffers)
  • ご覧のとおり、GPU を有効にする新しい API のコードは、以前のコードよりも少しシンプルになっています。

  • そのため、LiteRT 初期化オプションで setEnableGpu を最初に呼び出し、それを LiteRt.initialize に渡すと、GPU アクセラレータのコードがアプリに読み込まれます。

  • そして、modelOptions を構築して CompiledModel.create に渡すときに、特定のモデルにどのアクセラレータを使用するかを指定します。他のモデルを GPU のみで実行するか、GPU と CPU フォールバックで実行するか、CPU のみで実行するかを指定できます。

  • それでは

  • ご覧のとおり、新しい API では GPU の有効化がはるかに簡単になっています。

  • では、実際にどのようなメリットがあるのでしょうか。

  • Chintan が説明します。

  • それでは

  • Fergus さん、ありがとうございました。

  • では、これらすべてから何が得られるかについてお話ししましょう。

  • アプリを構築すると言いましたので、実際に構築したアプリを見てみましょう。

  • これが私です。ここでは、ML Kit を使用して私を見つけています。つまり、ML Kit のようなターンキー API によってセグメンテーションが行われています。

  • これは、私の頭を検出しているところです。

  • 次に、LiteRT は、スタイル変換に似た別のモデルを使用して背景をペイントします。

  • その他のメリットとしては、前述のとおり、約 8 MB のスペースを節約できること、すべてのアプリ(少なくともここで共有されているアプリ)で使用されるランタイムが 1 つになること、自動更新が提供されること、GPU が高速化され、リッチなユーザー エクスペリエンスのためにリアルタイム処理が必要なものでも 30 フレーム/秒近くを実現できることが挙げられます。

  • また、これらすべてを手作業で行う必要がないのもメリットです。

  • また、Android で新たにリリースされたスキルもあります。基本的には、Android LiteRT スキルを公開しました。これにより、オンデバイス ML を数分でアップグレードできます。

  • これを使用して、既存のアプリをアップグレードしたり、新しいアプリをスキャフォールディングしたり、アプリをアップグレードしてこの新しい Play 開発者サービス統合を使用したりできます。

  • これらすべてが利用可能です。

  • リンクはスライドにあります。GitHub にもあり、ドキュメントやブログにも表示されます。

  • そうですね。すぐに変更を加えたいと考えている方にも役立つはずです。

  • では、Fergus から今後の展望について説明してもらいましょう。

LiteRT のロードマップと概要

  • そのため、Google は LiteRT を使用したオンデバイス AI に取り組んでいます。

  • Google は、GPU アクセラレーションとモデル カバレッジの改善に多大な投資を続けており、オープンソース バンドルの LiteRT と、Google Play 開発者サービスの LiteRT である Google Play 開発者サービスの LiteRT Maven パッケージの両方について、LiteRT 2.x の定期的なアップデートを提供し続けています。

  • また、下位互換性も引き続き提供していきます。

  • Play 開発者サービス ランタイムは、古い SDK でビルドされたアプリを引き続きサポートします。

  • うん

  • これはユーザーにとって非常に重要であり、Google にとっても非常に重要です。

  • LiteRT 1.x SDK は現在メンテナンス モードになっているため、重要なセキュリティ修正のみが適用されます。

  • 2024 年に非推奨となった NNAPI は、2027 年から削除される予定です。

  • そのため、NNAPI を使用している場合は、バンドルされた LiteRT で GPU アクセラレーションまたは NPU アクセラレーションを使用するように切り替えてください。

  • 違いますね。

  • うん

  • そのため、2.x と CompiledModel への移行を早めに行うほど、後で行う作業が少なくなります。

  • 重要なポイントは 3 つあります。

  • 構築しましょう。

  • まずは ML Kit から始めましょう。必要な機能がすでに備わっていれば、それで十分です。

  • そうでない場合は、カスタムモデルに LiteRT を使用します。

  • Google Play 開発者サービスの LiteRT にアップグレードすると、APK のサイズを削減し、共有ランタイムを利用できます。

  • また、より高速でシンプルな GPU 推論のために、CompiledModel API の使用を検討してください。

  • うん

  • すべては、AI Edge の LiteRT ドキュメント サイト(URL はこちら)に記載されています。

  • そうですね。

  • ご参加いただきありがとうございました。皆様がどのようなものを構築されるのか楽しみにしています。

コード

コード スニペット

「サンプル アプリケーションの概要」の 15:09 の Kotlin スニペット
// 1. Configure and create segmenter client
val segmenter = Segmentation.getClient(
    SelfieSegmenterOptions.Builder()
        .setDetectorMode(
            SelfieSegmenterOptions.STREAM_MODE)
        .build())

// 2. Prepare image & run inference
fun processFrame(mediaImage: Image) {
  val image = InputImage.fromMediaImage(
      mediaImage, /* rotationDegrees= */ 0)
  segmenter.process(image)
      .addOnSuccessListener { mask -> … }
}
「LiteRT のカスタムモデル」の 17:34 の Python スニペット
import litert_torch

model = my_models.Stylizer().eval()

# Convert with torch.export under the hood
edge_model = litert_torch.convert(
    model, sample_inputs)
edge_model.export("stylizer_f32.tflite")
「LiteRT のカスタムモデル」の 18:04 の Python スニペット
# Python
# previously ai_edge_quantizer
from litert_quantizer import quantizer, recipe

qt = quantizer.Quantizer("stylizer_f32.tflite")

# int8 weights, float activations
qt.load_quantization_recipe(
    recipe.dynamic_wi8_afp32())

qt.quantize().export_model("stylizer_int8.tflite")
「CompiledModel API による GPU アクセラレーション」の 21:06 の Kotlin スニペット
// Kotlin
// Build once, off the main thread.
private val model: CompiledModel =
    CompiledModel.create(
        context.assets,
        "stylizer_int8.tflite",
        // Simple GPU setup!
        CompiledModel.Options(Accelerator.GPU),
        environment
    )

// Reuse buffers across frames.
private val inputBuffers  = model.createInputBuffers()
private val outputBuffers = model.createOutputBuffers()

fun stylize(input: FloatArray): FloatArray {
    inputBuffers[0].writeFloat(input)
    model.run(inputBuffers, outputBuffers) // sync
    return outputBuffers[0].readFloat()
}
「Google Play 開発者サービスで LiteRT を使用する」の 22:36 の Groovy スニペット
// Easier with CompiledModel API: 3 steps

// 1. app/build.gradle: replace litert with play-services-litert
- implementation 'com.google.ai.edge.litert:litert:...'
+ implementation 'com.google.android.gms:play-services-litert:17.0.0'  // also pulls in litert-api
「Google Play 開発者サービスで LiteRT を使用する」の 23:43 の Kotlin スニペット
// 2. Initialize the LiteRT Environment from Play services (context = your Activity)
- val environment = Environment.create(...)
+ import com.google.android.gms.litert.LiteRt
+ val environment = LiteRt.initialize(context).await()

// 3. Pass environment whenever you call CompiledModel.create

// That's it! The rest of your app uses the normal CompiledModel APIs from litert-api.
「Google Play 開発者サービスで LiteRT を使用する」の 25:45 の Kotlin スニペット
// Dependencies (Gradle):
// play-services-tflite-java and/or litert (bundled)

// Init
TfLite.initialize(context).await()

// Load
val interpreterOptions =
    InterpreterApi.Options().apply {
      runtime = TfLiteRuntime.FROM_SYSTEM_ONLY
    }

val interpreter =
    InterpreterApi.create(modelFile, interpreterOptions)

// Prepare inputs/outputs
val inputs = …
val outputs = …

// Execute
interpreter.run(inputs, outputs)
「Google Play 開発者サービスで LiteRT を使用する」の 25:45 の Kotlin スニペット
// Dependencies (Gradle):
// play-services-litert

// Init
val environment =
  LiteRt.initialize(context).await()

// Load
val modelOptions = CompiledModel.Options(Accelerator.CPU)

val model =
    CompiledModel.create(modelPath, modelOptions, environment)

// Prepare inputs/outputs
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()

// Execute
model.run(inputBuffers, outputBuffers
「Google Play 開発者サービスで LiteRT を使用する」の 26:25 の Kotlin スニペット
// Dependencies (Gradle):
// play-services-tflite-java and/or litert (bundled)

// Init
val initOptions = TfLiteInitializationOptions.builder()
    .setEnableGpuDelegateSupport(true).build()
TfLite.initialize(context, initOptions).await()
val useGpu =
    TfLiteGpu.isGpuDelegateAvailable(context).await()

// Load
val interpreterOptions =
    InterpreterApi.Options().apply {
      runtime = TfLiteRuntime.FROM_SYSTEM_ONLY
    }
if (useGpu) {
    interpreterOptions.addDelegateFactory(
        GpuDelegateFactory());
}
val interpreter =
    InterpreterApi.create(modelFile, interpreterOptions)

// Prepare inputs/outputs
val inputs = …
val outputs = …

// Execute
interpreter.run(inputs, outputs)
「Google Play 開発者サービスで LiteRT を使用する」の 26:25 の Kotlin スニペット
// Dependencies (Gradle):
// play-services-litert

// Init
val initOptions = LiteRtInitializationOptions.builder()
    .setEnableGpu(true).build()
val environment = LiteRt.initialize(context, initOptions).await()

// Load
val modelOptions =
    CompiledModel.Options(Accelerator.CPU, Accelerator.GPU)

val model =
    CompiledModel.create(modelPath, modelOptions, environment)

// Prepare inputs/outputs
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()

// Execute
model.run(inputBuffers, outputBuffers)