Huggingface

2 librairies principales : datasets et transformers

datasets : compatible pytorch, tensorflow, Jax -> retourne des tenseurs PyTorch ou TensorFlow

transformers :

Ces modèles peuvent être fine-tunés pour des tâches spécifiques. Ca permte d’obtenir de bonnes performances sans devoir entraîner un modèle à partir de zéro.

La librairie inclut également des outils pour la tokenization. Chqaue modele a son propre tokenizer.


Structure d’un modele dans pytorch

import torch
from torch.utils.data import Dataset, DataLoader

class CustomDataset(Dataset):
    def __init__(self, data, targets):
        self.data = data
        self.targets = targets

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx], self.targets[idx]

class SimpleNN(torch.nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleNN, self).__init__()
        self.layer1 = torch.nn.Linear(input_size, hidden_size)
        self.relu = torch.nn.ReLU()
        self.layer2 = torch.nn.Linear(hidden_size, output_size)

    def forward(self, x):
        x = self.layer1(x)
        x = self.relu(x)
        x = self.layer2(x)
        return x

    def predict(self, x):
        self.eval()  # Met le modèle en mode évaluation
        with torch.no_grad():  # Désactive le calcul du gradient
            x = torch.from_numpy(x).float()  # Convertit en tenseur (si nécessaire)
            predictions = self.forward(x)  # Effectue le passage vers l'avant
        return predictions.numpy()  # Convertit en numpy (si nécessaire)

ou torch.nn est Base class for all neural network modules.

Exemple d’utilisation

data = torch.randn(100, 10)  # 100 échantillons, 10 caractéristiques
targets = torch.randint(0, 2, (100,))  # 100 cibles, 0 ou 1

dataset = CustomDataset(data, targets)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

model = SimpleNN(input_size=10, hidden_size=5, output_size=2)

CustomDataset : cette classe gère le chargement et le traitement des données.

SimpleNN : simple réseau neuronal avec une couche cachée.

DataLoader : automatise le regroupement des données, le mélange et le chargement par batch pour l’entraînement.


Exemple : Analyse de sentiments avec DistilBERT sur les IMDB movie reviews

avec transformers et datasets

DistilBERT est pré-entraîné par distillation de connaissances pour créer un modèle plus petit avec une inférence plus rapide et nécessitant moins de calcul pour l’entraînement.

Distillation

Workflow typique d’une tâche NLP :

Les données

Choisir le modele

training

Evaluation etc:

Loader un modele: from_pretrained vs from_config

2 façons de charger un modèle :

from_pretrained(pretrained_model_name_or_path, **kwargs) :

from_config(config, **kwargs) :

Code

Notebook sur Google Colab avec T4 free tier

Charger les données

import numpy as np
import torch

from datasets import load_dataset

# Charger le dataset IMDb
dataset = load_dataset('imdb')

# Splitter le dataset
train_dataset = dataset['train']
test_dataset = dataset['test']

Tokenizer (2mn)

DistilBertTokenizerFast derivé de BertTokenizer

# tokenizer
from transformers import DistilBertTokenizerFast

# Initialiser le tokenizer
tokenizer = DistilBertTokenizerFast.from_pretrained('distilbert-base-uncased')

# Tokenizer les datasets
def tokenize_function(examples):
    """applique le tokenizer à chaque exemple dans le dataset.
    - truncation=True assure que les séquences plus longues que max_length sont tronquées,
    - padding='max_length' remplit les séquences plus courtes à la même longueur.
    """

    return tokenizer(
        examples["text"],
        truncation=True,
        padding="max_length",
        max_length=128
    )

#  appliquer le tokenizer
train_dataset = train_dataset.map(tokenize_function, batched=True)
test_dataset = test_dataset.map(tokenize_function, batched=True)

train_dataset = train_dataset.remove_columns(["text"])
test_dataset = test_dataset.remove_columns(["text"])

train_dataset = train_dataset.rename_column("label", "labels")
test_dataset = test_dataset.rename_column("label", "labels")

