Ottimizzazione delle aggregazioni consigliate per l'apprendimento

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza la fonte su GitHub Scarica taccuino

Il tff.learning modulo contiene un certo numero di modi per udpates modello aggregati con configurazione di default consigliata:

In questo tutorial, spieghiamo la motivazione sottostante, come vengono implementati e forniamo suggerimenti su come personalizzare la loro configurazione.


!pip install --quiet --upgrade tensorflow-federated-nightly
!pip install --quiet --upgrade nest-asyncio

import nest_asyncio
nest_asyncio.apply()
import math
import tensorflow_federated as tff
tff.federated_computation(lambda: 'Hello, World!')()
b'Hello, World!'

Metodi di aggregazione sono rappresentati da oggetti che possono essere passati al tff.learning.build_federated_averaging_process come model_update_aggregation_factory nell'argomento chiave. Come tali, gli aggregatori qui discusse possono essere usate direttamente per modificare una precedente esercitazione sull'apprendimento federata.

La linea di base media ponderata dal FedAvg algoritmo può essere espresso utilizzando tff.aggregators.MeanFactory come segue:

mean = tff.aggregators.MeanFactory()
iterative_process = tff.learning.build_federated_averaging_process(
    ...,
    model_update_aggregation_factory=mean)

Le tecniche che possono essere utilizzate per estendere la media ponderata trattate in questo tutorial sono:

  • azzeramento
  • Ritaglio
  • Privacy differenziale
  • Compressione
  • Aggregazione sicura

L'estensione viene effettuata utilizzando la composizione, in cui il MeanFactory avvolge una fabbrica interna, delegando una parte dell'aggregazione, o è esso stesso avvolto da un'altra fabbrica aggregazione. Per maggiori dettagli sul progetto, consultare esecuzione aggregatori personalizzati del tutorial.

Innanzitutto, spiegheremo come abilitare e configurare queste tecniche individualmente, quindi mostreremo come possono essere combinate insieme.

tecniche

Prima di approfondire le singole tecniche, introduciamo prima l'algoritmo di corrispondenza dei quantili, che sarà utile per configurare le tecniche di seguito.

Corrispondenza quantile

Molte delle tecniche di aggregazione seguenti richiedono l'utilizzo di un limite di norma che controlla alcuni aspetti dell'aggregazione. Tali limiti possono essere forniti come una costante, ma di solito è meglio adattare il limite durante il corso dell'allenamento. Il metodo consigliato è quello di utilizzare l'algoritmo quantile corrispondenza di Andrew et al. (2019) , inizialmente proposto per la sua compatibilità con la privacy differenziale ma utile più ampio. Per stimare il valore in un dato quantile, è possibile utilizzare tff.aggregators.PrivateQuantileEstimationProcess . Ad esempio, per adattarsi alla mediana di una distribuzione, puoi utilizzare:

median_estimate = tff.aggregators.PrivateQuantileEstimationProcess.no_noise(
    initial_estimate=1.0, target_quantile=0.5, learning_rate=0.2)

Tecniche diverse che utilizzano l'algoritmo di stima quantile richiederanno valori diversi dei parametri dell'algoritmo, come vedremo. In generale, aumentando le learning_rate mezzi parametri più rapido adattamento al quantile corretta, ma con una maggiore varianza. Il no_noise classmethod costrutti un quantile processo di corrispondenza che non aggiunge rumore per la privacy differenziale.

azzeramento

L'azzeramento si riferisce alla sostituzione di valori insolitamente grandi con zeri. Qui, "insolitamente grande" potrebbe significare più grande di una soglia predefinita o grande rispetto ai valori dei cicli precedenti del calcolo. L'azzeramento può aumentare la robustezza del sistema fino al danneggiamento dei dati sui client difettosi.

Per calcolare una media di valori con norme L-infinito più grandi di ZEROING_CONSTANT azzerato-out, abbiamo avvolgere una tff.aggregators.MeanFactory con un tff.aggregators.zeroing_factory che esegue l'azzeramento:

