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 :

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!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

AI

Construire des solutions IDP bien conçues avec une approche personnalisée - Partie 6 Durabilité

Un projet de traitement intelligent de documents (IDP) combine généralement la reconnaissance optique de caractères (...

AI

Améliorer le traitement intelligent des documents AWS avec l'IA générative

La classification, l'extraction et l'analyse des données peuvent être un défi pour les organisations qui traitent d'i...

Actualités sur l'IA

Commencez avec la distribution open-source Amazon SageMaker

Les scientifiques de données ont besoin d'un environnement cohérent et reproductible pour les charges de travail de l...

AI

Recherche intelligente du contenu Drupal en utilisant Amazon Kendra

Amazon Kendra est un service de recherche intelligent alimenté par l'apprentissage automatique (ML). Amazon Kendra vo...

AI

Apprenez à construire et déployer des agents LLM utilisant des outils en utilisant les modèles de base AWS SageMaker JumpStart

Les agents de modèle de langage étendu (LLM) sont des programmes qui étendent les capacités des LLM autonomes avec 1)...

Actualités sur l'IA

Créez un pipeline de traduction automatique multilingue avec Amazon Translate Active Custom Translation.

Plongez dans l'apprentissage en profondeur (D2L.ai) est un manuel open-source qui rend l'apprentissage en profondeur ...