Nucleo federato

Questo documento introduce lo strato centrale del TFF che funge da base per l'apprendimento federato e i possibili futuri algoritmi federati non di apprendimento.

Per un'introduzione delicata a Federated Core, leggi i seguenti tutorial, poiché introducono alcuni concetti fondamentali tramite esempi e dimostrano passo dopo passo la costruzione di un semplice algoritmo di media federata.

Ti invitiamo inoltre a familiarizzare con l'apprendimento federato e i tutorial associati sulla classificazione delle immagini e sulla generazione di testo , poiché gli usi dell'API Federated Core (API FC) per l'apprendimento federato forniscono un contesto importante per alcune delle scelte che abbiamo fatto in progettando questo livello.

Panoramica

Obiettivi, usi previsti e ambito

Federated Core (FC) è meglio inteso come un ambiente di programmazione per l'implementazione di calcoli distribuiti, ovvero calcoli che coinvolgono più computer (telefoni cellulari, tablet, dispositivi integrati, computer desktop, sensori, server di database, ecc.) che possono eseguire ciascuno operazioni non elaborazioni banali a livello locale e comunicano attraverso la rete per coordinare il proprio lavoro.

Il termine distribuito è molto generico e TFF non si rivolge a tutti i possibili tipi di algoritmi distribuiti disponibili, quindi preferiamo utilizzare il termine meno generico computazione federata per descrivere i tipi di algoritmi che possono essere espressi in questo framework.

Sebbene definire il termine computazione federata in modo del tutto formale esuli dallo scopo di questo documento, pensa ai tipi di algoritmi che potresti vedere espressi in pseudocodice in una pubblicazione di ricerca che descrive un nuovo algoritmo di apprendimento distribuito.

L'obiettivo di FC, in poche parole, è quello di consentire una rappresentazione altrettanto compatta, a un livello di astrazione simile a quello dello pseudocodice, della logica del programma che non è pseudocodice, ma piuttosto eseguibile in una varietà di ambienti di destinazione.

La caratteristica chiave che definisce il tipo di algoritmi che FC è progettato per esprimere è che le azioni dei partecipanti al sistema sono descritte in modo collettivo. Pertanto, tendiamo a parlare di ciascun dispositivo che trasforma localmente i dati e dei dispositivi che coordinano il lavoro di un coordinatore centralizzato che trasmette , raccoglie o aggrega i loro risultati.

Sebbene TFF sia stato progettato per poter andare oltre le semplici architetture client-server , la nozione di elaborazione collettiva è fondamentale. Ciò è dovuto alle origini del TFF nell'apprendimento federato, una tecnologia originariamente progettata per supportare calcoli su dati potenzialmente sensibili che rimangono sotto il controllo dei dispositivi client e che non possono essere semplicemente scaricati in una posizione centralizzata per motivi di privacy. Mentre ciascun cliente in tali sistemi contribuisce con dati e potenza di elaborazione all'elaborazione di un risultato da parte del sistema (un risultato che generalmente ci aspetteremmo fosse di valore per tutti i partecipanti), ci sforziamo anche di preservare la privacy e l'anonimato di ciascun cliente.

Pertanto, mentre la maggior parte dei framework per il calcolo distribuito sono progettati per esprimere l'elaborazione dal punto di vista dei singoli partecipanti, cioè a livello dei singoli scambi di messaggi punto a punto, e dell'interdipendenza delle transizioni di stato locale del partecipante con i messaggi in entrata e in uscita. , il Federated Core di TFF è progettato per descrivere il comportamento del sistema dalla prospettiva globale dell'intero sistema (in modo simile, ad esempio, a MapReduce ).

Di conseguenza, mentre i framework distribuiti per scopi generali possono offrire operazioni come invio e ricezione come elementi costitutivi, FC fornisce elementi costitutivi come tff.federated_sum , tff.federated_reduce o tff.federated_broadcast che incapsulano semplici protocolli distribuiti.

Lingua

Interfaccia Python

