Réglage des agrégations recommandées pour l'apprentissage

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Le tff.learning module contient un certain nombre de façons de modéliser globales udpates avec la configuration par défaut recommandée:

Dans ce didacticiel, nous expliquons la motivation sous-jacente, comment ils sont mis en œuvre et fournissons des suggestions sur la façon de personnaliser leur configuration.


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

Méthodes d'agrégation sont représentés par des objets qui peuvent être transmis à tff.learning.build_federated_averaging_process comme model_update_aggregation_factory argument mot - clé. À ce titre, les agrégateurs discutés ici peuvent être directement utilisés pour modifier un précédent tutoriel sur l' apprentissage fédéré.

La ligne de base moyenne pondérée en fonction de l' FedAvg algorithme peut être exprimé en utilisant tff.aggregators.MeanFactory comme suit:

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

Les techniques qui peuvent être utilisées pour étendre la moyenne pondérée couvertes dans ce didacticiel sont :

  • Remise à zéro
  • Coupure
  • Confidentialité différentielle
  • Compression
  • Agrégation sécurisée

L'extension est effectuée en utilisant la composition, dans laquelle la MeanFactory enveloppe intérieure d' une usine à laquelle il délègue une partie de l'agrégation, ou est lui-même enveloppé par une autre usine d'agrégation. Pour plus de détails sur la conception, voir exécution agrégateurs personnalisés tutoriel.

Tout d'abord, nous expliquerons comment activer et configurer ces techniques individuellement, puis montrerons comment elles peuvent être combinées entre elles.

Technique

Avant d'approfondir les techniques individuelles, nous introduisons d'abord l'algorithme d'appariement quantile, qui sera utile pour configurer les techniques ci-dessous.

Correspondance quantile

Plusieurs des techniques d'agrégation ci-dessous nécessitent d'utiliser une limite de norme qui contrôle certains aspects de l'agrégation. De telles bornes peuvent être fournies sous forme de constante, mais il est généralement préférable d'adapter la borne au cours de la formation. La méthode recommandée est d'utiliser l'algorithme de correspondance de quantile d' Andrew et al. (2019) , initialement proposé pour sa compatibilité avec la vie privée différentielle mais utile plus largement. Pour estimer la valeur à un quantile donné, vous pouvez utiliser tff.aggregators.PrivateQuantileEstimationProcess . Par exemple, pour s'adapter à la médiane d'une distribution, vous pouvez utiliser :

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

Différentes techniques qui utilisent l'algorithme d'estimation quantile nécessiteront des valeurs différentes des paramètres de l'algorithme, comme nous le verrons. En général, ce qui augmente les learning_rate moyens de paramètres d' adaptation plus rapide au quantile correcte, mais avec une variance plus élevée. Le no_noise classmethod construit un quantile processus d'appariement qui n'ajoute pas le bruit de la vie privée différentielle.

Remise à zéro

La remise à zéro fait référence au remplacement de valeurs inhabituellement grandes par des zéros. Ici, "exceptionnellement grand" pourrait signifier plus grand qu'un seuil prédéfini, ou grand par rapport aux valeurs des cycles précédents du calcul. La remise à zéro peut augmenter la robustesse du système à la corruption des données sur les clients défectueux.

Pour calculer une moyenne des valeurs avec les normes L-infini plus grandes que ZEROING_CONSTANT réduites à zéro, nous envelopper un tff.aggregators.MeanFactory avec un tff.aggregators.zeroing_factory qui effectue la mise à zéro:

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

Nous envelopper ici un MeanFactory avec un zeroing_factory parce que nous voulons que les effets (pré-agrégation) du zeroing_factory à appliquer aux valeurs aux clients avant qu'ils ne soient transmis à l'intérieur MeanFactory pour l' agrégation par moyenne.

Cependant, pour la plupart des applications, nous recommandons la mise à zéro adaptative avec l'estimateur quantile. Pour ce faire, nous utilisons l'algorithme d'appariement quantile comme suit :

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)