zeroing_mean = tff.aggregators.zeroing_factory(
    zeroing_norm=MY_ZEROING_CONSTANT,
    inner_agg_factory=tff.aggregators.MeanFactory())

Qui ci avvolgiamo un MeanFactory con uno zeroing_factory perché vogliamo che il (pre-aggregazione) effetti della zeroing_factory di applicare ai valori al cliente prima di essere passati al interno MeanFactory di aggregazione tramite calcolo della media.

Tuttavia, per la maggior parte delle applicazioni si consiglia l'azzeramento adattivo con lo stimatore quantile. Per fare ciò, utilizziamo l'algoritmo di corrispondenza dei quantili come segue:

zeroing_norm = tff.aggregators.PrivateQuantileEstimationProcess.no_noise(
    initial_estimate=10.0,
    target_quantile=0.98,
    learning_rate=math.log(10),
    multiplier=2.0,
    increment=1.0)
zeroing_mean = tff.aggregators.zeroing_factory(
    zeroing_norm=zeroing_norm,
    inner_agg_factory=tff.aggregators.MeanFactory())

# Equivalent to:
# zeroing_mean = tff.learning.robust_aggregator(clipping=False)

Sono stati scelti i parametri in modo che le adatta processo molto rapidamente (relativamente grande learning_rate ) ad un valore alquanto maggiore maggiori valori visto finora. Per un quantile stima Q , la soglia utilizzata per l'azzeramento sarà Q * multiplier + increment .

Ritaglio alla norma L2 vincolata

Ritagliare gli aggiornamenti del client (proiezione su una sfera L2) può migliorare la robustezza ai valori anomali. Un tff.aggregators.clipping_factory è strutturato esattamente come tff.aggregators.zeroing_factory discusso sopra, e può richiedere una costante o una tff.templates.EstimationProcess come clipping_norm argomento. La best practice consigliata consiste nell'utilizzare un ritaglio che si adatti moderatamente rapidamente a una norma moderatamente alta, come segue:

clipping_norm = tff.aggregators.PrivateQuantileEstimationProcess.no_noise(
    initial_estimate=1.0,
    target_quantile=0.8,
    learning_rate=0.2)
clipping_mean = tff.aggregators.clipping_factory(
    clipping_norm=clipping_norm,
    inner_agg_factory=tff.aggregators.MeanFactory())

# Equivalent to:
# clipping_mean = tff.learning.robust_aggregator(zeroing=False)

Nella nostra esperienza nel corso di molti problemi, il valore preciso di target_quantile non sembra importare troppo fino a quando i tassi di apprendimento sono sintonizzati in modo appropriato. Tuttavia, impostarlo su un valore molto basso potrebbe richiedere l'aumento della velocità di apprendimento del server per le migliori prestazioni, rispetto al non utilizzo del clipping, motivo per cui consigliamo 0.8 per impostazione predefinita.

Privacy differenziale

TFF supporta anche l'aggregazione privata in modo differenziale, utilizzando il ritaglio adattivo e il rumore gaussiano. Un aggregatore per eseguire una media differenziata privata può essere costruito come segue:

dp_mean = tff.aggregators.DifferentiallyPrivateFactory.gaussian_adaptive(
    noise_multiplier=0.1, clients_per_round=100)

# Equivalent to:
# dp_mean = tff.learning.dp_aggregator(
#   noise_multiplier=0.1, clients_per_round=100, zeroing=False)

Una guida su come impostare la noise_multiplier argomento può essere trovato nel tutorial di TFF DP .

Compressione con perdita

Rispetto alla compressione senza perdita di dati come gzip, la compressione con perdita di dati generalmente si traduce in un rapporto di compressione molto più elevato e può ancora essere combinata con la compressione senza perdita di dati in seguito. Poiché è necessario dedicare meno tempo alla comunicazione client-server, i cicli di formazione vengono completati più rapidamente. A causa della natura intrinsecamente randomizzata degli algoritmi di apprendimento, fino a una certa soglia, l'inesattezza della compressione con perdita non ha un impatto negativo sulle prestazioni complessive.

