Transformers documentation

Perplexité des modèles de longueur fixe

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Perplexité des modèles de longueur fixe

La perplexité (PPL) est l'une des métriques les plus courantes pour évaluer les modèles de langage. Avant d'entrer dans le détail, il convient de noter que cette métrique s'applique spécifiquement aux modèles de langage classiques (parfois appelés modèles de langage autorégressifs ou causaux) et qu'elle n'est pas bien définie pour les modèles de langage masqués comme BERT.

La perplexité est définie comme la moyenne exponentielle de la négative log-vraisemblance d'une séquence. Si nous avons une séquence tokenisée $X = (x_0, x_1, \dots, x_t)$, alors la perplexité de XX est, PPL(X)=exp⁡{−1t∑itlog⁡pθ(xi∣x<i)} \text{PPL}(X) = \exp\left\{ -\frac{1}{t}\sum_i^t \log p_\theta (x_i|x_{<i}) \right\}

où $\log p\theta (x_i|x{<i})$ est la log-vraisemblance du ième jeton conditionné sur les jetons précédents $x_{<i}$ selon notre modèle. Intuitivement, cela peut être considéré comme une évaluation de la capacité du modèle à prédire uniformément parmi l’ensemble des jetons spécifiés dans un corpus. Il est important de noter que la procédure de tokenisation a un impact direct sur la perplexité d’un modèle, ce qui doit toujours être pris en compte lors de la comparaison de différents modèles.

Cela équivaut également à l’exponentiation de l’entropie croisée entre les données et les prédictions du modèle. Pour mieux comprendre l’intuition derrière la perplexité et sa relation avec les Bits Par Caractère (BPC) et la compression de données, consultez ce fabuleux article de blog sur The Gradient.

Calculer le PPL avec des modèles de longueur fixe

Si nous n’étions pas limités par la taille du contexte d’un modèle, nous évaluerions la perplexité du modèle en factorisant de manière autorégressive une séquence et en conditionnant sur toute la sous-séquence précédente à chaque étape, comme indiqué ci-dessous.

Full decomposition of a sequence with unlimited context length

Lorsque l'on travaille avec des modèles approximatifs, nous avons généralement une contrainte sur le nombre de jetons que le modèle peut traiter. La plus grande version de GPT-2, par exemple, a une longueur fixe de 1024 jetons, nous ne pouvons donc pas calculer $p\theta(x_t|x{<t})$ directement lorsque tt est supérieur à 1024.

Plutôt, la séquence est généralement découpée en sous-séquences égales à la taille d'entrée maximale du modèle. Si la taille d'entrée maximale d'un modèle est $k$, nous approchons alors la probabilité d'un jeton xtx_t en ne conditionnant que sur les k−1k-1 jetons qui le précèdent plutôt que sur l'ensemble du contexte. Lors de l'évaluation de la perplexité d'un modèle sur une séquence, une approche tentante mais sous-optimale consiste à découper la séquence en morceaux disjoints et à additionner les log-vraisemblances décomposées de chaque segment indépendamment.

Suboptimal PPL not taking advantage of full available context

Ce calcul est rapide, car la perplexité de chaque segment peut être obtenue en un seul passage avant, mais il constitue une mauvaise approximation de la perplexité entièrement factorisée et produira généralement une PPL plus élevée (pire), car le modèle disposera de moins de contexte lors de la plupart des étapes de prédiction.

À la place, le PPL des modèles de longueur fixe doit être évalué avec une stratégie de fenêtre glissante. Il s’agit de faire glisser la fenêtre de contexte de manière répétée afin que le modèle dispose d’un contexte plus large lors de chaque prédiction.

Sliding window PPL taking advantage of all available context

Il s’agit d’une approximation plus proche de la vraie décomposition de la probabilité de la séquence, et elle donnera généralement un score plus favorable. L’inconvénient est qu’elle nécessite un passage avant séparé pour chaque jeton du corpus. Un compromis pratique judicieux consiste à utiliser une fenêtre glissante à pas, en déplaçant le contexte par des pas plus larges plutôt que de faire glisser d’un seul jeton à la fois. Cela permet aux calculs de progresser beaucoup plus rapidement tout en offrant au modèle un large contexte pour effectuer des prédictions à chaque étape.