Les paramètres ont été choisis de telle sorte que le processus se adapte très rapidement (relativement grande learning_rate ) à une valeur légèrement plus grande que les valeurs les plus élevées observées jusqu'à présent. Pour une estimation de quantile Q , le seuil utilisé pour la réduction à zéro sera Q * multiplier + increment .

Découpage à la norme L2 liée

L'écrêtage des mises à jour du client (projection sur une boule L2) peut améliorer la robustesse aux valeurs aberrantes. Un tff.aggregators.clipping_factory est structuré exactement comme tff.aggregators.zeroing_factory discuté ci - dessus, et peut prendre une constante ou un tff.templates.EstimationProcess comme clipping_norm argument. La meilleure pratique recommandée consiste à utiliser un découpage qui s'adapte modérément rapidement à une norme modérément élevée, comme suit :

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)

Dans notre expérience de nombreux problèmes, la valeur précise de target_quantile ne semble pas trop d' importance tant que les taux d' apprentissage sont correctement réglés. Cependant, le définir très bas peut nécessiter d'augmenter le taux d'apprentissage du serveur pour de meilleures performances, par rapport à la non-utilisation de l'écrêtage, c'est pourquoi nous recommandons 0.8 par défaut.

Confidentialité différentielle

TFF prend également en charge l'agrégation différentielle privée, en utilisant l'écrêtage adaptatif et le bruit gaussien. Un agrégateur pour effectuer une moyenne différentielle privée peut être construit comme suit :

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)

Lignes directrices sur la façon de régler le noise_multiplier argument peut être trouvé dans le tutoriel DP TFF .

La compression avec perte

Par rapport à la compression sans perte telle que gzip, la compression avec perte entraîne généralement un taux de compression beaucoup plus élevé et peut toujours être combinée avec une compression sans perte par la suite. Étant donné que moins de temps doit être consacré à la communication client-serveur, les cycles de formation se terminent plus rapidement. En raison de la nature intrinsèquement aléatoire des algorithmes d'apprentissage, jusqu'à un certain seuil, l'imprécision de la compression avec perte n'a pas d'impact négatif sur les performances globales.

La recommandation par défaut consiste à utiliser une quantification simple uniforme (voir Suresh et al. , Par exemple), paramétrée par deux valeurs: la taille du tenseur compression threshold et le nombre de quantization_bits . Pour chaque tenseur t , si le nombre d'éléments de t est inférieure ou égale à threshold , il est pas compressé. Si elle est plus grande, les éléments de t sont quantifiés à l' aide arrondi aléatoire pour quantizaton_bits les bits. C'est-à-dire que nous appliquons l'opération

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

résultant en des valeurs entières dans la plage de [0, 2**quantizaton_bits-1] . Les valeurs quantifiées sont directement emballées dans un type entier pour la transmission, puis la transformation inverse est appliquée.

Nous vous recommandons de régler quantizaton_bits égal à 8 et threshold égal à 20 000:

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)

Suggestions de réglage

Les deux paramètres, quantization_bits et threshold peuvent être ajustés, et le nombre de clients qui participent à chaque tour de formation peuvent également un impact sur l'efficacité de la compression.

Seuil. La valeur par défaut de 20000 est choisie car nous avons observé que les variables avec un petit nombre d'éléments, telles que les biais dans les types de couches courants, sont beaucoup plus sensibles au bruit introduit. De plus, il y a peu à gagner à compresser des variables avec un petit nombre d'éléments en pratique, car leur taille non compressée est relativement petite au départ.

Dans certaines applications, il peut être judicieux de modifier le choix du seuil. Par exemple, les biais de la couche de sortie d'un modèle de classification peuvent être plus sensibles au bruit. Si vous formez un modèle de langage avec un vocabulaire de 20004, vous pouvez définir le threshold pour être 20004.

