Apprentissage fédéré utilisant Hugging Face et Flower
'Federated Learning with Hugging Face and Flower'
Ce tutoriel montrera comment exploiter Hugging Face pour fédérer l’entraînement de modèles de langage sur plusieurs clients en utilisant Flower. Plus précisément, nous affinerons un modèle Transformer pré-entraîné (distilBERT) pour la classification de séquences sur un ensemble de données de notes IMDB. L’objectif final est de détecter si une note de film est positive ou négative.
Un cahier est également disponible ici mais au lieu de s’exécuter sur plusieurs clients séparés, il utilise la fonctionnalité de simulation de Flower (en utilisant flwr['simulation']
) afin d’émuler un environnement fédéré dans Google Colab (cela signifie également qu’au lieu d’appeler start_server
, nous appellerons start_simulation
, et que quelques autres modifications sont nécessaires).
Dépendances
Pour suivre ce tutoriel, vous devrez installer les packages suivants : datasets
, evaluate
, flwr
, torch
et transformers
. Cela peut être fait en utilisant pip
:
- Accélérer l’inférence de diffusion stable sur les processeurs Intel
- Inférence rapide sur de grands modèles de langage BLOOMZ sur l’accélérateur Habana Gaudi2
- Bulletin d’éthique et de société n°3 Ouverture éthique chez Hugging Face
pip install datasets evaluate flwr torch transformers
Flux de travail Hugging Face standard
Gestion des données
Pour récupérer l’ensemble de données IMDB, nous utiliserons la bibliothèque datasets
de Hugging Face. Ensuite, nous devons tokenizer les données et créer des dataloaders PyTorch
, tout cela est fait dans la fonction load_data
:
import random
import torch
from datasets import load_dataset
from torch.utils.data import DataLoader
from transformers import AutoTokenizer, DataCollatorWithPadding
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
CHECKPOINT = "distilbert-base-uncased"
def load_data():
"""Charger les données IMDB (entraînement et évaluation)"""
raw_datasets = load_dataset("imdb")
raw_datasets = raw_datasets.shuffle(seed=42)
# supprimer le fractionnement des données inutiles
del raw_datasets["unsupervised"]
tokenizer = AutoTokenizer.from_pretrained(CHECKPOINT)
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True)
# Nous prendrons un petit échantillon afin de réduire le temps de calcul, cela est facultatif
train_population = random.sample(range(len(raw_datasets["train"])), 100)
test_population = random.sample(range(len(raw_datasets["test"])), 100)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
tokenized_datasets["train"] = tokenized_datasets["train"].select(train_population)
tokenized_datasets["test"] = tokenized_datasets["test"].select(test_population)
tokenized_datasets = tokenized_datasets.remove_columns("text")
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
trainloader = DataLoader(
tokenized_datasets["train"],
shuffle=True,
batch_size=32,
collate_fn=data_collator,
)
testloader = DataLoader(
tokenized_datasets["test"], batch_size=32, collate_fn=data_collator
)
return trainloader, testloader
trainloader, testloader = load_data()
Entraînement et test du modèle
Une fois que nous avons une façon de créer nos trainloader et testloader, nous pouvons nous occuper de l’entraînement et des tests. Cela est très similaire à n’importe quelle boucle d’entraînement ou de test PyTorch
:
from evaluate import load as load_metric
from transformers import AdamW
def train(net, trainloader, epochs):
optimizer = AdamW(net.parameters(), lr=5e-5)
net.train()
for _ in range(epochs):
for batch in trainloader:
batch = {k: v.to(DEVICE) for k, v in batch.items()}
outputs = net(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
optimizer.zero_grad()
def test(net, testloader):
metric = load_metric("accuracy")
loss = 0
net.eval()
for batch in testloader:
batch = {k: v.to(DEVICE) for k, v in batch.items()}
with torch.no_grad():
outputs = net(**batch)
logits = outputs.logits
loss += outputs.loss.item()
predictions = torch.argmax(logits, dim=-1)
metric.add_batch(predictions=predictions, references=batch["labels"])
loss /= len(testloader.dataset)
accuracy = metric.compute()["accuracy"]
return loss, accuracy
Création du modèle lui-même
Pour créer le modèle lui-même, nous allons simplement charger le modèle pré-entrainé distillBERT en utilisant la classe AutoModelForSequenceClassification
de Hugging Face :
from transformers import AutoModelForSequenceClassification
net = AutoModelForSequenceClassification.from_pretrained(
CHECKPOINT, num_labels=2
).to(DEVICE)
Fédérer l’exemple
L’idée derrière l’apprentissage fédéré est de former un modèle entre plusieurs clients et un serveur sans avoir à partager de données. Cela est réalisé en permettant à chaque client de former le modèle localement sur ses données et d’envoyer ses paramètres au serveur, qui agrège ensuite tous les paramètres des clients en utilisant une stratégie prédéfinie. Ce processus est rendu très simple en utilisant le framework Flower. Si vous souhaitez obtenir une vue d’ensemble plus complète, assurez-vous de consulter ce guide : Qu’est-ce que l’apprentissage fédéré ?
Création du client IMDB
Pour fédérer notre exemple sur plusieurs clients, nous devons d’abord écrire notre classe de client Flower (en héritant de la classe flwr.client.NumPyClient
). C’est très simple, car notre modèle est un modèle standard PyTorch
:
from collections import OrderedDict
import flwr as fl
class IMDBClient(fl.client.NumPyClient):
def get_parameters(self, config):
return [val.cpu().numpy() for _, val in net.state_dict().items()]
def set_parameters(self, parameters):
params_dict = zip(net.state_dict().keys(), parameters)
state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict})
net.load_state_dict(state_dict, strict=True)
def fit(self, parameters, config):
self.set_parameters(parameters)
print("Entraînement commencé...")
train(net, trainloader, epochs=1)
print("Entraînement terminé.")
return self.get_parameters(config={}), len(trainloader), {}
def evaluate(self, parameters, config):
self.set_parameters(parameters)
loss, accuracy = test(net, testloader)
return float(loss), len(testloader), {"accuracy": float(accuracy)}
La fonction get_parameters
permet au serveur d’obtenir les paramètres du client. Inversement, la fonction set_parameters
permet au serveur d’envoyer ses paramètres au client. Enfin, la fonction fit
entraîne le modèle localement pour le client, et la fonction evaluate
teste le modèle localement et renvoie les métriques pertinentes.
Nous pouvons maintenant démarrer des instances de clients en utilisant :
fl.client.start_numpy_client(server_address="127.0.0.1:8080",
client=IMDBClient())
Démarrage du serveur
Maintenant que nous avons un moyen d’instancier des clients, nous devons créer notre serveur pour agréger les résultats. En utilisant Flower, cela peut être fait très facilement en choisissant d’abord une stratégie (ici, nous utilisons FedAvg
, qui définit les poids globaux comme la moyenne des poids de tous les clients à chaque round) puis en utilisant la fonction flwr.server.start_server
:
def weighted_average(metrics):
accuracies = [num_examples * m["accuracy"] for num_examples, m in metrics]
losses = [num_examples * m["loss"] for num_examples, m in metrics]
examples = [num_examples for num_examples, _ in metrics]
return {"accuracy": sum(accuracies) / sum(examples), "loss": sum(losses) / sum(examples)}
# Définir la stratégie
strategy = fl.server.strategy.FedAvg(
fraction_fit=1.0,
fraction_evaluate=1.0,
evaluate_metrics_aggregation_fn=weighted_average,
)
# Démarrer le serveur
fl.server.start_server(
server_address="0.0.0.0:8080",
config=fl.server.ServerConfig(num_rounds=3),
strategy=strategy,
)
La fonction weighted_average
permet d’agréger les métriques distribuées parmi les clients (essentiellement cela nous permet d’afficher une moyenne précise de la précision et de la perte pour chaque round).
Mettre tout ensemble
Si vous voulez voir tout cela mis ensemble, vous devriez consulter l’exemple de code que nous avons écrit pour le repo Flower : https://github.com/adap/flower/tree/main/examples/quickstart_huggingface .
Bien sûr, ceci est un exemple très basique, et beaucoup de choses peuvent être ajoutées ou modifiées, c’était simplement pour montrer comment nous pourrions simplement fédérer un flux de travail Hugging Face en utilisant Flower.
Notez que dans cet exemple, nous avons utilisé PyTorch
, mais nous aurions très bien pu utiliser TensorFlow
.
We will continue to update IPGirl; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles
- StackLLaMA Un guide pratique pour former LLaMA avec RLHF
- Création d’une IA préservant la vie privée avec Substra
- Classification de graphes avec les Transformers
- Accélérer les Transformers Hugging Face avec AWS Inferentia2
- Comment héberger un jeu Unity dans un espace
- Présentation du blog HuggingFace pour les locuteurs chinois Favoriser la collaboration avec la communauté chinoise de l’IA
- Entraînement d’un modèle de langage avec 🤗 Transformers en utilisant TensorFlow et TPUs