train_dataset.set_format("torch")
test_dataset.set_format("torch")

Modèle

# Charger le modèle DistilBERT pré-entraîné pour la classification de séquences
# num_labels=2 spécifie que nous avons deux classes (sentiments positif et négatif).

from transformers import DistilBertForSequenceClassification

model = DistilBertForSequenceClassification.from_pretrained(
        'distilbert-base-uncased',
        num_labels=2
    )

On peut voir la structure du modele avec : print(model)

DistilBertForSequenceClassification(
  (distilbert): DistilBertModel(
    (embeddings): Embeddings(
      (word_embeddings): Embedding(30522, 768, padding_idx=0)
      (position_embeddings): Embedding(512, 768)
      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (transformer): Transformer(
      (layer): ModuleList(
        (0-5): 6 x TransformerBlock(
          (attention): DistilBertSdpaAttention(
            (dropout): Dropout(p=0.1, inplace=False)
            (q_lin): Linear(in_features=768, out_features=768, bias=True)
            (k_lin): Linear(in_features=768, out_features=768, bias=True)
            (v_lin): Linear(in_features=768, out_features=768, bias=True)
            (out_lin): Linear(in_features=768, out_features=768, bias=True)
          )
          (sa_layer_norm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
          (ffn): FFN(
            (dropout): Dropout(p=0.1, inplace=False)
            (lin1): Linear(in_features=768, out_features=3072, bias=True)
            (lin2): Linear(in_features=3072, out_features=768, bias=True)
            (activation): GELUActivation()
          )
          (output_layer_norm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
        )
      )
    )
  )
  (pre_classifier): Linear(in_features=768, out_features=768, bias=True)
  (classifier): Linear(in_features=768, out_features=2, bias=True)
  (dropout): Dropout(p=0.2, inplace=False)
)

Le modele est entierement defini dans la repo de huggingface

https://github.com/huggingface/transformers/blob/main/src/transformers/models/distilbert/modeling_distilbert.py

Métriques d’évaluation

from sklearn.metrics import accuracy_score, precision_recall_fscore_support
# Définir les métriques d'évaluation
def compute_metrics(pred):
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='binary')
    acc = accuracy_score(labels, preds)
    return {
        'accuracy': acc,
        'f1': f1,
        'precision': precision,
        'recall': recall
    }

Entraînement

Trainer

# Définir les arguments d'entraînement
# configure le processus d'entraînement, incluant : répertoire de sortie, le nombre d'epochs, bacth size, learning rate.

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=2,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=64,
    warmup_steps=500,
    weight_decay=0.01,

    logging_dir='./logs',
    logging_strategy="steps",   # log based on steps
    logging_steps=1,            # log every step
    log_level="info",           # increase verbosity
    report_to="none",           # disable WandB etc. if enabled by default

    evaluation_strategy="steps",  # optional: evaluate more often
    eval_steps=50,                # evaluate every 50 steps
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=test_dataset,
    compute_metrics=compute_metrics,
)

trainer.train()

Évaluation

# Évaluer le modèle
trainer.evaluate()

TrainingArguments

Une classe dans la librairie transformers qui encapsule une large gamme de paramètres et configurations pour le processus d’entraînement.

Mais les parametres dépendents

Au final, plus compliqué de génerer du code qui tourne et qui soit utilisable en regard des contraintes des ressources que de travailler avec scikit-learn.

TrainingArguments inclut des paramètres contrôlant :

TrainingArguments est typiquement utilisé quand vous utilisez la classe Trainer dans transformers. La classe Trainer est une abstraction de haut niveau qui simplifie la boucle d’entraînement, et elle dépend de TrainingArguments pour configurer le processus d’entraînement.


Implementation sur Google Colab avec T4 free tier

Notebook


Tuning des hyperparamètres

1. Commencer avec les valeurs par défaut

Ajuster un à la fois (ou quelques uns) : Changer un ou un petit groupe d’hyperparamètres en gardant les autres constants. Cela aide à isoler l’effet de chaque paramètre sur la performance du modèle.

2. Tuning du learning rate

3. Tuning de la taille du batch

4. Régularisation (Weight Decay, Dropout)

5. Optimisation automatique des hyperparamètres

6. Monitoring et évaluation

7. Considérer la taille du dataset

En résumé

  1. Commencer avec les valeurs par défaut.
  2. Se concentrer d’abord sur le learning rate, la taille du batch et la régularisation.
  3. Tuner un ou quelques hyperparamètres à la fois.
  4. Utiliser l’optimisation automatique pour un tuning plus avancé.
  5. Monitorer les métriques et utiliser l’early stopping.
  6. Considérer la taille de votre dataset.

Comment accelerer l’entraînement ?

  1. Data Optimization:
    • Dataset Size Reduction: If possible, reduce the size of your dataset. Experiment with using a smaller subset of the data for initial training and hyperparameter tuning. Once you have a good configuration, you can train on the full dataset.
    • Data Type Optimization: Ensure that your dataset uses the most efficient data types. For example, if your labels are 0 and 1, use an int8 or bool data type instead of int64.
  2. Model Selection:
    • Smaller Model: Use a smaller, faster pre-trained model like DistilBERT or MobileBERT instead of larger models like BERT or RoBERTa. Smaller models have fewer parameters, which translates to faster training and inference.
  3. Batch Size Optimization:
    • Maximize Batch Size: Increase the per_device_train_batch_size and per_device_eval_batch_size as much as your Colab’s TP4 memory allows. Experiment to find the largest batch size that doesn’t cause out-of-memory errors. Larger batch sizes can significantly speed up training.
    • Gradient Accumulation: If you can’t fit a large batch size into memory, use gradient_accumulation_steps. This simulates a larger batch size by accumulating gradients over multiple smaller batches before performing a weight update.
  4. Training Optimization:
    • Mixed Precision Training (FP16): Enable mixed-precision training by setting fp16=True in your TrainingArguments. This uses lower-precision floating-point numbers, which can significantly speed up training on TPUs.
    • Gradient Checkpointing: If memory is still a bottleneck, try using gradient checkpointing. This trades off some computation for memory savings by recomputing activations during the backward pass. Look for a gradient_checkpointing option in your model’s configuration or Trainer.
    • Reduce Epochs: Start with a smaller number of training epochs and monitor the validation loss. Stop training early if the model starts to overfit.
    • Learning Rate Tuning: Optimize your learning rate. Use a learning rate finder or experiment with different learning rate schedules. A well-tuned learning rate can lead to faster convergence.
  5. TPU Utilization:
    • Ensure TPU Utilization: Monitor TPU utilization during training. If the TPU is not being fully utilized, it indicates a bottleneck elsewhere in your pipeline (e.g., data loading).
  6. Data Loading Optimization:
    • datasets Caching: The datasets library automatically caches datasets to disk. Make sure you have enough disk space on your Colab instance to store the cached dataset.
    • Efficient Data Loading: Ensure that your data loading pipeline is efficient. Avoid unnecessary data conversions or transformations.
    • Parallel Data Loading: Use the num_workers argument in the DataLoader to load data in parallel.
  7. Code Optimization:
    • Profile Your Code: Use profiling tools to identify any bottlenecks in your code.
    • Optimize Loops: If you have any custom loops in your code, make sure they are optimized for performance.
  8. Regularly Restart Colab:
    • Memory Leaks: Google Colab can sometimes experience memory leaks. Regularly restarting the Colab runtime can help free up memory and improve performance.
  9. Use a Faster Tier (If Possible):
    • Colab Pro/Pro+: If you need even faster training, consider subscribing to Colab Pro or Pro+. These tiers offer access to more powerful GPUs and TPUs, as well as more memory.

In Summary: Focus on reducing model size, maximizing batch size (with gradient accumulation if needed), enabling mixed-precision training, optimizing data loading, and tuning the learning rate.