【自己创建分词器tokenizer】(2)——BPE tokenizer

【自己创建分词器】WordPiece tokenizer
【自己创建分词器】BPE tokenizer
【自己创建分词器】Unigram tokenizer

1 整体步骤

分词包括以下几个步骤:

  1. 标准化(Normalization,对文本进行必要的清理,例如去除空格、重音、Unicode标准化等)。
  2. 预分词(Pre-tokenization,将输入拆分为单词)。
  3. 将输入传递给模型(Model,使用预分词的单词生成令牌序列)。
  4. 后处理(Post-processing,添加分词器的特殊令牌,生成注意力掩码和令牌类型ID)。

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"]

函数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")

3 全过程

3.1 import and prepare

要使用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,不需要它。

3.2 pre-tokenization

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))]

3.3 Model

接下来是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', '.']

3.4 post-processor

我们对 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."

4 最后

现在我们完成了,我们可以像之前一样保存标记器,并将其包装在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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值