On-Device Personalization のデベロッパー ガイド

On-Device Personalization(ODP)は、アプリからエンドユーザーの情報を保護するように設計されています。アプリケーションは ODP を使用してエンドユーザー向けにプロダクトやサービスをカスタマイズしますが、ユーザー向けに行われたカスタマイズを正確に確認することはできません(アプリケーションとエンドユーザーの間で ODP の外部で直接やり取りが行われない限り)。機械学習モデルや統計分析を使用するアプリケーションの場合、ODP は、適切な差分プライバシー メカニズムを使用して、それらが適切に匿名化されるようにする一連のサービスとアルゴリズムを提供します。詳しくは、On-Device Personalization の説明をご覧ください。

ODP は IsolatedProcess でデベロッパー コードを実行します。このコードは、ネットワーク、ローカル ディスク、またはデバイスで実行されているその他のサービスには直接アクセスできませんが、ローカルに保存された次のデータソースにはアクセスできます。

  • RemoteData - リモートのデベロッパーが運用するバックエンドからダウンロードされた不変の Key-Value データ(該当する場合)。
  • LocalData - デベロッパーがローカルに保持する変更可能な Key-Value データ(該当する場合)。
  • UserData - プラットフォームから提供されるユーザーデータ。

次の出力がサポートされています。

  • 永続出力: これらの出力は、今後のローカル処理で使用できます。表示される出力の生成、フェデレーション ラーニングによるモデル トレーニング、フェデレーション分析によるクロスデバイス統計分析に使用できます。
    • デベロッパーは、リクエストと処理結果をローカルの REQUESTS テーブルに書き込むことができます。
    • デベロッパーは、以前のリクエストに関連する追加データを EVENTS テーブルに書き込むことができます。
  • 表示された出力:
    • デベロッパーは、ODP によってレンダリングされた HTML を、SurfaceView 内の WebView で返すことができます。そこでレンダリングされたコンテンツは、呼び出し元のアプリからは見えません。
    • デベロッパーは、ODP から提供されたイベント URL を HTML 出力内に埋め込むことで、レンダリングされた HTML に対するユーザー操作のログ記録と処理をトリガーできます。ODP は、これらの URL へのリクエストをインターセプトし、コードを呼び出して EVENTS テーブルに書き込まれるデータを生成します。

クライアント アプリと SDK は、ODP API を使用して ODP を呼び出し、SurfaceView に HTML コンテンツを表示できます。SurfaceView でレンダリングされたコンテンツは、呼び出し元のアプリには表示されません。クライアント アプリまたは SDK は、ODP で開発したものとは異なるエンティティにすることができます。

ODP サービスは、ODP を呼び出して UI 内にパーソナライズされたコンテンツを表示するクライアント アプリを管理します。デベロッパーが提供するエンドポイントからコンテンツをダウンロードし、ダウンロードしたデータの後処理のロジックを呼び出します。また、IsolatedProcess と他のサービスやアプリとの間のすべての通信を仲介します。

クライアント アプリは、OnDevicePersonalizationManager クラスのメソッドを使用して、IsolatedProcess で実行されているデベロッパーのコードとやり取りします。IsolatedProcess で実行されるデベロッパーのコードは、IsolatedService クラスを拡張し、IsolatedWorker インターフェースを実装します。IsolatedService は、リクエストごとに IsolatedWorker のインスタンスを作成する必要があります。

次の図は、OnDevicePersonalizationManagerIsolatedWorker のメソッドの関係を示しています。

OnDevicePersonalizationManagerIsolatedWorker の関係の図。

クライアント アプリが、IsolatedService という名前で execute メソッドを使用して ODP を呼び出します。ODP サービスは、IsolatedWorkeronExecute メソッドに呼び出しを転送します。IsolatedWorker は、保持されるレコードと表示するコンテンツを返します。ODP サービスは、永続的な出力を REQUESTS テーブルまたは EVENTS テーブルに書き込み、表示された出力への不透明な参照をクライアント アプリに返します。クライアント アプリは、この不透明な参照を今後の requestSurfacePackage 呼び出しで使用して、UI 内に任意の表示コンテンツを表示できます。

永続出力

