Руководство разработчика по персонализации на устройстве

Персонализация на устройстве (On-Device Personalization, ODP) предназначена для защиты информации конечных пользователей от приложений. Приложения используют ODP для персонализации своих продуктов и услуг для конечных пользователей, но они не смогут увидеть точные настройки, сделанные для пользователя (если только не будет прямого взаимодействия вне ODP между приложением и конечным пользователем). Для приложений, использующих модели машинного обучения или статистический анализ, ODP предоставляет набор сервисов и алгоритмов, обеспечивающих надлежащую анонимизацию с использованием соответствующих механизмов дифференциальной конфиденциальности. Для получения более подробной информации см. пояснение к персонализации на устройстве .

ODP запускает код разработчика в IsolatedProcess , который не имеет прямого доступа к сети, локальным дискам или другим службам, работающим на устройстве, но имеет доступ к следующим локально сохраняемым источникам данных:

  • RemoteData — неизменяемые данные типа «ключ-значение», загружаемые с удаленных серверных частей, управляемых разработчиками (если применимо).
  • LocalData — Изменяемые данные в формате ключ-значение, сохраняемые локально разработчиком, если таковые имеются.
  • UserData — Пользовательские данные, предоставляемые платформой.

Поддерживаются следующие форматы вывода:

  • Постоянный вывод: Эти выходные данные могут быть использованы в дальнейшей локальной обработке для получения отображаемых результатов, обучения модели с помощью федеративного обучения или статистического анализа на разных устройствах с помощью федеративной аналитики.
    • Разработчики могут записывать запросы, а также результаты их обработки, в локальную таблицу REQUESTS .
    • Разработчики могут записывать в таблицу EVENTS дополнительные данные, связанные с предыдущим запросом.
  • Отображаемый результат:
    • Разработчики могут возвращать HTML-код, отрисованный ODP, в WebView расположенном внутри SurfaceView . Отрисованное там содержимое не будет видно вызывающему приложению.
    • Разработчики могут встраивать предоставленные ODP URL-адреса событий в HTML-вывод, чтобы запускать регистрацию и обработку взаимодействий пользователя с отображаемым HTML. ODP перехватывает запросы к этим URL-адресам и вызывает код для генерации данных, которые записываются в таблицу EVENTS .

Клиентские приложения и SDK могут вызывать ODP для отображения HTML-контента в SurfaceView с помощью API ODP. Контент, отображаемый в SurfaceView , не виден вызывающему приложению. Клиентское приложение или SDK может быть иным, чем то, которое разрабатывается с использованием ODP.

Сервис ODP управляет клиентским приложением, которое хочет использовать ODP для отображения персонализированного контента в своем пользовательском интерфейсе. Он загружает контент из предоставленных разработчиком конечных точек и запускает логику для постобработки загруженных данных. Он также выступает посредником во всех коммуникациях между IsolatedProcess и другими сервисами и приложениями.

Клиентские приложения используют методы класса OnDevicePersonalizationManager для взаимодействия с кодом разработчика, выполняющимся в IsolatedProcess . Код разработчика, выполняющийся в IsolatedProcess расширяет класс IsolatedService и реализует интерфейс IsolatedWorker . IsolatedService должен создавать экземпляр IsolatedWorker для каждого запроса.

На следующей диаграмме показана взаимосвязь между методами в классах OnDevicePersonalizationManager и IsolatedWorker .

Диаграмма, показывающая взаимосвязь между OnDevicePersonalizationManager и IsolatedWorker .

Клиентское приложение вызывает ODP, используя метод execute с именованным IsolatedService . Служба ODP перенаправляет вызов методу onExecute класса IsolatedWorker . IsolatedWorker возвращает записи для сохранения и контент для отображения. Служба ODP записывает сохраненные данные в таблицу REQUESTS или EVENTS и возвращает клиентскому приложению непрозрачную ссылку на отображаемый контент. Клиентское приложение может использовать эту непрозрачную ссылку в последующем вызове requestSurfacePackage для отображения любого контента в своем пользовательском интерфейсе.

Постоянный выход

Сервис ODP сохраняет запись в таблице REQUESTS после того, как реализация метода onExecute выполненная разработчиком, возвращает результат. Каждая запись в таблице REQUESTS содержит некоторые общие данные для каждого запроса, генерируемые сервисом ODP, и список возвращаемых Rows . Каждая Row содержит список пар (key, value) . Каждое значение представляет собой скаляр, строку или двоичный объект. Числовые значения могут быть представлены после агрегации, а строковые или двоичные данные — после применения локальной или централизованной дифференциальной конфиденциальности. Разработчики также могут записывать последующие события взаимодействия с пользователем в таблицу EVENTS — каждая запись в таблице EVENTS связана со строкой в ​​таблице REQUESTS . Сервис ODP прозрачно регистрирует метку времени и имя пакета вызывающего приложения и APK-файл разработчика ODP с каждой записью.

