Przewodnik dla programistów po personalizacji na urządzeniu

Personalizacja na urządzeniu (ODP) ma na celu ochronę informacji użytkowników przed aplikacjami. Aplikacje korzystają z Odpowiedzi na potrzeby użytkowników, aby dostosować swoje produkty i usługi do potrzeb użytkowników, ale nie będą mogły zobaczyć dokładnych ustawień dostosowanych do użytkownika (chyba że poza Odpowiedzią na potrzeby użytkowników nastąpi bezpośrednia interakcja między aplikacją a użytkownikiem). W przypadku aplikacji z modelami uczenia maszynowego lub analiz statystycznych ODP udostępnia zestaw usług i algorytmów, które zapewniają odpowiednie zanonimizowanie za pomocą odpowiednich mechanizmów ochrony prywatności różnicowej. Więcej informacji znajdziesz w artykule Personalizacja na urządzeniu.

ODP uruchamia kod dewelopera w procesie IsolatedProcess, który nie ma bezpośredniego dostępu do sieci, dysków lokalnych ani innych usług działających na urządzeniu, ale ma dostęp do tych lokalnie zapisanych źródeł danych:

  • RemoteData – niezmienne dane klucz-wartość pobrane z odległych serwerów backendowych obsługiwanych przez dewelopera (w stosownych przypadkach).
  • LocalData – zmienne dane klucz-wartość zapisane lokalnie przez dewelopera (jeśli dotyczy).
  • UserData – dane użytkownika podane przez platformę.

Obsługiwane są te dane wyjściowe:

  • Trwały wynik: te dane mogą być używane w przyszłości do przetwarzania lokalnego, wyświetlania danych wyjściowych, trenowania modeli za pomocą sfederowanego uczenia się lub przeprowadzania analizy statystycznej na różnych urządzeniach za pomocą sfederowanych funkcji Analytics.
    • Deweloperzy mogą zapisywać żądania i ich wyniki przetwarzania w lokalnej tabeli REQUESTS.
    • Deweloperzy mogą zapisywać dodatkowe dane powiązane z poprzednim żądaniem w tabeli EVENTS.
  • Wyświetlany wynik:
    • Deweloperzy mogą zwracać kod HTML renderowany przez ODP w elementach WebView wewnątrz elementu SurfaceView. Treści renderowane w tym miejscu nie będą widoczne dla wywołującej aplikacji.
    • Deweloperzy mogą umieszczać w wyjściowym kodzie HTML adresy URL zdarzeń udostępniane przez ODP, aby wywołać rejestrowanie i przetwarzanie interakcji użytkowników z wyrenderowanym kodem HTML. ODP przechwytuje żądania dotyczące tych adresów URL i wywołuje kod, aby wygenerować dane, które są zapisywane w tabeli EVENTS.

Aplikacje klienta i pakiety SDK mogą wywoływać ODP, aby wyświetlać treści HTML w ramach SurfaceView, korzystając z interfejsów API ODP. Treści renderowane w SurfaceView nie są widoczne dla aplikacji wywołującej. Aplikacja kliencka lub pakiet SDK może być innym podmiotem niż ten, który rozwija ODP.

Usługa ODP zarządza aplikacją klienta, która chce wywołać ODP, aby wyświetlić spersonalizowane treści w interfejsie. Pobiera treści z punktów końcowych udostępnionych przez dewelopera i wywołuje logikę do dalszego przetwarzania pobranych danych. Pośredniczy też w komunikacji między IsolatedProcess a innymi usługami i aplikacjami.

Aplikacje klienckie korzystają z metod klasy OnDevicePersonalizationManager, aby wchodzić w interakcje z kodem dewelopera działającym w kontekście IsolatedProcess. Kod programisty działający w komponencie IsolatedProcess rozszerza klasę IsolatedService i implementuje interfejs IsolatedWorker. IsolatedService musi utworzyć instancję IsolatedWorker dla każdego żądania.

Ten diagram pokazuje związek między metodami w funkcjach OnDevicePersonalizationManagerIsolatedWorker.

Diagram przedstawiający relację między OnDevicePersonalizationManagerIsolatedWorker.

Aplikacja klienta wywołuje ODP za pomocą metody execute z nazwą IsolatedService. Usługa ODP przekierowuje wywołanie do metody onExecute usługi IsolatedWorker. Funkcja IsolatedWorker zwraca rekordy, które mają być utrwalone, oraz treści, które mają być wyświetlane. Usługa ODP zapisuje trwały wynik w tabeli REQUESTS lub EVENTS i zwraca nieprzezroczyste odwołanie do wyświetlanego wyniku w aplikacji klienta. Aplikacja klienta może użyć tego nieprzezroczystego odwołania w przyszłym wywołaniu funkcji requestSurfacePackage, aby wyświetlić dowolną treść w interfejsie.