ODP サービスは、デベロッパーが onExecute の実装を返した後も、REQUESTS テーブルにレコードを保持します。REQUESTS テーブルの各レコードには、ODP サービスによって生成されたリクエストごとの一般的なデータと、返された Rows のリストが含まれています。各 Row には、(key, value) ペアのリストが含まれています。各値はスカラー、文字列、または blob です。数値は集計後に報告でき、文字列または blob データはローカルまたは中央の差分プライバシーを適用した後に報告できます。デベロッパーは、後続のユーザー操作イベントを EVENTS テーブルに書き込むこともできます。EVENTS テーブルの各レコードは、REQUESTS テーブルの行に関連付けられます。ODP サービスは、呼び出し元のアプリのタイムスタンプとパッケージ名、ODP デベロッパーの APK を各レコードに透過的に記録します。

始める前に

ODP を使用して開発を開始する前に、パッケージ マニフェストを設定し、デベロッパー モードを有効にする必要があります。

パッケージ マニフェストの設定

ODP を使用するには、以下が必要です。

  1. AndroidManifest.xml<property> タグ。ODP 構成情報が含まれるパッケージ内の XML リソースを参照します。
  2. 次の例に示すように、IsolatedService を拡張するクラスを識別する AndroidManifest.xml<service> タグ。<service> タグ内のサービスでは、属性 exportedisolatedProcesstrue に設定する必要があります。
  3. ステップ 1 で指定した XML リソース内の <service> タグ。ステップ 2 のサービスクラスを識別します。2 番目の例に示すように、<service> タグ自体に ODP 固有の追加設定も含める必要があります。

AndroidManifest.xml

<!-- Contents of AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.odpsample" >
    <application android:label="OdpSample">
        <!-- XML resource that contains other ODP settings. -->
        <property android:name="android.ondevicepersonalization.ON_DEVICE_PERSONALIZATION_CONFIG"
                  android:resource="@xml/OdpSettings"></property>
        <!-- The service that ODP binds to. -->
        <service android:name="com.example.odpsample.SampleService"
                android:exported="true" android:isolatedProcess="true" />
    </application>
</manifest>

XML リソースの ODP 固有のマニフェスト

<property> タグで指定した XML リソース ファイルでは、<service> タグでサービスクラスを宣言し、ODP がコンテンツをダウンロードして RemoteData テーブルに入力する URL エンドポイントも指定する必要があります。次の例をご覧ください。連携コンピューティング機能を使用する場合は、連携コンピューティング クライアントが接続する連携コンピューティング サーバー URL エンドポイントも指定する必要があります。

<!-- Contents of res/xml/OdpSettings.xml -->
<on-device-personalization>
   <!-- Name of the service subclass -->
   <service name="com.example.odpsample.SampleService">
     <!-- If this tag is present, ODP will periodically poll this URL and
          download content to populate REMOTE_DATA. Developers that do not need to
          download content from their servers can skip this tag. -->
     <download-settings url="https://example.com/get" />
     <!-- If you want to use federated compute feature to train a model, you
          need to specify this tag. -->
     <federated-compute-settings url="https://fcpserver.example.com/" />
   </service>
</on-device-personalization>

デベロッパー モードを有効にする

Android Studio ドキュメントの開発者向けオプションを有効にするの手順に沿って、デベロッパー モードを有効にします。

スイッチとフラグの設定

ODP には、特定の機能を制御するために使用されるスイッチとフラグのセットがあります。

  • _global_killswitch: すべての ODP 機能用のグローバル スイッチ。ODP を使用するには false に設定します。
  • _federated_compute_kill_switch: _ODP のすべてのトレーニング(連携学習)機能を制御するスイッチ。トレーニングを使用する場合は false に設定します。
  • _caller_app_allowlist: ODP の呼び出しを許可するユーザーを制御します。アプリ(pkg 名、[省略可] 証明書)をここに追加するか、* に設定してすべてを許可します。
  • _isolated_service_allowlist: 分離サービス プロセスで実行できるサービスを制御します。

次のコマンドを実行すると、制限なしで ODP を使用するようにすべてのスイッチとフラグを構成できます。

