赞
踩
【自己创建分词器】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,
)
现在让我们构建一个GPT-2分词器。与构建BERT分词器类似,我们首先使用BPE模型初始化一个分词器:
tokenizer = Tokenizer(models.BPE())
就像为BERT一样,如果我们有一个词汇表,也可以使用它来初始化这个模型(在这种情况下,我们需要传递词汇表和合并规则),但是由于我们将从头开始训练,所以不需要这样做。我们也不需要指定unk_token,因为GPT-2使用的是字节级别的BPE,不需要它。
GPT-2不使用规范化器,所以我们跳过这一步,直接进行预标记:
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
我们在这里添加给ByteLevel的选项是不在句子开头添加空格(否则默认会添加)。我们可以看一下类似之前的示例文本的预标记:
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))]
接下来是model,需要进行训练。对于GPT-2,唯一的特殊标记是结束文本标记:
trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
与WordPieceTrainer一样,除了vocab_size和special_tokens,我们还可以指定min_frequency(如果需要的话),或者如果我们有一个词尾标记(例如),可以使用end_of_word_suffix进行设置。
这个分词器也可以在文本文件上进行训练:
tokenizer.model = models.BPE()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)
让我们看一下示例文本的标记化:
encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
输出:
['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
我们对 GPT-2标记发生器应用字节级后处理,如下所示:
tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
trim_offsets = False选项告诉后处理器,我们应该保留以’Ġ’开头的标记的偏移量,这样偏移量的起始点将指向单词之前的空格,而不是单词的第一个字符(因为空格在技术上是标记的一部分)。让我们看一下刚刚编码的文本的结果,其中’Ġtest’是索引为4的标记:
sentence = "Let's test this tokenizer."
encoding = tokenizer.encode(sentence)
start, end = encoding.offsets[4]
sentence[start:end]
输出:
' test'
最后,我们添加一个字节级解码器:
tokenizer.decoder = decoders.ByteLevel()
测试是否正常工作:
tokenizer.decode(encoding.ids)
输出:
"Let's test this tokenizer."
现在我们完成了,我们可以像之前一样保存标记器,并将其包装在PreTrainedTokenizerFast或GPT2TokenizerFast中,如果我们想在Transformers中使用它:
from transformers import PreTrainedTokenizerFast
wrapped_tokenizer = PreTrainedTokenizerFast(
tokenizer_object=tokenizer,
bos_token="<|endoftext|>",
eos_token="<|endoftext|>",
)
from transformers import GPT2TokenizerFast
wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
其他部分,比如保存与加载等,和之前WordPiece tokenizer是一样的,本文没有赘述。
参考:
https://huggingface.co/learn/nlp-course/chapter6/8?fw=pt#building-a-bpe-tokenizer-from-scratch
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。