Noyau fédéré

Ce document présente la couche centrale de TFF qui sert de base à l'apprentissage fédéré et aux futurs algorithmes fédérés sans apprentissage possibles.

Pour une introduction douce à Federated Core, veuillez lire les didacticiels suivants, car ils présentent certains des concepts fondamentaux par l'exemple et démontrent étape par étape la construction d'un simple algorithme de moyenne fédérée.

Nous vous encourageons également à vous familiariser avec Federated Learning et les didacticiels associés sur la classification d'images et la génération de texte , car les utilisations de l'API Federated Core (API FC) pour l'apprentissage fédéré fournissent un contexte important pour certains des choix que nous avons faits dans concevoir cette couche.

Aperçu

Objectifs, utilisations prévues et portée

Federated Core (FC) est mieux compris comme un environnement de programmation pour la mise en œuvre de calculs distribués, c'est-à-dire des calculs impliquant plusieurs ordinateurs (téléphones mobiles, tablettes, appareils embarqués, ordinateurs de bureau, capteurs, serveurs de bases de données, etc.) qui peuvent chacun effectuer des tâches non-exécutables. traitement trivial localement et communiquer à travers le réseau pour coordonner leur travail.

Le terme distribué est très générique et TFF ne cible pas tous les types possibles d'algorithmes distribués. Nous préférons donc utiliser le terme moins générique de calcul fédéré pour décrire les types d'algorithmes qui peuvent être exprimés dans ce cadre.

Bien que définir le terme calcul fédéré de manière totalement formelle sorte du cadre de ce document, pensez aux types d'algorithmes que vous pourriez voir exprimés en pseudocode dans une publication de recherche décrivant un nouvel algorithme d'apprentissage distribué.

L'objectif de FC, en un mot, est de permettre une représentation tout aussi compacte, à un niveau d'abstraction similaire à celui d'un pseudocode, de la logique du programme qui n'est pas du pseudocode, mais plutôt exécutable dans une variété d'environnements cibles.

La caractéristique clé des types d’algorithmes que FC est conçu pour exprimer est que les actions des participants au système sont décrites de manière collective. Ainsi, on a tendance à parler de chaque appareil transformant localement les données, et des appareils coordonnant le travail par un coordinateur centralisé diffusant , collectant ou agrégeant leurs résultats.

Si TFF a été conçu pour pouvoir aller au-delà des simples architectures client-serveur , la notion de traitement collectif est fondamentale. Cela est dû aux origines de TFF dans l'apprentissage fédéré, une technologie conçue à l'origine pour prendre en charge les calculs sur des données potentiellement sensibles qui restent sous le contrôle des appareils clients et qui ne peuvent pas être simplement téléchargées vers un emplacement centralisé pour des raisons de confidentialité. Bien que chaque client de ces systèmes fournisse des données et de la puissance de traitement pour calculer un résultat par le système (un résultat dont nous nous attendons généralement à ce qu'il soit utile à tous les participants), nous nous efforçons également de préserver la confidentialité et l'anonymat de chaque client.

Ainsi, alors que la plupart des cadres d'informatique distribuée sont conçus pour exprimer le traitement du point de vue des participants individuels, c'est-à-dire au niveau des échanges de messages individuels point à point, et de l'interdépendance des transitions d'état locales du participant avec les messages entrants et sortants. , le Federated Core de TFF est conçu pour décrire le comportement du système du point de vue global du système (de la même manière que, par exemple, MapReduce ).

Par conséquent, alors que les frameworks distribués à des fins générales peuvent proposer des opérations telles que l'envoi et la réception comme blocs de construction, FC fournit des blocs de construction tels que tff.federated_sum , tff.federated_reduce ou tff.federated_broadcast qui encapsulent des protocoles distribués simples.

Langue

Interface Python

TFF utilise un langage interne pour représenter les calculs fédérés, dont la syntaxe est définie par la représentation sérialisable dans computation.proto . Cependant, les utilisateurs de l'API FC n'auront généralement pas besoin d'interagir directement avec ce langage. Au lieu de cela, nous fournissons une API Python (l'espace de noms tff ) qui l'entoure comme moyen de définir les calculs.