Прежде чем начать

Прежде чем начать разработку с использованием ODP, необходимо настроить манифест пакета и включить режим разработчика.

Настройки манифеста пакета

Для использования ODP требуется следующее:

  1. Тег <property> в файле AndroidManifest.xml , указывающий на XML-ресурс в пакете, содержащий информацию о конфигурации ODP.
  2. В файле AndroidManifest.xml должен быть тег <service> , который идентифицирует класс, расширяющий IsolatedService , как показано в следующем примере. Сервис в теге <service> должен иметь exported атрибуты и isolatedProcess установленный в true .
  3. В XML-ресурсе, указанном на шаге 1, должен присутствовать тег <service> , который идентифицирует класс службы из шага 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>

Манифест, специфичный для ODP, в XML-ресурсе

В XML-файле ресурсов, указанном в теге <property> , также должен быть объявлен класс службы в теге <service> и указан URL-адрес конечной точки, с которой ODP будет загружать контент для заполнения таблицы RemoteData , как показано в следующем примере. Если вы используете функции федеративных вычислений, вам также необходимо указать 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_kill switch : глобальный переключатель для всех функций ODP; установите значение false, чтобы использовать ODP.
  • _federated_compute_kill_switch: _ переключатель, управляющий всеми функциями обучения (федеративного обучения) ODP; установите значение false, чтобы использовать обучение.
  • _caller_app_allow list : управляет тем, кому разрешено вызывать ODP; приложения (имя пакета, [необязательно] сертификат) можно добавить сюда или установить значение * для разрешения всех.
  • Список _isolated_service_allow : определяет, какие службы могут запускаться в изолированном процессе службы.

Для настройки всех параметров и флагов для использования 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 на стороне устройства

Ознакомьтесь с документацией по Android API для ODP.

Взаимодействие с IsolatedService

Класс IsolatedService — это абстрактный базовый класс, который все разработчики, планирующие работать с ODP, должны расширять и объявлять в манифесте пакета как работающий в изолированном процессе. Служба ODP запускает этот класс в изолированном процессе и отправляет ему запросы. IsolatedService получает запросы от службы ODP и создает IsolatedWorker для обработки запроса.

Разработчикам необходимо реализовать методы интерфейса IsolatedWorker для обработки запросов клиентского приложения, завершения загрузки и событий, запускаемых отрендеренным HTML-кодом. Все эти методы имеют реализации по умолчанию, не выполняющие никаких действий, поэтому разработчики могут пропустить реализацию тех методов, которые им не нужны.

Класс OnDevicePersonalizationManager предоставляет API для взаимодействия приложений и SDK с реализованным разработчиком IsolatedService , работающим в изолированном процессе. Ниже приведены некоторые предполагаемые варианты использования:

Сгенерируйте HTML-контент для отображения в SurfaceView.

Для генерации контента для отображения с помощью OnDevicePersonalizationManager#execute вызывающее приложение может использовать возвращенный объект SurfacePackageToken в последующем вызове requestSurfacePackage , чтобы запросить отображение результата в SurfaceView .

В случае успеха вызывается приемник с объектом SurfacePackage для представления, отображаемого службой ODP. Клиентские приложения должны вставить SurfacePackage в SurfaceView в своей иерархии представлений.

Когда приложение выполняет вызов requestSurfacePackage с SurfacePackageToken возвращенным предыдущим вызовом OnDevicePersonalizationManager#execute , служба ODP вызывает IsolatedWorker#onRender для получения фрагмента HTML-кода, который будет отображен внутри рамочного окна. На этом этапе разработчик не имеет доступа к LocalData или UserData . Это предотвращает встраивание потенциально конфиденциальных UserData в URL-адреса получения ресурсов в сгенерированном HTML. Разработчики могут использовать IsolatedService#getEventUrlProvider для генерации URL-адресов отслеживания, которые будут включены в сгенерированный HTML. После рендеринга HTML служба ODP перехватывает запросы к этим URL-адресам и вызывает IsolatedWorker#onEvent . При реализации onRender() можно вызвать getRemoteData() .

Отслеживание событий внутри HTML-контента

Класс EventUrlProvider предоставляет API для генерации URL-адресов отслеживания событий, которые разработчики могут включать в свой HTML-код. При рендеринге HTML-кода ODP вызовет IsolatedWorker#onEvent с полезной нагрузкой URL-адреса события.

Сервис ODP перехватывает запросы к URL-адресам событий, сгенерированным ODP, внутри отображаемого HTML-кода, вызывает IsolatedWorker#onEvent и записывает возвращенную EventLogRecord в таблицу EVENTS .

Запись устойчивых результатов

С помощью OnDevicePersonalizationManager#execute служба может записывать данные в постоянное хранилище (таблицы REQUESTS и EVENTS ). Вот записи, которые можно записывать в эти таблицы:

  • Объект RequestLogRecord , который будет добавлен в таблицу REQUESTS .
  • Список объектов EventLogRecord , которые будут добавлены в таблицу EVENTS , каждый из которых содержит указатель на ранее созданный объект RequestLogRecord .

