La personnalisation sur l'appareil (ODP, On-Device Personalization) 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 pourront pas voir les personnalisations exactes apportées à l'utilisateur (sauf en cas d'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'ils sont correctement anonymisés à l'aide des mécanismes de confidentialité différentielle appropriés. Pour en savoir plus, consultez l'explication sur 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 aux autres services exécutés sur l'appareil, mais qui a accès aux sources de données persistantes locales suivantes :
RemoteData: données clé-valeur immuables téléchargées à partir de backends distants gérés par les développeurs, le cas échéant.LocalData: données clé/valeur modifiables et persistantes en local par le développeur, le cas échéant.UserData: données utilisateur fournies par la plate-forme.
Les sorties suivantes sont acceptées :
- Sorties persistantes : ces sorties peuvent être utilisées lors d'un traitement local ultérieur, pour générer des sorties affiché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.
- Résultat affiché :
- Les développeurs peuvent renvoyer du code HTML rendu par l'ODP dans un
WebViewà l'intérieur d'unSurfaceView. L'application appelante ne pourra pas voir le contenu affiché. - Les développeurs peuvent intégrer les URL d'événement fournies par l'ODP dans le code HTML généré pour déclencher la journalisation et le traitement des interactions utilisateur avec le code HTML généré. ODP intercepte les requêtes adressées à ces URL et appelle le code pour générer les données qui sont écrites dans la table
EVENTS.
- Les développeurs peuvent renvoyer du code HTML rendu par l'ODP dans un
Les applications clientes et les SDK peuvent appeler 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 ou le SDK client peuvent être une entité différente de celle qui développe 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 des points de terminaison fournis par le développeur et appelle la logique de post-traitement des données téléchargées. Il arbitre également toutes les communications entre IsolatedProcess et d'autres services et applications.
Les applications clientes utilisent des méthodes de la classe OnDevicePersonalizationManager pour interagir avec le code du développeur s'exécutant dans un IsolatedProcess. Le code du développeur s'exécutant dans un IsolatedProcess étend la classe IsolatedService et implémente l'interface IsolatedWorker. Le 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 ODP à l'aide de la méthode execute avec un IsolatedService nommé. Le service ODP transfère l'appel à la méthode onExecute du 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 futur 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 une fois que l'implémentation onExecute du développeur est renvoyée. Chaque enregistrement de la table REQUESTS contient des données courantes par requête générées par le service ODP, ainsi qu'une liste des 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 signalées après agrégation, et les données de chaîne ou de blob peuvent être signalées après application de 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 enregistre de manière transparente un code temporel, ainsi que le nom du package de l'application appelante et l'APK du développeur ODP pour 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 ODP, vous devez disposer des éléments suivants :
- Balise
<property>dansAndroidManifest.xmlqui pointe vers une ressource XML du package contenant les informations de configuration ODP. - Un tag
<service>dansAndroidManifest.xmlqui identifie la classe qui étendIsolatedService, comme illustré dans l'exemple suivant. Le service dans la balise<service>doit avoir les attributsexportedetisolatedProcessdé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 à l'intérieur de la balise elle-même, comme indiqué 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 à 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 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 des commutateurs et des indicateurs
ODP dispose d'un ensemble de commutateurs et d'indicateurs permettant de contrôler certaines fonctionnalités :
- _global_killswitch : paramètre global pour toutes les fonctionnalités ODP. Définissez-le sur "false" pour utiliser ODP.
- _federated_compute_kill_switch : paramètre qui contrôle toutes les fonctionnalités d'entraînement (apprentissage fédéré) d'ODP. Définissez-le sur "false" pour utiliser l'entraînement.
- _caller_app_allowlist : contrôle qui est autorisé à appeler ODP. Les applications (nom du package, certificat [facultatif]) peuvent être ajoutées ici ou définies sur * pour autoriser tout le monde.
- _isolated_service_allowlist : contrôle les services pouvant s'exécuter dans le processus Isolated Service.
Vous pouvez exécuter les commandes suivantes pour configurer tous les commutateurs et indicateurs afin d'utiliser ODP sans restrictions :
# 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 ODP.
Interactions avec IsolatedService
La classe IsolatedService est une classe de base abstraite que tous les développeurs souhaitant développer avec ODP doivent étendre et déclarer dans leur fichier manifeste du 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. Le IsolatedService reçoit les requêtes du service ODP et crée un IsolatedWorker pour les traiter.
Les développeurs doivent implémenter les méthodes de l'interface IsolatedWorker pour gérer les requêtes des applications clientes, les téléchargements terminés et les événements déclenchés par le code HTML rendu. 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 et 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 du contenu à afficher avec OnDevicePersonalizationManager#execute, l'application appelante peut utiliser l'objet SurfacePackageToken renvoyé dans un appel requestSurfacePackage ultérieur pour demander que le résultat soit affiché dans un SurfaceView .
En cas de réussite, le récepteur est appelé avec un SurfacePackage pour une vue rendue par le service ODP. Les applications clientes doivent insérer SurfacePackage dans un SurfaceView au sein de leur hiérarchie de vues.
Lorsqu'une application effectue un appel requestSurfacePackage avec un SurfacePackageToken renvoyé par un appel OnDevicePersonalizationManager#execute précédent, le service ODP appelle IsolatedWorker#onRender pour récupérer l'extrait HTML à afficher dans un cadre clôturé. Pendant cette phase, un développeur n'a pas accès à LocalData ni à UserData. Cela empêche le développeur d'intégrer des UserData potentiellement sensibles dans les URL de récupération des é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 les événements dans le 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énements 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 stockage persistant (tables REQUESTS et EVENTS). Voici les entrées que vous pouvez saisir dans ces tableaux :
RequestLogRecordà ajouter à la tableREQUESTS.- Liste d'objets
EventLogRecordà ajouter à la tableEVENTS, chacun contenant un pointeur vers unRequestLogRecordprécédemment écrit .
Les résultats persistants dans le stockage sur l'appareil peuvent être utilisés par l'apprentissage fédéré pour l'entraînement des 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 au 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 jobs de calcul fédéré, vous pouvez utiliser la classe FederatedComputeScheduler, qui fournit des API pour tous les IsolatedService ODP. Chaque job de calcul fédéré peut être identifié par le nom de sa population.
Avant de planifier un job de calcul fédéré :
- Une tâche portant ce nom de population doit déjà avoir été 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 les sorties persistantes
La section suivante décrit comment interagir avec les résultats persistants dans ODP.
Lire les tables locales
La classe LogReader fournit des API permettant de lire les tables REQUESTS et EVENTS. Ces tables contiennent des données écrites par IsolatedService lors d'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 ODP.
Télécharger du contenu depuis des serveurs
Le service ODP télécharge régulièrement du contenu à partir de l'URL déclarée dans le fichier manifeste du package IsolatedService et appelle onDownloadCompleted une fois le téléchargement terminé. Le fichier téléchargé est un fichier JSON contenant des paires clé/valeur.
Les développeurs qui adoptent ODP peuvent choisir le sous-ensemble de contenu téléchargé à ajouter à la table RemoteData et celui à supprimer. 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 demande de téléchargement
L'ODP interroge régulièrement le point de terminaison de l'URL déclaré dans le fichier manifeste du package du développeur pour récupérer le contenu permettant de remplir la table 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 à remplir. La valeur syncToken doit être un code temporel en secondes, limité à une heure UTC. Dans la demande de téléchargement, ODP fournit le syncToken du téléchargement précédemment terminé et le pays de l'appareil en tant que paramètres syncToken et country dans l'URL de téléchargement. Le serveur peut utiliser le syncToken précédent pour implémenter les téléchargements incrémentiels.
Format de fichier à télécharger
Le fichier téléchargé est un fichier JSON avec la structure suivante. Le fichier JSON doit contenir un syncToken pour identifier la version des données en cours de téléchargement. Le syncToken doit être un code temporel UTC limité à une heure et doit être supérieur au 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 tuples (clé, données, encodage). key doit être une chaîne UTF-8. Le champ encoding est un paramètre facultatif qui spécifie la façon dont le champ data est encodé. Il peut être défini sur "utf8" ou "base64", et est considéré comme "utf8" par défaut. Le champ key est converti en objet String et le champ data est converti en tableau d'octets avant l'appel de 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 décrit comment interagir avec les API du serveur de calcul fédéré.
API Federated Compute Server
Pour planifier un job de calcul fédéré côté client, vous avez besoin d'une tâche avec un nom de population créé 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é.
Lors de la création d'une tâche pour le générateur de tâches, les développeurs ODP doivent fournir deux ensembles de fichiers :
- Modèle tff.learning.models.FunctionalModel enregistré en appelant l'API tff.learning.models.save_functional_model. Vous trouverez un exemple dans notre dépôt GitHub.
- Un fichier fcp_server_config.json qui inclut les règles, la configuration de l'apprentissage fédéré et la 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 Task Builder pour construire des artefacts et générer de nouvelles tâches. Des instructions plus détaillées sont disponibles dans notre dépôt GitHub.