Plus précisément, TFF fournit des décorateurs de fonctions Python tels que tff.federated_computation qui tracent les corps des fonctions décorées et produisent des représentations sérialisées de la logique de calcul fédérée dans le langage de TFF. Une fonction décorée avec tff.federated_computation agit comme un support d'une telle représentation sérialisée et peut l'intégrer comme élément de base dans le corps d'un autre calcul, ou l'exécuter à la demande lorsqu'elle est invoquée.

Voici juste un exemple ; plus d'exemples peuvent être trouvés dans les didacticiels sur les algorithmes personnalisés .

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

Les lecteurs familiers avec TensorFlow non passionnés trouveront cette approche analogue à l'écriture de code Python qui utilise des fonctions telles que tf.add ou tf.reduce_sum dans une section de code Python qui définit un graphique TensorFlow. Bien que le code soit techniquement exprimé en Python, son objectif est de construire une représentation sérialisable d'un tf.Graph en dessous, et c'est le graphique, et non le code Python, qui est exécuté en interne par le runtime TensorFlow. De même, on peut considérer tff.federated_mean comme l'insertion d'une opération fédérée dans un calcul fédéré représenté par get_average_temperature .

Une partie de la raison pour laquelle FC définit un langage est liée au fait que, comme indiqué ci-dessus, les calculs fédérés spécifient des comportements collectifs distribués et, en tant que tels, leur logique est non locale. Par exemple, TFF fournit des opérateurs dont les entrées et sorties peuvent exister à différents endroits du réseau.

Cela nécessite un langage et un système de types qui capturent la notion de distribution.

Système de saisie

Federated Core propose les catégories de types suivantes. En décrivant ces types, nous pointons vers les constructeurs de types et introduisons une notation compacte, car c'est un moyen pratique de décrire les types de calculs et d'opérateurs.

Tout d’abord, voici les catégories de types qui sont conceptuellement similaires à ceux que l’on trouve dans les langages traditionnels existants :

  • Types de tenseurs ( tff.TensorType ). Tout comme dans TensorFlow, ceux-ci ont dtype et shape . La seule différence est que les objets de ce type ne se limitent pas aux instances tf.Tensor en Python qui représentent les sorties des opérations TensorFlow dans un graphe TensorFlow, mais peuvent également inclure des unités de données qui peuvent être produites, par exemple, en tant que sortie d'un graphe distribué. protocole d'agrégation. Ainsi, le type tenseur TFF est simplement une version abstraite d'une représentation physique concrète d'un tel type en Python ou TensorFlow.

    TensorTypes de TFF peuvent être plus stricts dans leur traitement (statique) des formes que TensorFlow. Par exemple, le système de types de TFF traite un tenseur de rang inconnu comme attribuable à tout autre tenseur du même dtype , mais non attribuable à un tenseur de rang fixe. Ce traitement évite certains échecs d'exécution (par exemple, tenter de remodeler un tenseur de rang inconnu en une forme avec un nombre d'éléments incorrect), au prix d'une plus grande rigueur dans les calculs que TFF accepte comme valides.

    La notation compacte pour les types tensoriels est dtype ou dtype[shape] . Par exemple, int32 et int32[10] sont respectivement les types d’entiers et de vecteurs int.

  • Types de séquence ( tff.SequenceType ). Il s'agit de l'équivalent abstrait de TFF du concept concret de TensorFlow de tf.data.Dataset s. Les éléments des séquences peuvent être consommés de manière séquentielle et peuvent inclure des types complexes.

    La représentation compacte des types de séquence est T* , où T est le type d'éléments. Par exemple int32* représente une séquence entière.

  • Types de tuples nommés ( tff.StructType ). Il s'agit de la manière dont TFF construit des tuples et des structures de type dictionnaire comportant un nombre prédéfini d' éléments avec des types spécifiques, nommés ou non. Il est important de noter que le concept de tuple nommé de TFF englobe l'équivalent abstrait des tuples d'arguments de Python, c'est-à-dire des collections d'éléments dont certains, mais pas tous, sont nommés, et certains sont positionnels.

    La notation compacte pour les tuples nommés est <n_1=T_1, ..., n_k=T_k> , où n_k sont des noms d'éléments facultatifs et T_k sont des types d'éléments. Par exemple, <int32,int32> est une notation compacte pour une paire d'entiers sans nom, et <X=float32,Y=float32> est une notation compacte pour une paire de flottants nommés X et Y qui peuvent représenter un point sur un plan. . Les tuples peuvent être imbriqués ou mélangés avec d'autres types, par exemple <X=float32,Y=float32>* serait une notation compacte pour une séquence de points.

  • Types de fonctions ( tff.FunctionType ). TFF est un framework de programmation fonctionnel, avec des fonctions traitées comme des valeurs de première classe . Les fonctions ont au plus un argument et exactement un résultat.

    La notation compacte pour les fonctions est (T -> U) , où T est le type d'un argument et U est le type du résultat, ou ( -> U) s'il n'y a pas d'argument (bien que les fonctions sans argument soient un dégénéré concept qui existe principalement uniquement au niveau Python). Par exemple (int32* -> int32) est une notation pour un type de fonctions qui réduisent une séquence entière à une seule valeur entière.