Dane wyjściowe trwałe

Usługa ODP zapisuje rekord w tabeli REQUESTS po zwróceniu przez implementację dewelopera funkcji onExecute. Każdy rekord w tabeli REQUESTS zawiera pewne wspólne dane dotyczące żądania wygenerowane przez usługę ODP oraz listę zwróconych Rows. Każdy element Row zawiera listę par (key, value). Każda wartość jest skalarem, ciągiem znaków lub blobem. Wartości liczbowe można raportować po agregacji, a dane w postaci ciągu znaków lub bloba – po zastosowaniu lokalnej lub centralnej prywatności różnicowej. Deweloperzy mogą też zapisywać kolejne zdarzenia interakcji z użytkownikiem w tabeli EVENTS – każdy rekord w tabeli EVENTS jest powiązany z wierszem w tabeli REQUESTS. Usługa ODP rejestruje w każdym rekordzie sygnaturę czasową, nazwę pakietu aplikacji wywołującej i plik APK dewelopera ODP.

Zanim zaczniesz

Zanim zaczniesz tworzyć aplikację z użyciem ODP, musisz skonfigurować plik manifestu pakietu i włączyć tryb programisty.

Ustawienia pliku manifestu pakietu

Aby korzystać z ODP, musisz:

  1. Tag <property> w AndroidManifest.xml, który wskazuje na zasób XML w pakiecie, zawierający informacje o konfiguracji ODP.
  2. Tag <service>AndroidManifest.xml, który identyfikuje klasę rozszerzającą klasę IsolatedService, jak w tym przykładzie. Usługa w tagu <service> musi mieć atrybuty exported i isolatedProcess ustawione na true.
  3. Tag <service> w zasobie XML określonym w kroku 1, który identyfikuje klasę usługi z kroku 2. Tag <service> musi też zawierać dodatkowe ustawienia związane z ODP, jak pokazano w drugim przykładzie.

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>

Plik manifestu specyficzny dla ODP w zasobach XML

Plik zasobu XML podany w tagu <property> musi też deklarować klasę usługi w tagu <service> i określać punkt końcowy adresu URL, z którego ODP będzie pobierać treści, aby wypełnić tabelę RemoteData, jak pokazano w tym przykładzie. Jeśli korzystasz z funkcji federacyjnego przetwarzania, musisz też określić punkt końcowy adresu URL serwera federacyjnego przetwarzania, z którym połączy się klient federacyjnego przetwarzania.

<!-- 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>

Włączanie trybu programisty

Włącz tryb programisty, wykonując instrukcje podane w sekcji Włączanie opcji dla deweloperów w dokumentacji Android Studio.

Ustawienia przełączników i flag

ODP zawiera zestaw przełączników i flag, które służą do sterowania niektórymi funkcjami:

  • _global_killswitch: globalny przełącznik dla wszystkich funkcji ODP; ustaw na „false”, aby korzystać z ODP
  • _federated_compute_kill_switch: _przełącznik kontrolujący wszystkie funkcje ODP dotyczące uczenia (uczenie federacyjne); ustaw na wartość false, aby korzystać z uczenia
  • _caller_app_allowlist: określa, kto może wywoływać ODP. Tutaj można dodać aplikacje (nazwa pakietu, [opcjonalnie] certyfikat) lub ustawić wartość *, aby zezwolić wszystkim.
  • _isolated_service_allowlist: określa, które usługi mogą działać w procesie usługi izolowanej.

Aby skonfigurować wszystkie przełączniki i flagi, aby korzystać z ODP bez ograniczeń, możesz uruchomić te polecenia:

# 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 \"*\"

Interfejsy API po stronie urządzenia

Zapoznaj się z dokumentacją interfejsu API Androida dotyczącą ODP.

Interakcje z IsolatedService

Klasa IsolatedService to abstrakcyjna klasa podstawowa, którą muszą rozszerzać wszyscy deweloperzy, którzy chcą tworzyć aplikacje korzystające z ODP, oraz deklarować w pliku manifestu pakietu jako proces izolowany. Usługa ODP uruchamia tę usługę w ramach izolowanego procesu i wysyła do niej żądania. Usługa IsolatedService odbiera żądania od usługi ODP i tworzy obiekt IsolatedWorker, aby je obsłużyć.