# Set flags and killswitches
adb shell device_config set_sync_disabled_for_tests persistent
adb shell device_config put on_device_personalization global_kill_switch false
adb shell device_config put on_device_personalization federated_compute_kill_switch false
adb shell device_config put on_device_personalization caller_app_allow_list \"*\"
adb shell device_config put on_device_personalization isolated_service_allow_list \"*\"

デバイス側 API

ODP については、Android API リファレンス ドキュメントをご覧ください。

IsolatedService とのやり取り

IsolatedService クラスは、ODP を対象とした開発を計画しているすべてのデベロッパーが拡張し、分離プロセスで実行されていることをパッケージ マニフェストで宣言する必要がある抽象基本クラスです。ODP サービスは、分離されたプロセスでこのサービスを開始し、リクエストを行います。IsolatedService は、ODP サービスからリクエストを受け取り、そのリクエストを処理する IsolatedWorker を作成します。

デベロッパーは、クライアント アプリのリクエスト、ダウンロードの完了、レンダリングされた HTML によってトリガーされるイベントを処理するために、IsolatedWorker インターフェースのメソッドを実装する必要があります。これらのメソッドはすべてデフォルトで NoOps 実装になっているため、デベロッパーは、関係のないメソッドの実装をスキップできます。

OnDevicePersonalizationManager クラスは、独立したプロセスで実行されるデベロッパー実装の IsolatedService を、アプリと SDK がやり取りするための API を提供します。次のような用途が考えられます。

SurfaceView に表示する HTML コンテンツを生成する

表示するコンテンツを生成するために、呼び出し元アプリは、OnDevicePersonalizationManager#execute を使用して、返された SurfacePackageToken オブジェクトを後続の requestSurfacePackage 呼び出しで使用して、SurfaceView でレンダリングされる結果をリクエストできます。

成功すると、ODP サービスによってレンダリングされたビューの SurfacePackage でレシーバーが呼び出されます。クライアント アプリは、ビュー階層内の SurfaceViewSurfacePackage を挿入する必要があります。

アプリが、前の OnDevicePersonalizationManager#execute 呼び出しによって返された SurfacePackageToken を使用して requestSurfacePackage 呼び出しを行うと、ODP サービスは IsolatedWorker#onRender を呼び出して、フェンス付きフレーム内でレンダリングされる HTML スニペットを取得します。このフェーズでは、デベロッパーは LocalData または UserData にアクセスできません。これにより、デベロッパーが生成された HTML のアセット取得 URL 内に機密性の高い UserData を埋め込むことがなくなります。デベロッパーは IsolatedService#getEventUrlProvider を使用して、生成された HTML に含めるトラッキング URL を生成できます。HTML がレンダリングされると、ODP サービスはこれらの URL へのリクエストをインターセプトし、IsolatedWorker#onEvent を呼び出します。onRender() を実装するときに getRemoteData() を呼び出すことができます。

HTML コンテンツ内のイベントをトラッキングする

EventUrlProvider クラスは、イベント トラッキング URL を生成するための API を提供します。デベロッパーはこの URL を HTML 出力に含めることができます。HTML がレンダリングされると、ODP はイベント URL のペイロードを使用して IsolatedWorker#onEvent を呼び出します。

ODP サービスは、レンダリングされた HTML 内の ODP 生成イベント URL へのリクエストをインターセプトし、IsolatedWorker#onEvent を呼び出して、返された EventLogRecordEVENTS テーブルに記録します。

永続的な結果を書き込む

OnDevicePersonalizationManager#execute を使用すると、サービスは永続ストレージ(REQUESTS テーブルと EVENTS テーブル)にデータを書き込むことができます。これらのテーブルに書き込めるエントリは次のとおりです。

  • REQUESTS テーブルに追加する RequestLogRecord
  • EVENTS テーブルに追加される EventLogRecord オブジェクトのリスト。各オブジェクトには、以前に書き込まれた RequestLogRecord へのポインタが含まれます。

オンデバイス ストレージ内の永続的な結果は、フェデレーション ラーニングでモデル トレーニングに使用できます。

デバイス上のトレーニング タスクの管理

連携コンピューティング トレーニング ジョブが開始され、ODP を採用しているデベロッパーから提供されたトレーニング サンプルを取得しようとすると、ODP サービスは IsolatedWorker#onTrainingExample を呼び出します。onTrainingExample() を実装するときに、getRemoteData()getLocalData()getUserData()getLogReader() を呼び出すことができます。

