#
Construction d'un tokenizer, bloc par bloc

Comme nous l’avons vu dans les sections précédentes, la tokenisation comprend plusieurs étapes :

- normalisation (tout nettoyage du texte jugé nécessaire, comme la suppression des espaces ou des accents, la normalisation Unicode, etc.),
- prétokénisation (division de l’entrée en mots),
- passage de l’entrée dans le modèle (utilisation des mots prétokénisés pour produire une séquence de tokens),
- post-traitement (ajout des tokens spéciaux du tokenizer, génération du masque d’attention et des identifiants du type de token).

Pour mémoire, voici un autre aperçu du processus global :

La bibliothèque 🤗 Tokenizers a été construite pour fournir plusieurs options pour chacune de ces étapes. Vous pouvez les mélanger et assortir ensemble. Dans cette section, nous verrons comment nous pouvons construire un tokenizer à partir de zéro, par opposition à entraîner un nouveau tokenizer à partir d’un ancien, comme nous l’avons fait dans section 2. Vous serez alors en mesure de construire n’importe quel type de tokenizer auquel vous pouvez penser !

Plus précisément, la bibliothèque est construite autour d’une classe centrale `Tokenizer` avec les blocs de construction regroupés en sous-modules :

- `normalizers` contient tous les types de `Normalizer` que vous pouvez utiliser (liste complète ici),
- `pre_tokenizers` contient tous les types de `PreTokenizer` que vous pouvez utiliser (liste complète ici),
- `models` contient les différents types de `Model` que vous pouvez utiliser, comme `BPE`, `WordPiece`, et `Unigram` (liste complète ici),
- `trainers` contient tous les différents types de `Trainer` que vous pouvez utiliser pour entraîner votre modèle sur un corpus (un par type de modèle ; liste complète ici),
- `post_processors` contient les différents types de `PostProcessor` que vous pouvez utiliser (liste complète ici),
- `decoders` contient les différents types de `Decoder` que vous pouvez utiliser pour décoder les sorties de tokenization (liste complète ici).

Vous pouvez trouver la liste complète des blocs de construction ici.

##
Acquisition d'un corpus

Pour entraîner notre nouveau tokenizer, nous utiliserons un petit corpus de texte (pour que les exemples soient rapides). Les étapes pour acquérir ce corpus sont similaires à celles que nous avons suivies au début du chapitre, mais cette fois nous utiliserons le jeu de données WikiText-2 :

    from datasets import load_dataset

    dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")

    def get_training_corpus():
        for i in range(0, len(dataset), 1000):
            yield dataset[i : i + 1000]["text"]

La fonction `get_training_corpus()` est un générateur qui donne des batchs de 1 000 textes, que nous utiliserons pour entraîner le tokenizer.

🤗 Tokenizers peut aussi être entraîné directement sur des fichiers texte. Voici comment nous pouvons générer un fichier texte contenant tous les textes de WikiText-2 que nous pourrons ensuite utilisé en local :

    with open("wikitext-2.txt", "w", encoding="utf-8") as f:
        for i in range(len(dataset)):
            f.write(dataset[i]["text"] + "\n")

Ensuite, nous vous montrerons comment construire vos propres tokenizers pour BERT, GPT-2 et XLNet, bloc par bloc. Cela vous donnera un exemple de chacun des trois principaux algorithmes de tokenisation : WordPiece, BPE et Unigram. Commençons par BERT !

##
Construire un tokenizer WordPiece à partir de zéro

Pour construire un tokenizer avec la bibliothèque 🤗 Tokenizers, nous commençons par instancier un objet `Tokenizer` avec un `model`. Puis nous définissons ses attributs `normalizer`, `pre_tokenizer`, `post_processor` et `decoder` aux valeurs que nous voulons.

Pour cet exemple, nous allons créer un `Tokenizer` avec un modèle WordPiece :

    from tokenizers import (
        decoders,
        models,
        normalizers,
        pre_tokenizers,
        processors,
        trainers,
        Tokenizer,
    )

    tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))

Nous devons spécifier le `unk_token` pour que le modèle sache quoi retourner lorsqu’il rencontre des caractères qu’il n’a pas vu auparavant. D’autres arguments que nous pouvons définir ici incluent le `vocab` de notre modèle (nous allons entraîner le modèle, donc nous n’avons pas besoin de le définir) et `max_input_chars_per_word`, qui spécifie une longueur maximale pour chaque mot (les mots plus longs que la valeur passée seront séparés).