Aby obsługiwać żądania aplikacji klienckiej, zakończenia pobierania i zdarzenia wywołane przez wyrenderowany kod HTML, deweloperzy muszą zaimplementować metody z interfejsu IsolatedWorker. Wszystkie te metody mają domyślne implementacje no-op, więc deweloperzy mogą pominąć implementację metod, które ich nie interesują.

Klasa OnDevicePersonalizationManager udostępnia interfejs API, który umożliwia aplikacjom i pakietom SDK komunikowanie się z usługą IsolatedService uruchomioną w odseparowanym procesie. Oto kilka przykładowych zastosowań:

Generowanie zawartości HTML do wyświetlania w widoku powierzchni

Aby wygenerować treść do wyświetlenia z OnDevicePersonalizationManager#execute, aplikacja wywołująca może użyć zwróconego obiektu SurfacePackageToken w kolejnych wywołaniach requestSurfacePackage, aby poprosić o wyświetlenie wyniku w komponencie SurfaceView .

W przypadku powodzenia wywoływana jest metoda SurfacePackage obiektu View zwracanego przez usługę ODP. Aplikacje klienckie muszą wstawić SurfacePackage do SurfaceView w hierarchii widoku.

Gdy aplikacja wywołuje funkcję requestSurfacePackage z argumentem SurfacePackageToken zwróconym przez wcześniejsze wywołanie funkcji OnDevicePersonalizationManager#execute, usługa ODP wywołuje funkcję IsolatedWorker#onRender, aby pobrać fragment kodu HTML, który ma zostać wyrenderowany w ramce odizolowanej. W tej fazie deweloper nie ma dostępu do LocalData ani UserData. Zapobiega to umieszczaniu przez dewelopera potencjalnie poufnych informacji UserData w adresach URL pobierania zasobów w wygenerowanym kodzie HTML. Deweloperzy mogą używać IsolatedService#getEventUrlProvider do generowania adresów URL śledzenia, które można uwzględnić w wygenerowanym kodzie HTML. Gdy renderowanie HTML zostanie zakończone, usługa ODP przechwyci żądania dotyczące tych adresów URL i wywoła funkcję IsolatedWorker#onEvent. Można wywołać getRemoteData() podczas implementowania onRender().

Śledzenie zdarzeń w treściach HTML

Klasa EventUrlProvider udostępnia interfejsy API do generowania linków monitorujących zdarzenia, które deweloperzy mogą uwzględniać w wyjściowym kodzie HTML. Gdy renderowany jest kod HTML, ODP wywołuje funkcję IsolatedWorker#onEvent z ładunkiem adresu URL zdarzenia.

Usługa ODP przechwytuje żądania do adresów URL zdarzeń wygenerowanych przez ODP w renderowanym kodzie HTML, wywołuje funkcję IsolatedWorker#onEvent i zapisują zwrócone EventLogRecord do tabeli EVENTS.

Zapisz trwałe wyniki

Dzięki OnDevicePersonalizationManager#execute usługa ma możliwość zapisywania danych w trwałym miejscu przechowywania (tabelach REQUESTSEVENTS). Oto wpisy, które można zapisać w tych tabelach:

  • RequestLogRecord dodawane do tabeli REQUESTS.
  • lista obiektów EventLogRecord do dodania do tabeli EVENTS, z których każdy zawiera wskaźnik do wcześniej zapisanego obiektu RequestLogRecord .

Trwałe wyniki w pamięci urządzenia mogą być wykorzystywane przez sfederowane uczenie się do trenowania modeli.

Zarządzanie zadaniami treningowymi na urządzeniu

Usługa ODP wywołuje IsolatedWorker#onTrainingExample, gdy rozpoczyna się zadanie trenowania w ramach sfederowanego przetwarzania, i chce uzyskać przykłady treningowe udostępnione przez programistów korzystających z ODP. Podczas implementowania onTrainingExample() możesz wywoływać getRemoteData(), getLocalData(), getUserData()getLogReader().

Aby zaplanować lub anulować zadania obliczeniowe w federacji, możesz użyć klasy FederatedComputeScheduler, która udostępnia interfejsy API dla wszystkich ODP IsolatedService. Każde zadanie obliczeniowe w ramach federacji można zidentyfikować na podstawie nazwy populacji.

Zanim zaplanowasz nowe zadanie obliczeniowe w federacji:

  • Na zdalnym zfederowanym serwerze obliczeniowym powinno już być utworzone zadanie o tej nazwie populacji.
  • Adres URL punktu końcowego serwera obliczeniowego federacji powinien być już określony w ustawieniach pliku manifestu pakietu za pomocą tagu federated-compute-settings.

Interakcje z trwałym wyjściem

W tej sekcji opisano, jak korzystać z trwałego wyjścia w ODP.

Odczytywanie tabel lokalnych

