La personnalisation sur l'appareil (ODP) est conçue pour protéger les informations des utilisateurs finaux contre les applications. Les applications utilisent l'ODP pour personnaliser leurs produits et services pour les utilisateurs finaux, mais elles ne peuvent pas voir les personnalisations exactes effectuées pour l'utilisateur (sauf s'il existe des interactions directes en dehors de l'ODP entre l'application et l'utilisateur final). Pour les applications avec des modèles de machine learning ou des analyses statistiques, l'ODP fournit un ensemble de services et d'algorithmes pour s'assurer qu'elles sont correctement anonymisées à l'aide des mécanismes de confidentialité différentielle appropriés. Pour en savoir plus, consultez la vidéo expliquant la personnalisation sur l'appareil.
L'ODP exécute le code du développeur dans un IsolatedProcess
qui n'a pas d'accès direct au réseau, aux disques locaux ni à d'autres services exécutés sur l'appareil, mais qui a accès aux sources de données conservées localement suivantes:
RemoteData
: données clé-valeur immuables téléchargées à partir de backends distants gérés par le développeur, le cas échéant.LocalData
: données clé-valeur modifiables conservées localement par le développeur, le cas échéant.UserData
: données utilisateur fournies par la plate-forme.
Les sorties suivantes sont acceptées:
- Résultat persistant: ces résultats peuvent être utilisés pour un traitement local ultérieur, pour produire des résultats affichés, pour l'entraînement de modèle facilité par l'apprentissage fédéré ou pour l'analyse statistique multi-appareil assistée par Federated Analytics.
- Résultat affiché :
- Les développeurs peuvent renvoyer du code HTML affiché par l'ODP dans un
WebView
dans unSurfaceView
. Le contenu affiché ne sera pas visible par l'application appelante. - Les développeurs peuvent intégrer des URL d'événement fournies par l'ODP dans la sortie HTML pour déclencher la journalisation et le traitement des interactions des utilisateurs avec le code HTML affiché. ODP intercepte les requêtes adressées à ces URL et appelle du code pour générer des données qui sont écrites dans la table
EVENTS
.
- Les développeurs peuvent renvoyer du code HTML affiché par l'ODP dans un
Les applications clientes et les SDK peuvent appeler l'ODP pour afficher du contenu HTML dans un SurfaceView
à l'aide des API ODP. Le contenu affiché dans un SurfaceView
n'est pas visible par l'application appelante. L'application cliente ou le SDK peuvent être une entité différente de celle développée avec ODP.
Le service ODP gère l'application cliente qui souhaite appeler ODP pour afficher du contenu personnalisé dans son UI. Il télécharge le contenu à partir de points de terminaison fournis par le développeur et appelle une logique pour le post-traitement des données téléchargées. Il assure également la médiation de toutes les communications entre le IsolatedProcess
et d'autres services et applications.
Les applications clientes utilisent les méthodes de la classe OnDevicePersonalizationManager
pour interagir avec le code du développeur exécuté dans un IsolatedProcess
. Le code du développeur exécuté dans un IsolatedProcess
étend la classe IsolatedService
et implémente l'interface IsolatedWorker
. IsolatedService
doit créer une instance de IsolatedWorker
pour chaque requête.
Le schéma suivant illustre la relation entre les méthodes dans OnDevicePersonalizationManager
et IsolatedWorker
.
OnDevicePersonalizationManager
et IsolatedWorker
.Une application cliente appelle l'ODP à l'aide de la méthode execute
avec un IsolatedService
nommé. Le service ODP transfère l'appel à la méthode onExecute
de IsolatedWorker
. IsolatedWorker
renvoie les enregistrements à conserver et le contenu à afficher. Le service ODP écrit la sortie persistante dans la table REQUESTS
ou EVENTS
et renvoie une référence opaque à la sortie affichée à l'application cliente. L'application cliente peut utiliser cette référence opaque dans un prochain appel requestSurfacePackage
pour afficher n'importe quel contenu d'affichage dans son UI.
Sortie persistante
Le service ODP conserve un enregistrement dans la table REQUESTS
après le retour de l'implémentation de onExecute
par le développeur. Chaque enregistrement de la table REQUESTS
contient des données communes par requête générées par le service ODP, ainsi qu'une liste de Rows
renvoyés. Chaque Row
contient une liste de paires (key, value)
. Chaque valeur est un scalaire, une chaîne ou un blob. Les valeurs numériques peuvent être rapportées après l'agrégation, et les données de chaîne ou de blob peuvent être rapportées après avoir appliqué la confidentialité différentielle locale ou centrale. Les développeurs peuvent également écrire des événements d'interaction utilisateur ultérieurs dans la table EVENTS
. Chaque enregistrement de la table EVENTS
est associé à une ligne de la table REQUESTS
. Le service ODP consigne de manière transparente un code temporel et le nom du package de l'application appelante et de l'APK du développeur ODP avec chaque enregistrement.
Avant de commencer
Avant de commencer à développer avec ODP, vous devez configurer le fichier manifeste de votre package et activer le mode développeur.
Paramètres du fichier manifeste du package
Pour utiliser l'ODP, vous devez disposer des éléments suivants :
- Une balise
<property>
dansAndroidManifest.xml
qui pointe vers une ressource XML du package contenant des informations de configuration ODP. - Une balise
<service>
dansAndroidManifest.xml
qui identifie la classe qui étendIsolatedService
, comme illustré dans l'exemple suivant. Les attributsexported
etisolatedProcess
du service dans la balise<service>
doivent être définis surtrue
. - Une balise
<service>
dans la ressource XML spécifiée à l'étape 1 qui identifie la classe de service de l'étape 2. La balise<service>
doit également inclure des paramètres supplémentaires spécifiques à l'ODP dans la balise elle-même, comme illustré dans le deuxième exemple.
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>
Manifeste spécifique à l'ODP dans une ressource XML
Le fichier de ressources XML spécifié dans la balise <property>
doit également déclarer la classe de service dans une balise <service>
et spécifier le point de terminaison de l'URL à partir duquel l'ODP téléchargera le contenu pour remplir la table RemoteData
, comme illustré dans l'exemple suivant. Si vous utilisez les fonctionnalités de calcul fédéré, vous devez également spécifier le point de terminaison de l'URL du serveur de calcul fédéré auquel le client de calcul fédéré se connectera.
<!-- 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>
Activer le mode développeur
Activez le mode développeur en suivant les instructions de la section Activer les options pour les développeurs de la documentation Android Studio.
Paramètres "Activer" et "Signaler"
L'ODP dispose d'un ensemble de commutateurs et d'indicateurs permettant de contrôler certaines fonctionnalités:
- _global_killswitch : bouton d'activation/de désactivation global pour toutes les fonctionnalités ODP. Définissez-le sur "false" pour utiliser ODP.
- _federated_compute_kill_switch: _boutons de commande qui contrôlent toutes les fonctionnalités d'entraînement (apprentissage fédéré) de l'ODP. Définissez-le sur "false" pour utiliser l'entraînement.
- _caller_app_allowlist: contrôle qui est autorisé à appeler l'ODP, les applications (nom pkg, certificat [facultatif]) peuvent être ajoutées ici ou définies sur * pour autoriser tous les utilisateurs
- _isolated_service_allowlist: contrôle les services pouvant s'exécuter dans le processus du service isolé.
Vous pouvez exécuter les commandes suivantes pour configurer tous les commutateurs et indicateurs afin d'utiliser l'ODP sans restriction :
# 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 côté appareil
Consultez la documentation de référence de l'API Android pour l'ODP.
Interactions avec IsolatedService
La classe IsolatedService
est une classe de base abstraite que tous les développeurs qui souhaitent développer des applications avec ODP doivent étendre et déclarer dans leur fichier manifeste de package comme s'exécutant dans un processus isolé. Le service ODP démarre ce service dans un processus isolé et lui envoie des requêtes. IsolatedService
reçoit les requêtes du service ODP et crée un IsolatedWorker
pour les gérer.
Les développeurs doivent implémenter les méthodes de l'interface IsolatedWorker
pour gérer les requêtes d'application cliente, les lectures complètes de téléchargements et les événements déclenchés par le code HTML affiché. Toutes ces méthodes ont des implémentations no-op par défaut. Les développeurs peuvent donc ignorer l'implémentation des méthodes qui ne les intéressent pas.
La classe OnDevicePersonalizationManager
fournit une API permettant aux applications et aux SDK d'interagir avec un IsolatedService
implémenté par le développeur s'exécutant dans un processus isolé. Voici quelques cas d'utilisation prévus :
Générer du contenu HTML à afficher dans une SurfaceView
Pour générer le contenu à afficher, avec OnDevicePersonalizationManager#execute
, l'application appelante peut utiliser l'objet SurfacePackageToken
renvoyé dans un appel requestSurfacePackage
ultérieur pour demander l'affichage du résultat dans un SurfaceView
.
En cas de réussite, le récepteur est appelé avec un SurfacePackage
pour une vue affichée par le service ODP. Les applications clientes doivent insérer le SurfacePackage
dans un SurfaceView
de leur hiérarchie des vues.
Lorsqu'une application effectue un appel requestSurfacePackage
avec un SurfacePackageToken
renvoyé par un OnDevicePersonalizationManager#execute
précédent, l'appel du service ODP appelle IsolatedWorker#onRender
pour récupérer l'extrait de code HTML à afficher dans un cadre cloisonné. Un développeur n'a pas accès à LocalData
ni à UserData
pendant cette phase. Cela empêche le développeur d'intégrer des UserData
potentiellement sensibles dans les URL de récupération d'éléments dans le code HTML généré. Les développeurs peuvent utiliser IsolatedService#getEventUrlProvider
pour générer des URL de suivi à inclure dans le code HTML généré. Lorsque le code HTML est affiché, le service ODP intercepte les requêtes adressées à ces URL et appelle IsolatedWorker#onEvent
. Vous pouvez appeler getRemoteData()
lors de l'implémentation de onRender()
.
Suivre des événements au sein d'un contenu HTML
La classe EventUrlProvider
fournit des API permettant de générer des URL de suivi des événements que les développeurs peuvent inclure dans leur sortie HTML. Lorsque le code HTML est affiché, ODP appelle IsolatedWorker#onEvent
avec la charge utile de l'URL de l'événement.
Le service ODP intercepte les requêtes adressées aux URL d'événement générées par ODP dans le code HTML affiché, appelle IsolatedWorker#onEvent
et consigne le EventLogRecord
renvoyé dans la table EVENTS
.
Écrire des résultats persistants
Avec OnDevicePersonalizationManager#execute
, le service a la possibilité d'écrire des données dans un espace de stockage persistant (tables REQUESTS
et EVENTS
). Voici les entrées qu'il est possible d'écrire dans ces tables:
- un
RequestLogRecord
à ajouter à la tableREQUESTS
. - une liste d'objets
EventLogRecord
à ajouter au tableauEVENTS
, chacun contenant un pointeur vers unRequestLogRecord
écrit précédemment.
Les résultats persistants stockés sur l'appareil peuvent être utilisés par l'apprentissage fédéré pour l'entraînement de modèles.
Gérer les tâches d'entraînement sur l'appareil
Le service ODP appelle IsolatedWorker#onTrainingExample
lorsqu'une tâche d'entraînement de calcul fédéré démarre et souhaite obtenir des exemples d'entraînement fournis par les développeurs qui adoptent ODP. Vous pouvez appeler getRemoteData()
, getLocalData()
, getUserData()
et getLogReader()
lors de l'implémentation de onTrainingExample()
.
Pour planifier ou annuler des tâches de calcul fédérées, vous pouvez utiliser la classe FederatedComputeScheduler
, qui fournit des API pour tous les IsolatedService
ODP. Chaque tâche de calcul fédérée peut être identifiée par son nom de remplissage.
Avant de planifier une nouvelle tâche de calcul fédérée:
- Une tâche portant ce nom devrait déjà être créée sur le serveur de calcul fédéré distant.
- Le point de terminaison de l'URL du serveur de calcul fédéré doit déjà être spécifié dans les paramètres du fichier manifeste du package avec la balise
federated-compute-settings
.
Interactions avec la sortie persistante
La section suivante explique comment interagir avec la sortie persistante dans ODP.
Lire des tables locales
La classe LogReader
fournit des API permettant de lire les tables REQUESTS
et EVENTS
. Ces tables contiennent les données écrites par IsolatedService
lors des appels onExecute()
ou onEvent()
. Les données de ces tables peuvent être utilisées pour l'entraînement de modèle basé sur l'apprentissage fédéré ou pour l'analyse statistique inter-appareil assistée par Federated Analytics.
Interactions avec le contenu téléchargé
La section suivante explique comment interagir avec le contenu téléchargé dans l'ODP.
Télécharger le contenu depuis les serveurs
Le service ODP télécharge régulièrement le contenu à partir de l'URL déclarée dans le fichier manifeste du package de IsolatedService
, puis appelle onDownloadCompleted
une fois le téléchargement terminé. Le téléchargement est un fichier JSON contenant des paires clé-valeur.
Les développeurs qui adoptent l'ODP peuvent choisir quel sous-ensemble du contenu téléchargé doit être ajouté à la table RemoteData
et lequel doit être supprimé. Les développeurs ne peuvent pas modifier le contenu téléchargé. Cela garantit que la table RemoteData
ne contient aucune donnée utilisateur. De plus, les développeurs sont libres de remplir la table LocalData
comme ils le souhaitent. Par exemple, ils peuvent mettre en cache certains résultats précalculés.
Format de la requête de téléchargement
L'ODP interroge périodiquement le point de terminaison d'URL déclaré dans le fichier manifeste du package du développeur pour récupérer du contenu à insérer dans le tableau RemoteData
.
Le point de terminaison doit renvoyer une réponse JSON, comme décrit plus loin. La réponse JSON doit contenir un syncToken
qui identifie la version des données envoyées, ainsi qu'une liste de paires clé/valeur à renseigner. La valeur syncToken
doit être un code temporel en secondes, limité à une limite horaire UTC. Dans la demande de téléchargement, l'ODP fournit le syncToken
du téléchargement précédemment effectué et le pays de l'appareil en tant que syncToken et paramètres de pays dans l'URL de téléchargement. Le serveur peut utiliser le syncToken
précédent pour implémenter des téléchargements incrémentiels.
Format de fichier à télécharger
Le fichier téléchargé est un fichier JSON dont la structure est la suivante. Le fichier JSON doit contenir un syncToken pour identifier la version des données téléchargées. Le syncToken doit être un code temporel UTC limité à une limite d'une heure et doit dépasser le syncToken du téléchargement précédent. Si le syncToken ne répond pas aux deux exigences, le contenu téléchargé est supprimé sans être traité.
Le champ "contents" est une liste de tupels (clé, données, codage). key
doit être une chaîne UTF-8. Le champ encoding
est un paramètre facultatif qui spécifie comment le champ data
est encodé. Il peut être défini sur "utf8" ou "base64", et est supposé être "utf8" par défaut. Le champ key
est converti en objet String
et le champ data
en tableau d'octets avant d'appeler onDownloadCompleted().
.
{
// 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 côté serveur
Cette section explique comment interagir avec les API du serveur de calcul fédéré.
API Federated Compute Server
Pour planifier une tâche de calcul fédérée côté client, vous devez créer une tâche avec un nom de population sur le serveur de calcul fédéré distant. Dans cette section, nous allons voir comment créer une telle tâche sur le serveur de calcul fédéré.
Lorsque vous créez une tâche pour l'outil de création de tâches, les développeurs ODP doivent fournir deux ensembles de fichiers :
- Un modèle tff.learning.models.FunctionalModel enregistré via l'appel d'API tff.learning.models.save_functional_model Vous trouverez un exemple dans notre dépôt GitHub.
- Fichier fcp_server_config.json qui inclut des règles, une configuration de l'apprentissage fédéré et une configuration de la confidentialité différentielle. Voici un exemple de fichier 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
}
}
Vous trouverez d'autres exemples dans notre dépôt GitHub.
Après avoir préparé ces deux entrées, appelez le générateur de tâches pour créer des artefacts et générer de nouvelles tâches. Des instructions plus détaillées sont disponibles dans notre dépôt GitHub.