Bits de quantification. La valeur par défaut de 8 pour quantization_bits devrait être très bien pour la plupart des utilisateurs. Si 8 fonctionne bien et que vous souhaitez obtenir un peu plus de performances, vous pouvez essayer de le réduire à 7 ou 6. Si les ressources permettent de faire une petite recherche de grille, nous vous recommandons d'identifier la valeur pour laquelle la formation devient instable ou la qualité du modèle final commence à se dégrader, puis augmente cette valeur de deux. Par exemple, si le réglage quantization_bits à 5 œuvres, mais la mise à 4 dégrade le modèle, nous recommandons la valeur par défaut soit 6 être « du bon côté ».

Clients par tour. A noter que l' augmentation significative du nombre de clients par tour peut permettre à une valeur plus petite pour quantization_bits bien fonctionner, car l'inexactitude aléatoire introduite par la quantification peut être aplanies par une moyenne sur plus de mises à jour des clients.

Agrégation sécurisée

Par Secure Aggregation (SecAgg), nous nous référons à un protocole cryptographique dans lequel les mises à jour des clients sont cryptées de manière à ce que le serveur ne puisse décrypter que leur somme. Si le nombre de clients qui font rapport est insuffisant, le serveur n'apprendra rien du tout -- et en aucun cas le serveur ne pourra inspecter les mises à jour individuelles. Ceci est réalisé par l' tff.federated_secure_sum_bitwidth opérateur.

Les mises à jour du modèle sont des valeurs à virgule flottante, mais SecAgg fonctionne sur des entiers. Par conséquent, nous devons couper toutes les grandes valeurs à une certaine limite avant la discrétisation en un type entier. La limite d'écrêtage peut être une constante ou déterminée de manière adaptative (valeur par défaut recommandée). Les nombres entiers sont ensuite additionnés de manière sécurisée et la somme est mappée vers le domaine à virgule flottante.

Pour calculer une moyenne des valeurs pondérées à l' aide SecAgg avec sommées MY_SECAGG_BOUND comme la coupure liée, passer SecureSumFactory à MeanFactory comme:

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

Pour faire de même tout en déterminant les limites de manière adaptative :

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)

Suggestions de réglage

Les paramètres adaptatifs ont été choisis de manière à ce que les bornes soient serrées (on ne perdra pas beaucoup de précision dans la discrétisation) mais l'écrêtage se produit rarement.

Si vous réglez les paramètres, gardez à l'esprit que le protocole SecAgg additionne les mises à jour pondérées du modèle, après pondération dans la moyenne. Les poids sont généralement le nombre de points de données traités localement, donc entre différentes tâches, la limite droite peut dépendre de cette quantité.

Nous ne recommandons pas d' utiliser l' increment argument mot - clé lors de la création d' adaptation secagg_bound , car cela pourrait entraîner une perte de précision relative, dans le cas des extrémités d'estimation réelle par être petit.

L'extrait de code ci-dessus utilisera SecAgg uniquement les valeurs pondérées. Si SecAgg doit également être utilisé pour la somme des poids, nous recommandons que les limites soient définies comme des constantes, car dans une configuration d'entraînement commune, le poids le plus important possible sera connu à l'avance :

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

Techniques de composition

Les techniques individuelles d'extension d'une moyenne introduites ci-dessus peuvent être combinées entre elles.

Nous recommandons que l'ordre dans lequel ces techniques sont appliquées chez les clients soit

  1. Remise à zéro
  2. Coupure
  3. Autres techniques

Les aggregators dans tff.aggregators module sont constitués par enveloppement « aggregators internes » (dont les effets de pré-agrégation se produire derniers et les effets post-agrégation se produisent en premier) à l' intérieur de « aggregators externes ». Par exemple, pour effectuer la mise à zéro, l'écrêtage et la compression (dans cet ordre), on écrirait :

# 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)

Notez que cette structure correspond aux agrégateurs par défaut pour les algorithmes d' apprentissage.

D'autres compositions sont également possibles. Nous étendons ce document lorsque nous sommes convaincus que nous pouvons fournir une configuration par défaut qui fonctionne dans plusieurs applications différentes. Pour la mise en œuvre des idées nouvelles, voir d' exécution agrégateurs personnalisés tutoriel.