Сохраненные результаты, хранящиеся в памяти устройства, могут быть использованы в федеративном обучении для тренировки модели.

Управление задачами обучения на устройстве

Сервис ODP вызывает IsolatedWorker#onTrainingExample когда запускается задача обучения с использованием федеративных вычислений и требуется получить примеры обучения, предоставленные разработчиками, использующими ODP. При реализации onTrainingExample() можно вызвать getRemoteData() , getLocalData() , getUserData() и getLogReader() .

Для планирования или отмены заданий федеративных вычислений можно использовать класс FederatedComputeScheduler , который предоставляет API для всех ODP IsolatedService . Каждое задание федеративных вычислений можно идентифицировать по его имени.

Перед тем как запланировать новое задание федеративных вычислений:

  • Задача с таким именем популяции уже должна быть создана на удаленном федеративном вычислительном сервере.
  • URL-адрес конечной точки федеративного вычислительного сервера должен быть уже указан в настройках манифеста пакета с помощью тега federated-compute-settings .

Взаимодействие с постоянным выводом

В следующем разделе описывается, как взаимодействовать с постоянными выходными данными в ODP.

Прочитайте локальные таблицы

Класс LogReader предоставляет API для чтения таблиц REQUESTS и EVENTS . Эти таблицы содержат данные, записанные IsolatedService во время вызовов onExecute() или onEvent() . Данные в этих таблицах могут использоваться для обучения моделей с помощью федеративного обучения или для статистического анализа на разных устройствах с помощью федеративной аналитики.

Взаимодействие с загруженным контентом

В следующем разделе описывается, как взаимодействовать с загруженным контентом в ODP.

Загрузка контента с серверов

Сервис ODP периодически загружает контент по URL-адресу, указанному в манифесте пакета IsolatedService , и вызывает onDownloadCompleted после завершения загрузки. Загрузка представляет собой JSON-файл, содержащий пары ключ-значение.

Разработчики, использующие ODP, могут выбирать, какое подмножество загруженного контента следует добавить в таблицу RemoteData , а какое удалить. Разработчики не могут изменять загруженный контент — это гарантирует, что таблица RemoteData не будет содержать никаких пользовательских данных. Кроме того, разработчики могут свободно заполнять таблицу LocalData по своему усмотрению; например, они могут кэшировать некоторые предварительно вычисленные результаты.

Формат запроса на загрузку

ODP периодически опрашивает конечную точку URL, указанную в манифесте пакета разработчика, чтобы получить содержимое для заполнения таблицы RemoteData .

Ожидается, что конечная точка вернет JSON-ответ, как описано далее. JSON-ответ должен содержать syncToken , идентифицирующий версию отправляемых данных, а также список пар ключ-значение для заполнения. Значение syncToken должно представлять собой метку времени в секундах, ограниченную часовым интервалом UTC. В рамках запроса на загрузку ODP предоставляет syncToken предыдущей завершенной загрузки и страну устройства в качестве параметров syncToken и country в URL-адресе загрузки. Сервер может использовать предыдущий syncToken для реализации инкрементальной загрузки.

Скачать файл в формате

Загруженный файл представляет собой JSON-файл со следующей структурой. Ожидается, что JSON-файл будет содержать syncToken для идентификации версии загружаемых данных. syncToken должен представлять собой метку времени UTC с ограничением по часам и должен превышать syncToken предыдущей загрузки. Если syncToken не соответствует обоим требованиям, загруженный контент отбрасывается без обработки.

Поле contents представляет собой список кортежей (key, data, encoding). Ожидается, что key будет строкой в ​​кодировке UTF-8. Поле encoding является необязательным параметром, определяющим способ кодирования поля data — оно может быть установлено либо на "utf8", либо на "base64", и по умолчанию предполагается "utf8". Поле key преобразуется в объект String , а поле data — в массив байтов перед вызовом метода 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 на стороне сервера

В этом разделе описывается, как взаимодействовать с API федеративного вычислительного сервера.

API федеративных вычислительных серверов

Для планирования задания федеративных вычислений на стороне клиента необходимо создать задачу с именем для заполнения на удаленном сервере федеративных вычислений. В этом разделе мы рассмотрим, как создать такую ​​задачу на сервере федеративных вычислений.

Схема федеративной вычислительной клиент-серверной топологии.

При создании новой задачи в Task Builder разработчики ODP должны предоставить два набора файлов:

  1. Сохраненная модель tff.learning.models.FunctionalModel получена путем вызова API-функции tff.learning.models.save_functional_model . Пример можно найти в нашем репозитории на GitHub.
  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.

После подготовки этих двух входных данных запустите Task Builder для создания артефактов и генерации новых задач. Более подробные инструкции доступны в нашем репозитории GitHub.