La première étape de la tokénisation est la normalisation. Puisque BERT est largement utilisé, une fonction `BertNormalizer` a été créée avec les options classiques que nous pouvons définir pour BERT : `lowercase` pour mettre le texte en minuscule, `strip_accents` qui enlève les accents, `clean_text` pour enlever tous les caractères de contrôle et fusionner des espaces répétés par un seul, et `handle_chinese_chars` qui place des espaces autour des caractères chinois. Pour reproduire le tokenizer `bert-base-uncased`, nous pouvons simplement définir ce normalizer :

    tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)

Cependant, généralement, lorsque vous construisez un nouveau tokenizer, vous n’avez pas accès à un normaliseur aussi pratique déjà implémenté dans la bibliothèque 🤗 Tokenizers. Donc voyons comment créer le normaliseur de BERT manuellement. La bibliothèque fournit un normaliseur `Lowercase` et un normaliseur `StripAccents`. Il est possible de composer plusieurs normaliseurs en utilisant une `Sequence` :

    tokenizer.normalizer = normalizers.Sequence(
        [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
    )

Nous utilisons également un normaliseur Unicode `NFD`, car sinon `StripAccents` ne reconnaîtra pas correctement les caractères accentués et ne les supprimera donc pas.

Comme nous l’avons vu précédemment, nous pouvons utiliser la méthode `normalize_str()` du `normalizer` pour vérifier les effets qu’il a sur un texte donné :

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

    hello how are u?

Pour aller plus loin Si vous testez les deux versions des normaliseurs précédents sur une chaîne contenant le caractère unicode `u"\u0085"` vous remarquerez sûrement qu’ils ne sont pas exactement équivalents. Pour ne pas trop compliquer la version avec `normalizers.Sequence`, nous n’avons pas inclus les Regex que le `BertNormalizer` requiert quand l’argument `clean_text` est mis à `True` ce qui est le comportement par défaut. Mais ne vous inquiétez pas : il est possible d’obtenir exactement la même normalisation sans utiliser le très pratique `BertNormalizer` en ajoutant deux `normalizers.Replace` à la séquence de normalisation.

L’étape suivante est la prétokenisation. Encore une fois, il y a un `BertPreTokenizer` préconstruit que nous pouvons utiliser :

    tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()

Ou nous pouvons le construire à partir de zéro :

    tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()

Notez que le `Whitespace` divise sur les espaces et tous les caractères qui ne sont pas des lettres, des chiffres ou le caractère de soulignement. Donc techniquement il divise sur les espaces et la ponctuation :

    tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")

    [('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
     ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]

Si vous voulez seulement séparer sur les espaces, vous devez utiliser `WhitespaceSplit` à la place :

    pre_tokenizer = pre_tokenizers.WhitespaceSplit()
    pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")

    [("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]

Comme pour les normaliseurs, vous pouvez utiliser une `Sequence` pour composer plusieurs prétokenizers :

    pre_tokenizer = pre_tokenizers.Sequence(
        [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
    )
    pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")

    [('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
     ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]

L’étape suivante dans le pipeline de tokénisation est de faire passer les entrées par le modèle. Nous avons déjà spécifié notre modèle dans l’initialisation, mais nous devons encore l’entraîner, ce qui nécessitera un `WordPieceTrainer`. La principale chose à retenir lors de l’instanciation d’un entraîneur dans 🤗 Tokenizers est que vous devez lui passer tous les tokens spéciaux que vous avez l’intention d’utiliser. Sinon il ne les ajoutera pas au vocabulaire puisqu’ils ne sont pas dans le corpus d’entraînement :

    special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
    trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)

En plus de spécifier la `vocab_size` et les `special_tokens`, nous pouvons définir la `min_frequency` (le nombre de fois qu’un token doit apparaître pour être inclus dans le vocabulaire) ou changer le `continuing_subword_prefix` (si nous voulons utiliser quelque chose de différent de `##`).

Pour entraîner notre modèle en utilisant l’itérateur que nous avons défini plus tôt, il suffit d’exécuter cette commande :

    tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)

Nous pouvons également utiliser des fichiers texte pour entraîner notre tokenizer qui ressemblerait alors à ceci (nous réinitialisons le modèle avec un `WordPiece` vide au préalable) :

    tokenizer.model = models.WordPiece(unk_token="[UNK]")
    tokenizer.train(["wikitext-2.txt"], trainer=trainer)

Dans les deux cas, nous pouvons ensuite tester le tokenizer sur un texte en appelant la méthode `encode()` :

    encoding = tokenizer.encode("Let's test this tokenizer.")
    print(encoding.tokens)

    ['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']

L’encodage obtenu est un `Encoding` contenant toutes les sorties nécessaires du tokenizer dans ses différents attributs : `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask` et `overflowing`.

La dernière étape du pipeline de tokénisation est le post-traitement. Nous devons ajouter le token `[CLS]` au début et le token `[SEP]` à la fin (ou après chaque phrase si nous avons une paire de phrases). Nous utiliserons `TemplateProcessor` pour cela, mais d’abord nous devons connaître les identifiants des tokens `[CLS]` et `[SEP]` dans le vocabulaire :

    cls_token_id = tokenizer.token_to_id("[CLS]")
    sep_token_id = tokenizer.token_to_id("[SEP]")
    print(cls_token_id, sep_token_id)

    (2, 3)

Pour écrire le gabarit pour `TemplateProcessor`, nous devons spécifier comment traiter une seule phrase et une paire de phrases. Pour les deux, nous écrivons les tokens spéciaux que nous voulons utiliser. La première (ou unique) phrase est représentée par `$A`, alors que la deuxième phrase (si on code une paire) est représentée par `$B`. Pour chacun de ces éléments (tokens spéciaux et phrases), nous spécifions également l’identifiant du token correspondant après un deux-points.

Le gabarit classique de BERT est donc défini comme suit :

    tokenizer.post_processor = processors.TemplateProcessing(
        single=f"[CLS]:0 $A:0 [SEP]:0",
        pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
        special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
    )

Notez que nous devons transmettre les identifiants des tokens spéciaux afin que le tokenizer puisse les convertir correctement.

Une fois cela ajouté, revenons à notre exemple précédent donnera :

    encoding = tokenizer.encode("Let's test this tokenizer.")
    print(encoding.tokens)

    ['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']

Et sur une paire de phrases, on obtient le bon résultat :

    encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
    print(encoding.tokens)
    print(encoding.type_ids)

    ['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]

Nous avons presque fini de construire ce tokenizer à partir de zéro. La dernière étape consiste à inclure un décodeur :

    tokenizer.decoder = decoders.WordPiece(prefix="##")

Testons-le sur notre précédent `encoding` :

    tokenizer.decode(encoding.ids)

    "let's test this tokenizer... on a pair of sentences." # Testons ce tokenizer... sur une paire de phrases.

Génial ! Nous pouvons enregistrer notre tokenizer dans un seul fichier JSON comme ceci :

    tokenizer.save("tokenizer.json")

Nous pouvons alors recharger ce fichier dans un objet `Tokenizer` avec la méthode `from_file()` :

    new_tokenizer = Tokenizer.from_file("tokenizer.json")

Pour utiliser ce tokenizer dans 🤗 Transformers, nous devons l’envelopper dans un `PreTrainedTokenizerFast`. Nous pouvons soit utiliser la classe générique, soit, si notre tokenizer correspond à un modèle existant, utiliser cette classe (ici, `BertTokenizerFast`). Si vous appliquez cette logique pour construire un tout nouveau tokenizer, vous devrez utiliser la première option.

Pour envelopper le tokenizer dans un `PreTrainedTokenizerFast`, nous pouvons soit passer le tokenizer que nous avons construit comme un `tokenizer_object`, soit passer le fichier de tokenizer que nous avons sauvegardé comme `tokenizer_file`. Ce qu’il faut retenir, c’est que nous devons définir manuellement tous les tokens spéciaux car cette classe ne peut pas déduire de l’objet `tokenizer` quel token est le token de masque, quel est le token`[CLS]`, etc :

    from transformers import PreTrainedTokenizerFast

    wrapped_tokenizer = PreTrainedTokenizerFast(
        tokenizer_object=tokenizer,
        # tokenizer_file="tokenizer.json", # Vous pouvez charger à partir du fichier du tokenizer, alternativement
        unk_token="[UNK]",
        pad_token="[PAD]",
        cls_token="[CLS]",
        sep_token="[SEP]",
        mask_token="[MASK]",
    )

Si vous utilisez une classe de tokenizer spécifique (comme `BertTokenizerFast`), vous aurez seulement besoin de spécifier les tokens spéciaux qui sont différents de ceux par défaut (ici, aucun) :

    from transformers import BertTokenizerFast

    wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)

Vous pouvez ensuite utiliser ce tokenizer comme n’importe quel autre tokenizer de 🤗 Transformers. Vous pouvez le sauvegarder avec la méthode `save_pretrained()` ou le télécharger sur le Hub avec la méthode `push_to_hub()`.

Maintenant que nous avons vu comment construire un tokenizer WordPiece, faisons de même pour un tokenizer BPE. Nous irons un peu plus vite puisque vous connaissez toutes les étapes. Nous ne soulignerons que les différences.

##
Construire un tokenizer BPE à partir de zéro

Construisons maintenant un tokenizer BPE. Comme pour le tokenizer BERT, nous commençons par initialiser un `Tokenizer` avec un modèle BPE :

    tokenizer = Tokenizer(models.BPE())

Comme pour BERT, nous pourrions initialiser ce modèle avec un vocabulaire si nous en avions un (nous aurions besoin de passer le `vocab` et le `merges` dans ce cas), mais puisque nous allons nous entraîner à partir de zéro, nous n’avons pas besoin de le faire. Nous n’avons pas non plus besoin de spécifier un `unk_token` parce que le GPT-2 utilise un BPE au niveau de l’octet.

GPT-2 n’utilise pas de normaliseur, donc nous sautons cette étape et allons directement à la prétokénisation :

    tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)

L’option que nous avons ajoutée à `ByteLevel` ici est de ne pas ajouter d’espace en début de phrase (ce qui est le cas par défaut). Nous pouvons jeter un coup d’oeil à la prétokénisation d’un texte d’exemple comme avant :

    tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")

    [('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
     ('tokenization', (15, 27)), ('!', (27, 28))]

Vient ensuite le modèle, qui doit être entraîné. Pour le GPT-2, le seul token spécial est le token de fin de texte :

    trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
    tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)

Comme avec le `WordPieceTrainer`, ainsi que le `vocab_size` et le `special_tokens`, nous pouvons spécifier la `min_frequency` si nous le voulons, ou si nous avons un suffixe de fin de mot (comme `</w>`), nous pouvons le définir avec `end_of_word_suffix`.

Ce tokenizer peut aussi être entraîné sur des fichiers texte :

    tokenizer.model = models.BPE()
    tokenizer.train(["wikitext-2.txt"], trainer=trainer)

Regardons la tokenisation d’un exemple de texte :

    encoding = tokenizer.encode("Let's test this tokenizer.")
    print(encoding.tokens)

    ['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']

Nous appliquons le post-traitement au niveau de l’octet pour le tokenizer du GPT-2 comme suit :

    tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)

L’option `trim_offsets = False` indique au post-processeur que nous devons laisser les offsets des tokens qui commencent par ‘Ġ’ tels quels : de cette façon, le début des offsets pointera sur l’espace avant le mot, et non sur le premier caractère du mot (puisque l’espace fait techniquement partie du token). Regardons le résultat avec le texte que nous venons de coder, où `'Ġtest'` est le token à l’index 4 :

    sentence = "Let's test this tokenizer."
    encoding = tokenizer.encode(sentence)
    start, end = encoding.offsets[4]
    sentence[start:end]

    ' test'

Enfin, nous ajoutons un décodeur au niveau de l’octet :

    tokenizer.decoder = decoders.ByteLevel()

et nous pouvons vérifier qu’il fonctionne correctement :

    tokenizer.decode(encoding.ids)

    "Let's test this tokenizer." # Testons ce tokenizer

Super ! Maintenant que nous avons terminé, nous pouvons sauvegarder le tokenizer comme avant, et l’envelopper dans un `PreTrainedTokenizerFast` ou un `GPT2TokenizerFast` si nous voulons l’utiliser dans 🤗 Transformers :

    from transformers import PreTrainedTokenizerFast

    wrapped_tokenizer = PreTrainedTokenizerFast(
        tokenizer_object=tokenizer,
        bos_token="<|endoftext|>",
        eos_token="<|endoftext|>",
    )

ou :

    from transformers import GPT2TokenizerFast

    wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)

Comme dernier exemple, nous allons vous montrer comment construire un tokenizer Unigram à partir de zéro.

##
Construire un tokenizer Unigram à partir de zéro

Construisons maintenant un tokenizer XLNet. Comme pour les tokenizers précédents, nous commençons par initialiser un `Tokenizer` avec un modèle Unigram :

    tokenizer = Tokenizer(models.Unigram())

Encore une fois, nous pourrions initialiser ce modèle avec un vocabulaire si nous en avions un.

Pour la normalisation, XLNet utilise quelques remplacements (qui proviennent de SentencePiece) :

    from tokenizers import Regex

    tokenizer.normalizer = normalizers.Sequence(
        [
            normalizers.Replace("``", '"'),
            normalizers.Replace("''", '"'),
            normalizers.NFKD(),
            normalizers.StripAccents(),
            normalizers.Replace(Regex(" {2,}"), " "),
        ]
    )

Il remplace `“` et `”` par `”` et toute séquence de deux espaces ou plus par un seul espace, de plus il supprime les accents.

Le prétokenizer à utiliser pour tout tokenizer SentencePiece est `Metaspace` :

    tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()

Nous pouvons jeter un coup d’oeil à la prétokénisation sur le même exemple de texte que précédemment :

    tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")

    [("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]

Vient ensuite le modèle, qui doit être entraîné. XLNet possède un certain nombre de tokens spéciaux :

    special_tokens = ["<cls>", "<sep>", "<unk>", "<pad>", "<mask>", "<s>", "</s>"]
    trainer = trainers.UnigramTrainer(
        vocab_size=25000, special_tokens=special_tokens, unk_token="<unk>"
    )
    tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)

Un argument très important à ne pas oublier pour le `UnigramTrainer` est le `unk_token`. Nous pouvons aussi passer d’autres arguments spécifiques à l’algorithme Unigram, comme le `shrinking_factor` pour chaque étape où nous enlevons des tokens (par défaut 0.75) ou le `max_piece_length` pour spécifier la longueur maximale d’un token donné (par défaut 16).

Ce tokenizer peut aussi être entraîné sur des fichiers texte :

    tokenizer.model = models.Unigram()
    tokenizer.train(["wikitext-2.txt"], trainer=trainer)

Regardons la tokenisation de notre exemple :

    encoding = tokenizer.encode("Let's test this tokenizer.")
    print(encoding.tokens)

    ['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']

Une particularité de XLNet est qu’il place le token `<cls>` à la fin de la phrase, avec un identifiant de 2 (pour le distinguer des autres tokens). Le résultat est un remplissage à gauche. Nous pouvons traiter tous les tokens spéciaux et les types d’identifiant de token avec un modèle, comme pour BERT. Mais d’abord nous devons obtenir les identifiants des tokens `<cls>` et `<sep>` :

    cls_token_id = tokenizer.token_to_id("<cls>")
    sep_token_id = tokenizer.token_to_id("<sep>")
    print(cls_token_id, sep_token_id)

    0 1

Le modèle ressemble à ceci :

    tokenizer.post_processor = processors.TemplateProcessing(
        single="$A:0 <sep>:0 <cls>:2",
        pair="$A:0 <sep>:0 $B:1 <sep>:1 <cls>:2",
        special_tokens=[("<sep>", sep_token_id), ("<cls>", cls_token_id)],
    )

Et nous pouvons tester son fonctionnement en codant une paire de phrases :

    encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
    print(encoding.tokens)
    print(encoding.type_ids)

    ['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '<sep>', '▁', 'on', '▁', 'a', '▁pair',
      '▁of', '▁sentence', 's', '!', '<sep>', '<cls>']
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]

Enfin, nous ajoutons un décodeur `Metaspace` :

    tokenizer.decoder = decoders.Metaspace()

et on en a fini avec ce tokenizer ! On peut le sauvegarder et l’envelopper dans un `PreTrainedTokenizerFast` ou `XLNetTokenizerFast` si on veut l’utiliser dans 🤗 Transformers. Une chose à noter lors de l’utilisation de `PreTrainedTokenizerFast` est qu’en plus des tokens spéciaux, nous devons dire à la bibliothèque 🤗 Transformers de rembourrer à gauche :

    from transformers import PreTrainedTokenizerFast

    wrapped_tokenizer = PreTrainedTokenizerFast(
        tokenizer_object=tokenizer,
        bos_token="<s>",
        eos_token="</s>",
        unk_token="<unk>",
        pad_token="<pad>",
        cls_token="<cls>",
        sep_token="<sep>",
        mask_token="<mask>",
        padding_side="left",
    )

Ou alternativement :

    from transformers import XLNetTokenizerFast

    wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)

Maintenant que vous avez vu comment les différentes briques sont utilisées pour construire des tokenizers existants, vous devriez être capable d’écrire n’importe quel tokenizer que vous voulez avec la bibliothèque 🤗 Tokenizers et pouvoir l’utiliser dans 🤗 Transformers.