Construction d’un <i> tokenizer </i> , 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 deNormalizer
que vous pouvez utiliser (liste complète ici),pre_tokenizers
contient tous les types dePreTokenizer
que vous pouvez utiliser (liste complète ici),models
contient les différents types deModel
que vous pouvez utiliser, commeBPE
,WordPiece
, etUnigram
(liste complète ici),trainers
contient tous les différents types deTrainer
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 dePostProcessor
que vous pouvez utiliser (liste complète ici),decoders
contient les différents types deDecoder
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 <i> tokenizer WordPiece </i> à 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 <i> tokenizer </i> 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 <i> tokenizer Unigram </i> à 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.