TFF utilizza un linguaggio interno per rappresentare i calcoli federati, la cui sintassi è definita dalla rappresentazione serializzabile in computation.proto . Tuttavia, gli utenti dell'API FC generalmente non avranno bisogno di interagire direttamente con questo linguaggio. Piuttosto, forniamo un'API Python (lo spazio dei nomi tff ) che la avvolge come un modo per definire i calcoli.

Nello specifico, TFF fornisce decoratori di funzioni Python come tff.federated_computation che tracciano il corpo delle funzioni decorate e producono rappresentazioni serializzate della logica di calcolo federato nel linguaggio di TFF. Una funzione decorata con tff.federated_computation agisce come portatore di tale rappresentazione serializzata e può incorporarla come elemento costitutivo nel corpo di un altro calcolo o eseguirla su richiesta quando richiamata.

Ecco solo un esempio; ulteriori esempi possono essere trovati nei tutorial sugli algoritmi personalizzati .

@tff.federated_computation(tff.FederatedType(np.float32, tff.CLIENTS))
def get_average_temperature(sensor_readings):
  return tff.federated_mean(sensor_readings)

I lettori che hanno familiarità con TensorFlow non entusiasti troveranno questo approccio analogo alla scrittura di codice Python che utilizza funzioni come tf.add o tf.reduce_sum in una sezione di codice Python che definisce un grafico TensorFlow. Sebbene il codice sia tecnicamente espresso in Python, il suo scopo è costruire una rappresentazione serializzabile di un tf.Graph sottostante, ed è il grafico, non il codice Python, ad essere eseguito internamente dal runtime TensorFlow. Allo stesso modo, si può pensare a tff.federated_mean come all'inserimento di un'operazione federata in un calcolo federato rappresentato da get_average_temperature .

Parte del motivo per cui FC definisce un linguaggio ha a che fare con il fatto che, come notato sopra, i calcoli federati specificano comportamenti collettivi distribuiti e, come tali, la loro logica non è locale. Ad esempio, TFF fornisce operatori, i cui input e output possono esistere in diversi punti della rete.

Ciò richiede un linguaggio e un sistema di tipi che catturino la nozione di distribuzione.

Digitare Sistema

Federated Core offre le seguenti categorie di tipi. Nel descrivere questi tipi, indichiamo i costruttori di tipi e introduciamo una notazione compatta, poiché è un modo pratico per descrivere tipi di calcoli e operatori.

Innanzitutto, ecco le categorie di tipi che sono concettualmente simili a quelli che si trovano nelle lingue tradizionali esistenti:

  • Tipi di tensore ( tff.TensorType ). Proprio come in TensorFlow, questi hanno dtype e shape . L'unica differenza è che gli oggetti di questo tipo non sono limitati alle istanze tf.Tensor in Python che rappresentano gli output delle operazioni TensorFlow in un grafico TensorFlow, ma possono anche includere unità di dati che possono essere prodotte, ad esempio, come output di un sistema distribuito protocollo di aggregazione. Pertanto, il tipo tensore TFF è semplicemente una versione astratta di una rappresentazione fisica concreta di tale tipo in Python o TensorFlow.

    TensorTypes di TFF possono essere più severi nel trattamento (statico) delle forme rispetto a TensorFlow. Ad esempio, il typesystem di TFF tratta un tensore con rango sconosciuto come assegnabile da qualsiasi altro tensore dello stesso dtype , ma non assegnabile a nessun tensore con rango fisso. Questo trattamento previene alcuni errori di runtime (ad esempio, il tentativo di rimodellare un tensore di rango sconosciuto in una forma con un numero errato di elementi), a costo di una maggiore severità in quali calcoli TFF accetta come validi.

    La notazione compatta per i tipi tensoriali è dtype o dtype[shape] . Ad esempio, int32 e int32[10] sono rispettivamente i tipi di numeri interi e vettori int.

  • Tipi di sequenza ( tff.SequenceType ). Questi sono l'equivalente astratto di TFF del concetto concreto di tf.data.Dataset s di TensorFlow. Gli elementi delle sequenze possono essere consumati in modo sequenziale e possono includere tipi complessi.

    La rappresentazione compatta dei tipi di sequenza è T* , dove T è il tipo di elementi. Ad esempio int32* rappresenta una sequenza di numeri interi.

  • Tipi di tupla denominati ( tff.StructType ). Questo è il modo in cui TFF costruisce tuple e strutture simili a dizionari che hanno un numero predefinito di elementi con tipi specifici, denominati o senza nome. È importante sottolineare che il concetto di tupla con nome di TFF comprende l'equivalente astratto delle tuple di argomenti di Python, cioè raccolte di elementi di cui alcuni, ma non tutti, hanno un nome, e alcuni sono posizionali.

    La notazione compatta per le tuple con nome è <n_1=T_1, ..., n_k=T_k> , dove n_k sono nomi di elementi opzionali e T_k sono tipi di elementi. Ad esempio, <int32,int32> è una notazione compatta per una coppia di numeri interi senza nome e <X=float32,Y=float32> è una notazione compatta per una coppia di numeri in virgola mobile denominati X e Y che può rappresentare un punto su un piano . Le tuple possono essere nidificate così come mescolate con altri tipi, ad esempio <X=float32,Y=float32>* sarebbe una notazione compatta per una sequenza di punti.

  • Tipi di funzione ( tff.FunctionType ). TFF è un framework di programmazione funzionale, con funzioni trattate come valori di prima classe . Le funzioni hanno al massimo un argomento ed esattamente un risultato.

    La notazione compatta per le funzioni è (T -> U) , dove T è il tipo di argomento e U è il tipo del risultato, o ( -> U) se non c'è argomento (sebbene le funzioni senza argomenti siano degenerate concetto che esiste principalmente solo a livello Python). Ad esempio (int32* -> int32) è una notazione per un tipo di funzioni che riducono una sequenza di numeri interi a un singolo valore intero.