連携コンピューティング ジョブをスケジュールまたはキャンセルするには、すべての ODP IsolatedService の API を提供する FederatedComputeScheduler クラスを使用します。各連携コンピューティング ジョブは、その母集団名で識別できます。

新しい連携コンピューティング ジョブをスケジュールする前に:

  • リモートの連携コンピューティング サーバーに、この母集団名のタスクがすでに作成されている必要があります。
  • 連携コンピューティング サーバーの URL エンドポイントは、パッケージ マニフェストの設定federated-compute-settings タグを使用してすでに指定されている必要があります。

永続出力との相互作用

以降のセクションでは、ODP で永続出力を使用する方法について説明します。

ローカル テーブルを読み取る

LogReader クラスは、REQUESTS テーブルと EVENTS テーブルを読み取る API を提供します。これらのテーブルには、onExecute() または onEvent() の呼び出し中に IsolatedService によって書き込まれたデータが含まれています。これらのテーブルのデータは、フェデレーション ラーニングによるモデル トレーニングまたはフェデレーション分析によるクロスデバイス統計分析に使用できます。

一時保存したコンテンツの操作

以降のセクションでは、ODP でダウンロードしたコンテンツを操作する方法について説明します。

サーバーからコンテンツをダウンロードする

ODP サービスは、IsolatedService のパッケージ マニフェストで宣言された URL からコンテンツを定期的にダウンロードし、ダウンロードが完了すると onDownloadCompleted を呼び出します。ダウンロードされるファイルは、Key-Value ペアを含む JSON ファイルです。

ODP を導入するデベロッパーは、ダウンロードされたコンテンツのうち、RemoteData テーブルに追加するものと削除すべきのサブセットを選択できます。デベロッパーはダウンロードしたコンテンツを変更できません。これにより、RemoteData テーブルにユーザーデータが含まれなくなります。また、デベロッパーは必要に応じて LocalData テーブルにデータを自由に入力できます。たとえば、事前に計算された結果をキャッシュに保存できます。

ダウンロード リクエストの形式

ODP は、デベロッパーのパッケージ マニフェストで宣言された URL エンドポイントを定期的にポーリングしてコンテンツを取得し、RemoteData テーブルに入力します。

このエンドポイントは、後述するように JSON レスポンスを返すことが想定されています。JSON レスポンスには、送信するデータのバージョンを識別する syncToken と、入力する Key-Value ペアのリストが含まれている必要があります。syncToken の値は、UTC の 1 時間の境界にクランプされた秒単位のタイムスタンプである必要があります。ODP は、ダウンロード リクエストの一部として、以前に完了したダウンロードの syncToken と、デバイスの国をダウンロード URL の syncToken パラメータと country パラメータとして提供します。サーバーは、以前の syncToken を使用して増分ダウンロードを実装できます。

ダウンロード ファイルの形式

ダウンロードされたファイルは、次の構造の JSON ファイルです。JSON ファイルには、ダウンロードされるデータのバージョンを識別する syncToken が含まれていることが想定されています。syncToken は、1 時間の境界にクランプされた UTC タイムスタンプで、前のダウンロードの syncToken より大きい必要があります。syncToken が両方の要件を満たさない場合、ダウンロードしたコンテンツは処理されずに破棄されます。

contents フィールドは、(キー、データ、エンコード)タプルのリストです。key は UTF-8 文字列にする必要があります。encoding フィールドは、data フィールドのエンコード方法を指定するオプションのパラメータです。「utf8」または「base64」のいずれかに設定でき、デフォルトでは「utf8」とみなされます。onDownloadCompleted(). を呼び出す前に、key フィールドが String オブジェクトに変換され、data フィールドがバイト配列に変換されます。

{
  // syncToken must be a UTC timestamp clamped to an hour boundary, and must be
  // greater than the syncToken of the previously completed download.
  "syncToken": <timeStampInSecRoundedToUtcHour>,
  "contents": [
    // List of { key, data } pairs.
    { "key": "key1",
      "data": "data1"
    },
    { "key": "key2",
      "data": "data2",
      "encoding": "base64"
    },
    // ...
  ]
}

