#
Normalisation et prétokenization

Avant de nous plonger plus profondément dans les trois algorithmes de tokénisation en sous-mots les plus courants utilisés avec les transformers (Byte-Pair Encoding (BPE), WordPiece et Unigram), nous allons d’abord examiner le prétraitement que chaque tokenizer applique au texte. Voici un aperçu de haut niveau des étapes du pipeline de tokenisation :

Avant de diviser un texte en sous-tokens (selon le modèle), le tokenizer effectue deux étapes : la normalisation et la prétokénisation.

##
Normalisation

L’étape de normalisation implique un nettoyage général, comme la suppression des espaces inutiles, la mise en minuscules et/ou la suppression des accents. Si vous êtes familier avec la normalisation Unicode (comme NFC ou NFKC), c’est aussi quelque chose que le tokenizer peut appliquer.

Le `tokenizer` de 🤗 Transformers possède un attribut appelé `backend_tokenizer` qui donne accès au tokenizer sous-jacent de la bibliothèque 🤗 Tokenizers :

    from transformers import AutoTokenizer

    tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
    print(type(tokenizer.backend_tokenizer))

    <class 'tokenizers.Tokenizer'>

L’attribut `normalizer` de l’objet `tokenizer` possède une méthode `normalize_str()` que nous pouvons utiliser pour voir comment la normalisation est effectuée :

    print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))

    'hello how are u?'

Dans cet exemple, puisque nous avons choisi le checkpoint `bert-base-uncased`, la normalisation a mis le texte en minuscule et supprimé les accents.

✏️ Essayez ! Chargez un tokenizer depuis le checkpoint `bert-base-cased` et passez-lui le même exemple. Quelles sont les principales différences que vous pouvez voir entre les versions casée et non casée du tokenizer ?

##
Prétokenization

Comme nous le verrons dans les sections suivantes, un tokenizer ne peut pas être entraîné uniquement sur du texte brut. Au lieu de cela, nous devons d’abord diviser les textes en petites entités, comme des mots. C’est là qu’intervient l’étape de prétokénisation. Comme nous l’avons vu dans le chapitre 2, un tokenizer basé sur les mots peut simplement diviser un texte brut en mots sur les espaces et la ponctuation. Ces mots constitueront les limites des sous-tokens que le tokenizer peut apprendre pendant son entraînement.

Pour voir comment un tokenizer rapide effectue la prétokénisation, nous pouvons utiliser la méthode `pre_tokenize_str()`de l’attribut `pre_tokenizer` de l’objet `tokenizer` :

    tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

    [('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]

Remarquez que le tokenizer garde déjà la trace des offsets, ce qui lui permet de nous donner la correspondance des décalages que nous avons utilisée dans la section précédente. Ici, le tokenizer ignore les deux espaces et les remplace par un seul, mais le décalage saute entre `are` et `you` pour en tenir compte.

Puisque nous utilisons le tokenizer de BERT, la prétokénisation implique la séparation des espaces et de la ponctuation. D’autres tokenizers peuvent avoir des règles différentes pour cette étape. Par exemple, si nous utilisons le tokenizer du GPT-2 :

    tokenizer = AutoTokenizer.from_pretrained("gpt2")
    tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

Il séparera aussi sur les espaces et la ponctuation mais gardera les espaces et les remplacera par un symbole `Ġ`, ce qui lui permettra de récupérer les espaces originaux si nous décodons les tokens :

    [('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
     ('?', (19, 20))]

Notez également que, contrairement au tokenizer de BERT, ce tokenizer n’ignore pas les doubles espaces.

Pour un dernier exemple, regardons le tokenizer du 5, qui est basé sur l’algorithme SentencePiece :

    tokenizer = AutoTokenizer.from_pretrained("t5-small")
    tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are  you?")

    [('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]

Comme le tokenizer du GPT-2, celui-ci garde les espaces et les remplace par un token spécifique (`_`), mais le tokenizer du T5 ne sépare que sur les espaces, pas sur la ponctuation. Notez également qu’il a ajouté un espace par défaut au début de la phrase (avant `Hello`) et il ignore le double espace entre `are` et `you`.

Maintenant que nous avons vu un peu comment les différents tokenizers traitent le texte, nous pouvons commencer à explorer les algorithmes sous-jacents eux-mêmes. Nous commencerons par jeter un coup d’oeil rapide sur le très répandu SentencePiece, puis au cours des trois sections suivantes nous examinerons le fonctionnement des trois principaux algorithmes utilisés pour la tokenisation en sous-mots.

##
SentencePiece

SentencePiece est un algorithme de tokenisation pour le prétraitement du texte que vous pouvez utiliser avec n’importe lequel des modèles que nous verrons dans les trois prochaines sections. Il considère le texte comme une séquence de caractères Unicode et il remplace les espaces par un caractère spécial : `▁`. Utilisé en conjonction avec l’algorithme Unigram (voir la section 7), il ne nécessite même pas d’étape de prétokénisation, ce qui est très utile pour les langues où le caractère espace n’est pas utilisé (comme le chinois ou le japonais).

L’autre caractéristique principale de SentencePiece est le tokenisation réversible : comme il n’y a pas de traitement spécial des espaces, le décodage des tokens se fait simplement en les concaténant et en remplaçant les `_` par des espaces, ce qui donne le texte normalisé. Comme nous l’avons vu précédemment, le tokenizer de BERT supprime les espaces répétitifs, donc sa tokenisation n’est pas réversible.

##
Vue d'ensemble des algorithmes

Dans les sections suivantes, nous allons nous plonger dans les trois principaux algorithmes de tokenisation en sous-mots : BPE (utilisé par GPT-2 et autres), WordPiece (utilisé par exemple par BERT), et Unigram (utilisé par T5 et autres). Avant de commencer, voici un rapide aperçu du fonctionnement de chacun d’entre eux. N’hésitez pas à revenir à ce tableau après avoir lu chacune des sections suivantes si cela n’a pas encore de sens pour vous.

Modèle | BPE | WordPiece | Unigramme
--- | --- | --- | ---
Entraînement | Part d’un petit vocabulaire et apprend des règles pour fusionner les tokens | Part d’un petit vocabulaire et apprend des règles pour fusionner les tokens | Part d’un grand vocabulaire et apprend des règles pour supprimer les tokens
Étape d’entraînement | Fusionne les tokens correspondant à la paire la plus commune | Fusionne les tokens correspondant à la paire ayant le meilleur score basé sur la fréquence de la paire, en privilégiant les paires où chaque token individuel est moins fréquent | Supprime tous les tokens du vocabulaire qui minimiseront la perte calculée sur le corpus entier
Apprend | A fusionner des règles et un vocabulaire | Juste un vocabulaire | Un vocabulaire avec un score pour chaque token
Encodage | Découpe un mot en caractères et applique les fusions apprises pendant l’entraînement | Trouve le plus long sous-mot depuis le début qui est dans le vocabulaire puis fait de même pour le reste du mot | Trouve la division la plus probable en tokens, en utilisant les scores appris pendant l’entraînement

Maintenant, plongeons dans le BPE !