La raccomandazione predefinito è di usare semplice quantizzazione uniforme (vedi Suresh et al. , Per esempio), parametrizzata da due valori: la compressione dimensione tensore threshold e il numero di quantization_bits . Per ogni tensore t , se il numero di elementi di t è minore o uguale alla threshold , non è compresso. Se è maggiore, gli elementi di t sono quantizzati utilizzando arrotondamento randomizzato per quantizaton_bits bit. Cioè, applichiamo l'operazione

t = round((t - min(t)) / (max(t) - min(t)) * (2**quantizaton_bits - 1)),

conseguente valori interi nell'intervallo [0, 2**quantizaton_bits-1] . I valori quantizzati vengono impacchettati direttamente in un tipo intero per la trasmissione, quindi viene applicata la trasformazione inversa.

Si consiglia di impostare quantizaton_bits pari a 8 e threshold pari a 20000:

compressed_mean = tff.aggregators.MeanFactory(
    tff.aggregators.EncodedSumFactory.quantize_above_threshold(
        quantization_bits=8, threshold=20000))

# Equivalent to:
# compressed_mean = tff.learning.compression_aggregator(zeroing=False, clipping=False)

Suggerimenti per la messa a punto

Entrambi i parametri, quantization_bits e threshold possono essere regolati, e il numero di clienti che partecipano a ogni giro di formazione possono anche avere un impatto l'efficacia di compressione.

Soglia. Viene scelto il valore predefinito di 20000 perché abbiamo osservato che le variabili con un numero ridotto di elementi, come i bias nei tipi di layer comuni, sono molto più sensibili al rumore introdotto. Inoltre, c'è poco da guadagnare dalla compressione di variabili con un piccolo numero di elementi in pratica, poiché la loro dimensione non compressa è relativamente piccola per cominciare.

In alcune applicazioni può avere senso modificare la scelta della soglia. Ad esempio, i bias del livello di output di un modello di classificazione possono essere più sensibili al rumore. Se ci si allena un modello di linguaggio con un vocabolario di 20004, è possibile impostare threshold per essere 20004.

Bit di quantizzazione. Il valore predefinito di 8 per quantization_bits dovrebbe andare bene per la maggior parte degli utenti. Se 8 funziona bene e vuoi ottenere un po' più di prestazioni, potresti provare a ridurlo a 7 o 6. Se le risorse consentono di eseguire una piccola ricerca sulla griglia, ti consigliamo di identificare il valore per il quale l'addestramento diventa instabile o la qualità del modello finale inizia a peggiorare, quindi aumenta il valore di due. Per esempio, se l'impostazione quantization_bits a 5 opere, ma impostandolo a 4 degrada il modello, consigliamo di default per essere 6 per essere "al sicuro".

Clienti per turno. Si noti che aumentando in modo significativo il numero di clienti per ogni turno può abilitare un valore più piccolo per quantization_bits di lavorare bene, perché l'imprecisione randomizzato introdotto dalla quantizzazione può essere livellata con una media di oltre ulteriori aggiornamenti client.

Aggregazione sicura

Per Secure Aggregation (SecAgg) ci riferiamo a un protocollo crittografico in cui gli aggiornamenti del client sono crittografati in modo tale che il server possa solo decrittografarne la somma. Se il numero di client che riportano è insufficiente, il server non apprenderà nulla e in nessun caso il server sarà in grado di ispezionare i singoli aggiornamenti. Questo è realizzato utilizzando il tff.federated_secure_sum_bitwidth dell'operatore.

Gli aggiornamenti del modello sono valori in virgola mobile, ma SecAgg opera su numeri interi. Pertanto è necessario ritagliare tutti i valori grandi su un limite prima della discretizzazione su un tipo intero. Il limite di ritaglio può essere una costante o determinato in modo adattivo (impostazione predefinita consigliata). Gli interi vengono quindi sommati in modo sicuro e la somma viene mappata nuovamente al dominio in virgola mobile.