Klasa LogReader udostępnia interfejsy API do odczytu tabel REQUESTS i EVENTS. Te tabele zawierają dane zapisane przez IsolatedService podczas wywołania funkcji onExecute() lub onEvent(). Dane w tych tabelach można wykorzystać do trenowania modeli za pomocą sfederowanego uczenia się lub do przeprowadzania analizy statystycznej na różnych urządzeniach za pomocą sfederowanych funkcji Analytics.

Interakcje z pobranymi treściami

Z tej sekcji dowiesz się, jak korzystać z pobranych treści w ODP.

Pobieranie treści z serwerów

Usługa ODP okresowo pobiera treści z adresu URL zadeklarowanego w pliku manifestu pakietu IsolatedService i po zakończeniu pobierania wywołuje funkcję onDownloadCompleted. Plik do pobrania to plik JSON zawierający pary klucz-wartość.

Deweloperzy, którzy korzystają z ODP, mogą wybrać, które z pobrane treści mają zostać dodane do tabeli RemoteData, a które mają zostać usunięte. Deweloperzy nie mogą modyfikować pobranych treści – dzięki temu tabela RemoteData nie zawiera żadnych danych użytkownika. Ponadto deweloperzy mogą dowolnie wypełniać tabelę LocalData, np. mogą przechowywać w niej w pamięci podręcznej niektóre z wyliczonych wcześniej wyników.

Format żądania pobierania

ODP okresowo sprawdza punkt końcowy adresu URL zadeklarowany w pliku manifestu pakietu dewelopera, aby pobrać treści i uzupełnić tabelę RemoteData.

Punkt końcowy powinien zwracać odpowiedź w formacie JSON, jak opisano poniżej. Odpowiedź JSON musi zawierać syncToken, który identyfikuje wersję wysyłanych danych, oraz listę par klucz-wartość do wypełnienia. Wartość syncToken musi być sygnaturą czasową w sekundach, przyciśniętą do granicy godziny UTC. W ramach żądania pobierania ODP udostępnia syncToken wcześniejszego pobierania oraz kraj urządzenia jako parametry syncToken i country w adresie URL pobierania. Serwer może użyć poprzedniego syncToken, aby wdrożyć stopniowe pobieranie.

Format pliku do pobrania

Pobrany plik to plik JSON o tej strukturze. Plik JSON powinien zawierać identyfikator synchronizacji (syncToken) służący do identyfikowania wersji pobieranych danych. Wartość syncToken musi być sygnaturą czasową UTC ograniczoną do godziny i musi być większa od wartości syncToken z poprzedniego pobierania. Jeśli token synchronizacji nie spełnia obu tych wymagań, pobrane treści są odrzucane bez przetwarzania.

Pole zawartość to lista tupla (klucz, dane, kodowanie). Wartość key powinna być ciągiem znaków w formacie UTF-8. Pole encoding to opcjonalny parametr, który określa sposób kodowania pola data. Może ono mieć wartość „utf8” lub „base64”. Domyślnie przyjmuje się kodowanie „utf8”. Pole key jest konwertowane na obiekt String, a pole data na tablicę bajtów przed wywołaniem funkcji 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"
    },
    // ...
  ]
}

Interfejsy API po stronie serwera

W tej sekcji opisano, jak korzystać z interfejsów API serwera obliczeniowego federacyjnego.

Interfejsy Federated Compute Server API

Aby zaplanować zadanie federacyjne na serwerze obliczeniowym po stronie klienta, musisz utworzyć zadanie z nazwą populacji na zdalnym serwerze obliczeniowym federacyjnym. W tej sekcji opisujemy, jak utworzyć takie zadanie na serwerze obliczeniowym federacji.

Diagram topologia klienta-serwera w przypadku obliczeń federacyjnych.

Podczas tworzenia nowego zadania w narzędziu do tworzenia zadań deweloperzy ODP powinni przesłać 2 zbiory plików:

  1. Zapisany model tff.learning.models.FunctionalModel utworzony przez wywołanie wywołania interfejsu API tff.learning.models.save_functional_model. Przykładowy kod znajdziesz w repozytorium GitHub.
  2. Plik fcp_server_config.json, który zawiera zasady, konfigurację sfederowanego uczenia się i konfigurację prywatności różnicowej. Poniżej znajduje się przykład pliku 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
  }
}

Więcej przykładów znajdziesz w naszym repozytorium GitHub.

Po przygotowaniu tych 2 danych wejściowych uruchom kreatora zadań, aby utworzyć artefakty i wygenerować nowe zadania. Bardziej szczegółowe instrukcje znajdziesz w naszym repozytorium GitHub.