サーバーサイド API

このセクションでは、連携コンピューティング サーバー API を操作する方法について説明します。

連携 Compute Server API

クライアント側で連携コンピューティング ジョブをスケジュールするには、リモート連携コンピューティング サーバー上に母集団名を指定したタスクが必要です。このセクションでは、このようなタスクを連携コンピューティング サーバー上に作成する方法について説明します。

連携コンピューティングのクライアント / サーバー トポロジの図。

タスクビルダー用に新しいタスクを作成する場合、ODP デベロッパーは次の 2 セットのファイルを用意する必要があります。

  1. tff.learning.models.save_functional_model API 呼び出しを介して保存された tff.learning.models.FunctionalModel モデル。GitHub リポジトリでサンプルを 1 つ確認できます。
  2. ポリシー、連携学習の設定、差分プライバシーの設定を含む fcp_server_config.json。fcp_server_config.json の例を次に示します。
{
  # Task execution mode.
  mode: TRAINING_AND_EVAL
  # Identifies the set of client devices that participate.
  population_name: "mnist_cnn_task"
  policies {
    # Policy for sampling on-device examples. It is checked every
    # time a device is attempting to start a new training.
    min_separation_policy {
      # The minimum separation required between two successful
      # consective task executions. If a client successfully contributes
      # to a task at index `x`, the earliest they can contribute again
      # is at index `(x + minimum_separation)`. This is required by
      # DP.
      minimum_separation: 1
    }
    data_availability_policy {
      # The minimum number of examples on a device to be considered
      # eligible for training.
      min_example_count: 1
    }
    # Policy for releasing training results to developers adopting ODP.
    model_release_policy {
      # The maximum number of training rounds.
      num_max_training_rounds: 512
    }
  }

  # Federated learning setups. They are applied inside Task Builder.
  federated_learning {
    # Use federated averaging to build federated learning process.
    # Options you can choose:
      # * FED_AVG: Federated Averaging algorithm
      #            (https://arxiv.org/abs/2003.00295)
      # * FED_SGD: Federated SGD algorithm
      #            (https://arxiv.org/abs/1602.05629)
    type: FED_AVG
    learning_process {
      # Optimizer used at client side training. Options you can choose:
      # * ADAM
      # * SGD
      client_optimizer: SGD
      # Learning rate used at client side training.
      client_learning_rate: 0.02
      # Optimizer used at server side training. Options you can choose:
      # * ADAM
      # * SGD
      server_optimizer: SGD
      # Learning rate used at server side training.
      server_learning_rate: 1.0
      runtime_config {
        # Number of participating devices for each round of training.
      report_goal: 2
      }
      metrics {
        name: "sparse_categorical_accuracy"
      }
    }
    evaluation {
      # A checkpoint selector controls how checkpoints are chosen for
      # evaluation. One evaluation task typically runs per training
      # task, and on each round of execution, the eval task
      # randomly picks one checkpoint from the past 24 hours that has
      # been selected for evaluation by these rules.
      # Every_k_round and every_k_hour are definitions of quantization
      # buckets which each checkpoint is placed in for selection.
      checkpoint_selector: "every_1_round"
      # The percentage of a populate that should delicate to this
      # evaluation task.
      evaluation_traffic: 0.2
      # Number of participating devices for each round of evaluation.
      report_goal: 2
    }
  }

  # Differential Privacy setups. They are enforced inside the Task
  # Builder.
  differential_privacy {
    # * fixed_gaussian: DP-SGD with fixed clipping norm described in
    #                   "Learning Differentially Private Recurrent
    #                   Language Models"
    #                   (https://arxiv.org/abs/1710.06963).
    type: FIXED_GAUSSIAN
    #   The value of the clipping norm.
    clip_norm: 0.1
    # Noise multiplier for the Gaussian noise.
    noise_multiplier: 0.1
  }
}

その他のサンプルは、GitHub リポジトリで確認できます。

これらの 2 つの入力を準備したら、タスクビルダーを呼び出してアーティファクトを構築し、新しいタスクを生成します。詳細な手順については、GitHub リポジトリをご覧ください。