Per calcolare una media ponderata dei valori sommati utilizzando SecAgg con MY_SECAGG_BOUND come il ritaglio vincolato, passare SecureSumFactory a MeanFactory come:

secure_mean = tff.aggregators.MeanFactory(
    tff.aggregators.SecureSumFactory(MY_SECAGG_BOUND))

Per fare lo stesso mentre si determinano i limiti in modo adattivo:

secagg_bound = tff.aggregators.PrivateQuantileEstimationProcess.no_noise(
    initial_estimate=50.0,
    target_quantile=0.95,
    learning_rate=1.0,
    multiplier=2.0)
secure_mean = tff.aggregators.MeanFactory(
    tff.aggregators.SecureSumFactory(secagg_bound))

# Equivalent to:
# secure_mean = tff.learning.secure_aggregator(zeroing=Fasle, clipping=False)

Suggerimenti per la messa a punto

I parametri adattivi sono stati scelti in modo che i limiti siano stretti (non perderemo molta precisione nella discretizzazione) ma il clipping avviene raramente.

Se si regolano i parametri, tenere presente che il protocollo SecAgg somma gli aggiornamenti del modello ponderato, dopo aver ponderato nella media. I pesi sono in genere il numero di punti dati elaborati localmente, quindi tra diverse attività, il limite corretto potrebbe dipendere da questa quantità.

Si consiglia di non utilizzare il increment argomento chiave per la creazione di adaptive secagg_bound , in quanto ciò potrebbe causare una grande perdita di precisione relativa, nel caso le estremità effettivi di stima per essere piccola.

Il frammento di codice sopra utilizzerà SecAgg solo i valori ponderati. Se SecAgg dovesse essere utilizzato anche per la somma dei pesi, si consiglia di impostare i limiti come costanti, poiché in una configurazione di allenamento comune, il peso più grande possibile sarà noto in anticipo:

secure_mean = tff.aggregators.MeanFactory(
    value_sum_factory=tff.aggregators.SecureSumFactory(secagg_bound),
    weight_sum_factory=tff.aggregators.SecureSumFactory(
        upper_bound_threshold=MAX_WEIGHT, lower_bound_threshold=0.0))

Tecniche di composizione

Le singole tecniche per estendere una media introdotte sopra possono essere combinate insieme.

Raccomandiamo l'ordine in cui queste tecniche vengono applicate ai clienti per essere

  1. azzeramento
  2. Ritaglio
  3. Altre tecniche

Aggregatori in tff.aggregators modulo sono composti da incarto "aggregatori interni" (il cui pre-aggregazione effetti accadere ultimi e post-aggregazione effetti avvengono prima) nel "aggregatori esterni". Ad esempio, per eseguire l'azzeramento, il ritaglio e la compressione (in quest'ordine), si scriverebbe:

# Compression is innermost because its pre-aggregation effects are last.
compressed_mean = tff.aggregators.MeanFactory(
    tff.aggregators.EncodedSumFactory.quantize_above_threshold(
        quantization_bits=8, threshold=20000))
# Compressed mean is inner aggregator to clipping...
clipped_compressed_mean = tff.aggregators.clipping_factory(
    clipping_norm=MY_CLIPPING_CONSTANT,
    inner_agg_factory=compressed_mean)
# ...which is inner aggregator to zeroing, since zeroing happens first.
final_aggregator = tff.aggregators.zeroing_factory(
    zeroing_norm=MY_ZEROING_CONSTANT,
    inner_agg_factory=clipped_compressed_mean)

Si noti che tale struttura corrisponde ai aggregatori di default per l'apprendimento algoritmi.

Sono possibili anche altre composizioni. Estendiamo questo documento quando siamo sicuri di poter fornire una configurazione predefinita che funzioni in più applicazioni diverse. Per l'implementazione di nuove idee, vedere esecuzione aggregatori personalizzati del tutorial.