Exemple : calcul de la perplexité avec GPT-2 dans 🤗 Transformers

Démontrons ce processus avec GPT-2.

from transformers import GPT2LMHeadModel, GPT2TokenizerFast
from accelerate import Accelerator

device = Accelerator().device
model_id = "openai-community/gpt2-large"
model = GPT2LMHeadModel.from_pretrained(model_id).to(device)
tokenizer = GPT2TokenizerFast.from_pretrained(model_id)

Nous chargerons le jeu de données WikiText-2 et évaluerons la perplexité à l’aide de plusieurs stratégies de fenêtre glissante différentes. Étant donné que ce jeu de données est petit et que nous n’effectuons qu’un seul passage avant sur l’ensemble, nous pouvons charger et encoder l’intégralité du jeu de données en mémoire.

from datasets import load_dataset

test = load_dataset("Salesforce/wikitext", "wikitext-2-raw-v1", split="test")
encodings = tokenizer("\n\n".join(test["text"]), return_tensors="pt")

Avec 🤗 Transformers, il suffit de passer input_ids en tant que labels à notre modèle, et la moyenne de la négative log-vraisemblance pour chaque jeton est renvoyée en tant que perte. Avec notre approche par fenêtre glissante, cependant, il y a un chevauchement des jetons que nous passons au modèle à chaque itération. Nous ne voulons pas que la log-vraisemblance des jetons que nous traitons simplement comme contexte soit incluse dans notre perte, nous pouvons donc définir ces cibles sur -100 afin qu’elles soient ignorées. L’exemple suivant montre comment faire avec un pas de 512. Cela signifie que le modèle disposera d’au moins 512 jetons de contexte lors du calcul de la vraisemblance conditionnelle de n’importe quel jeton (à condition qu’il y ait 512 jetons précédents disponibles pour conditionner).

import torch
from tqdm import tqdm

max_length = model.config.n_positions
stride = 512
seq_len = encodings.input_ids.size(1)

nll_sum = 0.0
n_tokens = 0
prev_end_loc = 0
for begin_loc in tqdm(range(0, seq_len, stride)):
    end_loc = min(begin_loc + max_length, seq_len)
    trg_len = end_loc - prev_end_loc  # may be different from stride on last loop
    input_ids = encodings.input_ids[:, begin_loc:end_loc].to(device)
    target_ids = input_ids.clone()
    target_ids[:, :-trg_len] = -100

    with torch.no_grad():
        outputs = model(input_ids, labels=target_ids)

        # loss is calculated using CrossEntropyLoss which averages over valid labels
        # N.B. the model only calculates loss over trg_len - 1 labels, because it internally shifts the labels
        # to the left by 1.
        neg_log_likelihood = outputs.loss

    # Accumulate the total negative log-likelihood and the total number of tokens
    num_valid_tokens = (target_ids != -100).sum().item()  # number of valid tokens in target_ids
    batch_size = target_ids.size(0)
    num_loss_tokens = num_valid_tokens - batch_size  # subtract batch_size due to internal label shift
    nll_sum += neg_log_likelihood * num_loss_tokens
    n_tokens += num_loss_tokens

    prev_end_loc = end_loc
    if end_loc == seq_len:
        break

avg_nll = nll_sum / n_tokens  # average negative log-likelihood per token
ppl = torch.exp(avg_nll)

L’exécution de cette procédure avec une longueur de pas égale à la longueur maximale de l’entrée est équivalente à la stratégie non optimale, sans fenêtre glissante, que nous avons discutée ci-dessus. Plus le pas est petit, plus le modèle dispose de contexte pour effectuer chaque prédiction, et meilleure sera généralement la perplexité rapportée.

Lorsque nous exécutons le code ci-dessus avec stride = 1024, c’est-à-dire sans chevauchement, le PPL résultant est de 19.44, ce qui est à peu près identique à la valeur de 19.93 rapportée dans l’article GPT-2. En utilisant stride = 512 et en appliquant ainsi notre stratégie de fenêtre glissante, cette valeur baisse à 16.44. Il s’agit non seulement d’un score plus favorable, mais il est calculé d’une manière plus proche de la véritable décomposition autorégressive de la vraisemblance d’une séquence.

Update on GitHub