Les types suivants abordent l’aspect des systèmes distribués des calculs TFF. Comme ces concepts sont quelque peu uniques à TFF, nous vous encourageons à vous référer au didacticiel sur les algorithmes personnalisés pour des commentaires et des exemples supplémentaires.

  • Type d'emplacement . Ce type n'est pas encore exposé dans l'API publique autrement que sous la forme de 2 littéraux tff.SERVER et tff.CLIENTS que vous pouvez considérer comme des constantes de ce type. Il est cependant utilisé en interne et sera introduit dans l’API publique dans les prochaines versions. La représentation compacte de ce type est placement .

    Un placement représente un collectif de participants au système qui jouent un rôle particulier. La version initiale cible les calculs client-serveur, dans lesquels il existe 2 groupes de participants : des clients et un serveur (vous pouvez considérer ce dernier comme un groupe singleton). Cependant, dans des architectures plus élaborées, il pourrait y avoir d'autres rôles, tels que des agrégateurs intermédiaires dans un système multi-niveaux, qui pourraient effectuer différents types d'agrégation ou utiliser des types de compression/décompression de données différents de ceux utilisés par le serveur ou par le serveur. les clients.

    L'objectif principal de la définition de la notion de placements est de servir de base à la définition des types fédérés .

  • Types fédérés ( tff.FederatedType ). Une valeur de type fédéré est une valeur hébergée par un groupe de participants au système défini par un emplacement spécifique (tel que tff.SERVER ou tff.CLIENTS ). Un type fédéré est défini par la valeur de placement (il s'agit donc d'un type dépendant ), le type de membres constituants (quel type de contenu chacun des participants héberge localement) et le bit supplémentaire all_equal qui spécifie si tous les participants sont localement. hébergeant le même élément.

    La notation compacte pour le type fédéré de valeurs qui incluent des éléments (constituants membres) de type T , chacun hébergé par le groupe (placement) G est T@G ou {T}@G avec le bit all_equal défini ou non défini, respectivement.

    Par exemple:

    • {int32}@CLIENTS représente une valeur fédérée composée d'un ensemble d'entiers potentiellement distincts, un par appareil client. Notez que nous parlons d’une seule valeur fédérée englobant plusieurs éléments de données qui apparaissent à plusieurs emplacements sur le réseau. Une façon de voir cela est comme une sorte de tenseur avec une dimension « réseau », bien que cette analogie ne soit pas parfaite car TFF ne permet pas un accès aléatoire aux constituants membres d'une valeur fédérée.

    • {<X=float32,Y=float32>*}@CLIENTS représente un ensemble de données fédéré , une valeur composée de plusieurs séquences de coordonnées XY , une séquence par appareil client.

    • <weights=float32[10,5],bias=float32[5]>@SERVER représente un tuple nommé de tenseurs de poids et de biais sur le serveur. Puisque nous avons supprimé les accolades, cela indique que le bit all_equal est défini, c'est-à-dire qu'il n'y a qu'un seul tuple (quel que soit le nombre de répliques de serveur qu'il peut y avoir dans un cluster hébergeant cette valeur).

Blocs de construction

Le langage de Federated Core est une forme de lambda-calcul , avec quelques éléments supplémentaires.

Il fournit les abstractions de programmation suivantes actuellement exposées dans l'API publique :

  • Calculs TensorFlow ( tff.tf_computation ). Il s'agit de sections de code TensorFlow encapsulées sous forme de composants réutilisables dans TFF à l'aide du décorateur tff.tf_computation . Ils ont toujours des types fonctionnels et contrairement aux fonctions de TensorFlow, ils peuvent prendre des paramètres structurés ou renvoyer des résultats structurés d'un type séquence.

    Voici un exemple, un calcul TF de type (int32* -> int) qui utilise l'opérateur tf.data.Dataset.reduce pour calculer une somme d'entiers :

    @tff.tf_computation(tff.SequenceType(np.int32))
    def add_up_integers(x):
      return x.reduce(np.int32(0), lambda x, y: x + y)
    
  • Opérateurs intrinsèques ou fédérés ( tff.federated_... ). Il s'agit d'une bibliothèque de fonctions telles que tff.federated_sum ou tff.federated_broadcast qui constituent l'essentiel de l'API FC, dont la plupart représentent des opérateurs de communication distribués à utiliser avec TFF.

    Nous les appelons intrinsèques car, un peu comme les fonctions intrinsèques , il s'agit d'un ensemble ouvert et extensible d'opérateurs qui sont compris par TFF et compilés dans un code de niveau inférieur.

    La plupart de ces opérateurs ont des paramètres et des résultats de types fédérés, et la plupart sont des modèles pouvant être appliqués à différents types de données.

    Par exemple, tff.federated_broadcast peut être considéré comme un opérateur modèle d'un type fonctionnel T@SERVER -> T@CLIENTS .

  • Expressions lambda ( tff.federated_computation ). Une expression lambda dans TFF est l'équivalent d'un lambda ou def en Python ; il se compose du nom du paramètre et d'un corps (expression) qui contient des références à ce paramètre.

    Dans le code Python, ceux-ci peuvent être créés en décorant les fonctions Python avec tff.federated_computation et en définissant un argument.

    Voici un exemple d'expression lambda que nous avons déjà mentionné plus tôt :

    @tff.federated_computation(tff.FederatedType(np.float32, tff.CLIENTS))
    def get_average_temperature(sensor_readings):
      return tff.federated_mean(sensor_readings)
    
  • Littéraux de placement . Pour l'instant, seuls tff.SERVER et tff.CLIENTS permettent de définir des calculs client-serveur simples.

  • Invocations de fonctions ( __call__ ). Tout ce qui a un type fonctionnel peut être invoqué à l'aide de la syntaxe Python __call__ standard. L'invocation est une expression dont le type est le même que le type du résultat de la fonction invoquée.

    Par exemple:

    • add_up_integers(x) représente une invocation du calcul TensorFlow défini précédemment sur un argument x . Le type de cette expression est int32 .

    • tff.federated_mean(sensor_readings) représente une invocation de l'opérateur de moyenne fédérée sur sensor_readings . Le type de cette expression est float32@SERVER (en supposant le contexte de l'exemple ci-dessus).

  • Former des tuples et sélectionner leurs éléments. Expressions Python de la forme [x, y] , x[y] ou xy qui apparaissent dans les corps des fonctions décorées avec tff.federated_computation .