赞
踩
【自己创建分词器】WordPiece tokenizer
【自己创建分词器】BPE tokenizer
【自己创建分词器】Unigram tokenizer
分词包括以下几个步骤:
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"]
函数get_training_corpus()是一个生成器,它会产生批次包含1000个文本,后面将使用这些文本来训练分词器。
分词器还可以直接在文本文件上进行训练。以下是如何生成一个包含所有来自WikiText-2的文本/输入的文本文件,我们可以在本地使用:
with open("wikitext-2.txt", "w", encoding="utf-8") as f:
for i in range(len(dataset)):
f.write(dataset[i]["text"] + "\n")
要使用Tokenizers库构建一个分词器,首先我们实例化一个Tokenizer对象,然后将其normalizer、pre_tokenizer、post_processor和decoder属性设置为所需的值。
在这个示例中,我们将创建一个带有WordPiece模型的分词器:
from tokenizers import (
decoders,
models,
normalizers,
pre_tokenizers,
processors,
trainers,
Tokenizer,
)
现在让我们构建一个XLNet标记器。与之前的标记器一样,我们首先通过使用Unigram模型来初始化一个Tokenizer:
tokenizer = Tokenizer(models.Unigram())
同样地,如果我们有词汇表,我们可以使用它来初始化该模型。
对于标准化,XLNet使用了一些替换(这些替换来自SentencePiece):
from tokenizers import Regex
tokenizer.normalizer = normalizers.Sequence(
[
normalizers.Replace("``", '"'),
normalizers.Replace("''", '"'),
normalizers.NFKD(),
normalizers.StripAccents(),
normalizers.Replace(Regex(" {2,}"), " "),
]
)
这将"``“替换为’”‘;将"’‘"替换为’"',并将两个或多个空格的连续序列替换为单个空格,同时删除要进行标记的文本中的重音符号。
任何SentencePiece分词器使用的预标记器都是Metaspace。
tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
我们可以像前面一样查看示例文本的预标记化:
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))]
接下来是需要训练的模型。 XLNet 有很多特殊的标记:
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)
不要忘记UnigramTrainer的一个非常重要的参数是unk_token。我们还可以传递其他特定于Unigram算法的参数,例如在每个步骤中删除标记时的shrinking_factor(默认为0.75)或max_piece_length以指定给定标记的最大长度(默认为16)。
这个标记器也可以在文本文件上进行训练:
tokenizer.model = models.Unigram()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
让我们看一下示例文本的标记化:
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
输出:
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
XLNet的一个特点是它将标记放在句子的末尾,类型ID为2(以区分它与其他标记)。结果是左侧填充。我们可以使用一个类似BERT的模板来处理所有特殊标记和标记类型ID,但首先我们需要获取和标记的ID。
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
模板如下:
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)],
)
我们可以通过编码一对句子来测试它的工作原理:
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]
最后,我们添加一个Metaspace解码器:
tokenizer.decoder = decoders.Metaspace()
我们完成了这个分词器!我们可以像之前一样保存分词器,并且如果想要在Transformers中使用它,可以将其包装在PreTrainedTokenizerFast或XLNetTokenizerFast中。需要注意的一点是,在使用PreTrainedTokenizerFast时,除了特殊标记外,我们还需要告诉Transformers库在左侧填充:
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",
)
from transformers import XLNetTokenizerFast
wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
其他部分,比如保存与加载等,和之前WordPiece tokenizer是一样的,本文没有赘述。
参考:
https://huggingface.co/learn/nlp-course/chapter6/8?fw=pt#building-a-unigram-tokenizer-from-scratch
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。