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

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

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

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

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

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

クライアント アプリと SDK は、ODP を呼び出して、ODP API を使用して 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 内に表示コンテンツを表示できます。

永続出力

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

始める前に

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

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

ODP を使用するには、次の要件を満たしている必要があります。

  1. AndroidManifest.xml<property> タグ。ODP 構成情報が含まれるパッケージ内の XML リソースを参照します。
  2. 次の例に示すように、AndroidManifest.xml<service> タグ。IsolatedService を拡張するクラスを識別します。<service> タグの service の exported 属性と isolatedProcess 属性は true に設定する必要があります。
  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 インターフェースのメソッドを実装する必要があります。これらのメソッドにはすべてデフォルトの no-op 実装があるため、関心のないメソッドの実装をスキップできます。

OnDevicePersonalizationManager クラスは、アプリと SDK が、デベロッパーが実装した、分離されたプロセスで実行されている IsolatedService を操作するための API を提供します。想定されるユースケースは次のとおりです。

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

表示するコンテンツを生成するには、呼び出し元のアプリは OnDevicePersonalizationManager#execute で、その後の requestSurfacePackage 呼び出しで返された SurfacePackageToken オブジェクトを使用して、結果を 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 クラスは、デベロッパーが HTML 出力に含めることができるイベント トラッキング URL を生成する API を提供します。HTML がレンダリングされると、ODP はイベント URL のペイロードを使用して IsolatedWorker#onEvent を呼び出します。

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

永続的な結果を書き込む

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

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

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

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

ODP サービスは、連携コンピューティング トレーニング ジョブの開始時に IsolatedWorker#onTrainingExample を呼び出し、ODP を採用しているデベロッパーが提供するトレーニング サンプルを取得します。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 を操作する方法について説明します。

Federated 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 リポジトリで確認できます。