I seguenti tipi affrontano l'aspetto dei sistemi distribuiti dei calcoli TFF. Poiché questi concetti sono in qualche modo esclusivi di TFF, ti invitiamo a fare riferimento al tutorial sugli algoritmi personalizzati per ulteriori commenti ed esempi.

  • Tipo di posizionamento . Questo tipo non è ancora esposto nell'API pubblica se non sotto forma di 2 valori letterali tff.SERVER e tff.CLIENTS che puoi considerare come costanti di questo tipo. Viene tuttavia utilizzato internamente e verrà introdotto nell'API pubblica nelle versioni future. La rappresentazione compatta di questo tipo è placement .

    Un posizionamento rappresenta un collettivo di partecipanti al sistema che svolgono un ruolo particolare. La versione iniziale è mirata ai calcoli client-server, in cui sono presenti 2 gruppi di partecipanti: client e un server (puoi pensare a quest'ultimo come un gruppo singleton). Tuttavia, in architetture più elaborate, potrebbero esserci altri ruoli, come aggregatori intermedi in un sistema a più livelli, che potrebbero eseguire diversi tipi di aggregazione o utilizzare diversi tipi di compressione/decompressione dei dati rispetto a quelli utilizzati dal server o i clienti.

    Lo scopo principale della definizione della nozione di posizionamenti è come base per definire i tipi federati .

  • Tipi federati ( tff.FederatedType ). Un valore di tipo federato è quello ospitato da un gruppo di partecipanti al sistema definiti da un posizionamento specifico (come tff.SERVER o tff.CLIENTS ). Un tipo federato è definito dal valore del posizionamento (quindi è un tipo dipendente ), dal tipo di membri costituenti (il tipo di contenuto che ciascuno dei partecipanti ospita localmente) e dal bit aggiuntivo all_equal che specifica se tutti i partecipanti sono localmente ospitare lo stesso oggetto.

    La notazione compatta per il tipo federato di valori che includono elementi (costituenti membri) di tipo T , ciascuno ospitato dal gruppo (posizionamento) G è T@G o {T}@G con il bit all_equal impostato o non impostato, rispettivamente.

    Per esempio:

    • {int32}@CLIENTS rappresenta un valore federato costituito da un insieme di numeri interi potenzialmente distinti, uno per dispositivo client. Tieni presente che stiamo parlando di un singolo valore federato che comprende più elementi di dati che appaiono in più posizioni nella rete. Un modo di pensarlo è come una sorta di tensore con una dimensione di "rete", sebbene questa analogia non sia perfetta perché TFF non consente l'accesso casuale ai membri costituenti di un valore federato.

    • {<X=float32,Y=float32>*}@CLIENTS rappresenta un set di dati federato , un valore costituito da più sequenze di coordinate XY , una sequenza per dispositivo client.

    • <weights=float32[10,5],bias=float32[5]>@SERVER rappresenta una tupla denominata di tensori di peso e bias sul server. Dato che abbiamo eliminato le parentesi graffe, ciò indica che il bit all_equal è impostato, cioè c'è solo una singola tupla (indipendentemente da quante repliche del server potrebbero esserci in un cluster che ospita questo valore).

Elementi costitutivi

Il linguaggio del Federated Core è una forma di lambda-calcolo , con alcuni elementi aggiuntivi.

Fornisce le seguenti astrazioni di programmazione attualmente esposte nell'API pubblica:

  • Calcoli TensorFlow ( tff.tensorflow.computation ). Queste sono sezioni di codice TensorFlow racchiuse come componenti riutilizzabili in TFF utilizzando il decoratore tff.tensorflow.computation . Hanno sempre tipi funzionali e, a differenza delle funzioni in TensorFlow, possono accettare parametri strutturati o restituire risultati strutturati di un tipo di sequenza.

    Ecco un esempio, un calcolo TF di tipo (int32* -> int) che utilizza l'operatore tf.data.Dataset.reduce per calcolare una somma di numeri interi:

    @tff.tensorflow.computation(tff.SequenceType(np.int32))
    def add_up_integers(x):
      return x.reduce(np.int32(0), lambda x, y: x + y)
    
  • Operatori intrinseci o federati ( tff.federated_... ). Si tratta di una libreria di funzioni come tff.federated_sum o tff.federated_broadcast che costituiscono la maggior parte dell'API FC, la maggior parte delle quali rappresenta operatori di comunicazione distribuita da utilizzare con TFF.

    Li chiamiamo intrinseci perché, un po' come le funzioni intrinseche , sono un insieme di operatori estensibili e aperti, compresi da TFF e compilati nel codice di livello inferiore.

    La maggior parte di questi operatori presenta parametri e risultati di tipi federati e la maggior parte sono modelli che possono essere applicati a vari tipi di dati.

    Ad esempio, tff.federated_broadcast può essere considerato come un operatore modello di tipo funzionale T@SERVER -> T@CLIENTS .

  • Espressioni Lambda ( tff.federated_computation ). Un'espressione lambda in TFF è l'equivalente di lambda o def in Python; è costituito dal nome del parametro e da un corpo (espressione) che contiene riferimenti a questo parametro.

    Nel codice Python, questi possono essere creati decorando le funzioni Python con tff.federated_computation e definendo un argomento.

    Ecco un esempio di un'espressione lambda che abbiamo già menzionato in precedenza:

    @tff.federated_computation(tff.FederatedType(np.float32, tff.CLIENTS))
    def get_average_temperature(sensor_readings):
      return tff.federated_mean(sensor_readings)
    
  • Valori letterali di posizionamento . Per ora, solo tff.SERVER e tff.CLIENTS per consentire la definizione di semplici calcoli client-server.

  • Invocazioni di funzioni ( __call__ ). Tutto ciò che ha un tipo funzionale può essere invocato utilizzando la sintassi standard Python __call__ . L'invocazione è un'espressione il cui tipo è lo stesso del risultato della funzione invocata.

    Per esempio:

    • add_up_integers(x) rappresenta un'invocazione del calcolo TensorFlow definito in precedenza su un argomento x . Il tipo di questa espressione è int32 .

    • tff.federated_mean(sensor_readings) rappresenta un'invocazione dell'operatore di media federato su sensor_readings . Il tipo di questa espressione è float32@SERVER (presupponendo il contesto dell'esempio precedente).

  • Formare tuple e selezionare i loro elementi. Espressioni Python nella forma [x, y] , x[y] o xy che appaiono nel corpo delle funzioni decorate con tff.federated_computation .