当前位置:   article > 正文

【自己创建分词器tokenizer】(3)——Unigram tokenizer_自定义tokenizer

自定义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"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

函数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")
  • 1
  • 2
  • 3

3 全过程

3.1 引入依赖

要使用Tokenizers库构建一个分词器,首先我们实例化一个Tokenizer对象,然后将其normalizer、pre_tokenizer、post_processor和decoder属性设置为所需的值。
在这个示例中,我们将创建一个带有WordPiece模型的分词器:

from tokenizers import (
    decoders,
    models,
    normalizers,
    pre_tokenizers,
    processors,
    trainers,
    Tokenizer,
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

现在让我们构建一个XLNet标记器。与之前的标记器一样,我们首先通过使用Unigram模型来初始化一个Tokenizer:

tokenizer = Tokenizer(models.Unigram())
  • 1

3.2 normalization

同样地,如果我们有词汇表,我们可以使用它来初始化该模型。
对于标准化,XLNet使用了一些替换(这些替换来自SentencePiece):

from tokenizers import Regex

tokenizer.normalizer = normalizers.Sequence(
    [
        normalizers.Replace("``", '"'),
        normalizers.Replace("''", '"'),
        normalizers.NFKD(),
        normalizers.StripAccents(),
        normalizers.Replace(Regex(" {2,}"), " "),
    ]
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这将"``“替换为’”‘;将"’‘"替换为’"',并将两个或多个空格的连续序列替换为单个空格,同时删除要进行标记的文本中的重音符号。

3.3 pre-tokenizer

任何SentencePiece分词器使用的预标记器都是Metaspace。

tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
  • 1

我们可以像前面一样查看示例文本的预标记化:

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

输出:

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

3.4 Model

接下来是需要训练的模型。 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)
  • 1
  • 2
  • 3
  • 4
  • 5

不要忘记UnigramTrainer的一个非常重要的参数是unk_token。我们还可以传递其他特定于Unigram算法的参数,例如在每个步骤中删除标记时的shrinking_factor(默认为0.75)或max_piece_length以指定给定标记的最大长度(默认为16)。
这个标记器也可以在文本文件上进行训练:

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

让我们看一下示例文本的标记化:

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

输出:

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

3.5 post-processor

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)
  • 1
  • 2
  • 3

输出:

0 1
  • 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)],
)
  • 1
  • 2
  • 3
  • 4
  • 5

我们可以通过编码一对句子来测试它的工作原理:

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

输出:

['▁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]
  • 1
  • 2
  • 3

最后,我们添加一个Metaspace解码器:

tokenizer.decoder = decoders.Metaspace()
  • 1

4 最后

我们完成了这个分词器!我们可以像之前一样保存分词器,并且如果想要在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",
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
from transformers import XLNetTokenizerFast

wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
  • 1
  • 2
  • 3

其他部分,比如保存与加载等,和之前WordPiece tokenizer是一样的,本文没有赘述。

参考:
https://huggingface.co/learn/nlp-course/chapter6/8?fw=pt#building-a-unigram-tokenizer-from-scratch

end

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/945392
推荐阅读
  

闽ICP备14008679号