HuggingfaceNLP笔记2.3Tokenizers

分词器是自然语言处理(NLP)管道的核心组件之一。它们的任务是将文本转换为模型可以处理的数据。由于模型只能处理数字,因此分词器需要将我们的文本输入转换为数值数据。本节将详细介绍分词管道中的过程。

在NLP任务中,通常处理的是原始文本数据。例如:

Jim Henson was a puppeteer

然而,模型只能处理数字,所以我们需要找到一种方法将文本转换为数字。这就是分词器的作用,有许多方法可以实现。目标是找到对模型最有意义的表示形式,如果可能的话,是最小的表示。

让我们看看一些分词算法的例子,并尝试回答你可能对分词的一些问题。

Word-based

首先想到的分词类型是“基于词”的。它通常设置和使用起来非常简单,只需要遵循一些规则,通常能取得不错的结果。在下面的示例中,目标是将原始文本分割成单词,并为每个单词找到一个数值表示:

在这里插入图片描述

有多种方法分割文本。例如,我们可以使用Python的split()函数,利用空格将文本分割成单词:

tokenized_text = "Jim Henson was a puppeteer".split()
print(tokenized_text)
['Jim', 'Henson', 'was', 'a', 'puppeteer']

还有对标点符号有额外规则的词分词器。这种情况下,我们可能会得到一个较大的“词汇表”,其中词汇表由我们语料库中独立标记总数定义。

每个单词都会被分配一个ID,从0开始,直到词汇表的大小。模型使用这些ID来识别每个单词。

如果我们想完全用基于词的分词器覆盖一种语言,我们需要为该语言中的每个单词分配一个标识符,这将产生大量的标记。例如,英语中有超过500,000个单词,因此我们需要跟踪这么多的ID来构建从每个单词到输入ID的映射。此外,“dog”和“dogs”这样的单词会被分别表示,模型最初不会知道它们相似;同样,“run”和“running”这样的相似单词也会被模型视为不相关。

最后,我们还需要一个特殊的标记来表示不在词汇表中的单词,通常称为“未知”令牌,如”[UNK]”或””。如果分词器产生大量这样的令牌,通常是个坏信号,因为模型无法找到合理的单词表示,信息在过程中丢失了。构建词汇表时的目标是尽可能减少将单词转换为未知令牌的情况。

减少未知令牌的一个方法是深入一层,使用“基于字符的”分词器。

Character-based

基于字符的分词器将文本分割成字符,而不是单词。这有两个主要优点:

  • 词汇表更小。
  • 未知(out-of-vocabulary,OOV)令牌更少,因为每个单词都可以由字符组成。

然而,关于空格和标点符号的问题仍然存在:

在这里插入图片描述

这种方法也不是完美的。由于现在基于字符而不是单词的表示,有人可能会认为,直观上,这不太有意义:每个字符本身意义不大,而单词则不然。然而,这取决于语言;例如,在中文中,每个字符比拉丁语言中的字符携带更多信息。

另一个需要考虑的因素是,我们最终会得到大量需要由我们的模型处理的标记:而在基于单词的分词器中,一个词只会成为一个单独的标记,但当转换为字符时,它很容易变成10个或更多的标记。

为了兼顾效率和效果,我们可以采用第三种方法:子词分割

Subword tokenization

子词分割算法基于一个原则:常用词不应被分割成更小的子词,而罕见词应分解为有意义的子词。

例如,“annoyingly”可能被视为一个罕见词,可以分解为“annoying”和“ly”。这两个词作为独立的子词出现频率更高,同时“annoyingly”的含义通过“annoying”和“ly”组合保持。

下面是一个示例,展示子词分割算法如何处理序列“Let’s do tokenization!“:

在这里插入图片描述

这些子词提供了丰富的语义信息:例如,在上述示例中,“tokenization”被分割为“token”和“ization”,这两个词既具有语义意义,又节省空间(仅需两个令牌来表示长词)。这使得我们能够在小词汇表中获得相当好的覆盖,同时几乎没有未知令牌。

这种方法特别适用于像土耳其这样的黏着语,可以通过连接子词形成几乎无限长的复杂词。

And more!

毫不奇怪,还有许多其他技术。这里列举一些:

  • GPT-2中使用的字节级BPE
  • BERT中使用的WordPiece
  • 多语言模型中使用的SentencePiece或Unigram

现在你应该对分词器的工作原理有了足够的了解,可以开始使用API了。

Loading and saving

加载和保存分词器与模型的处理方式相同。实际上,它们基于相同的两种方法:from_pretrained()save_pretrained()。这些方法会加载或保存分词器使用的算法(类似于模型的“架构”)以及其词汇表(类似于模型的“权重”)。

加载与BERT使用相同检查点训练的分词器,就像加载模型一样,但使用BertTokenizer类:

from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-cased")

AutoModel类似,AutoTokenizer类会根据检查点名称从库中获取适当的分词器类,并可以直接用于任何检查点:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

现在我们可以像前面那样使用分词器:

tokenizer("Using a Transformer network is simple")
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}

保存分词器与保存模型完全相同:

tokenizer.save_pretrained("我的计算机上的目录")

关于token_type_ids的内容将在第3章中详细介绍,attention_mask键稍后会解释。首先,让我们看看如何生成input_ids。为此,我们需要查看分词器的中间方法。

Encoding

将文本转换为数字称为编码。编码是一个两步过程:首先进行分词,然后转换为输入ID。

正如我们所见,第一步是将文本分割成单词(或单词的一部分、标点符号等),通常称为令牌。这个过程可能遵循多种规则,因此我们需要使用模型的名称来实例化分词器,以确保使用与模型预训练时相同的规则。

第二步是将这些令牌转换为数字,以便我们可以构建一个张量并将它们馈送到模型。为此,分词器有一个词汇表,这是我们在使用from_pretrained()方法实例化时下载的部分。同样,我们需要使用与模型预训练时相同的词汇表。

为了更好地理解这两个步骤,我们将分别探讨。请注意,我们将使用部分分词 pipeline的方法来展示这些步骤的中间结果,但在实践中,你应该直接在输入上调用分词器(如第2部分所示)。

###Tokenization

分词过程由分词器的 tokenize() 方法处理:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)

这种方法的输出是一个字符串列表,或者叫做令牌。

['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']

这个分词器是一个子词分词器:它会将单词拆分成可以由其词汇表表示的令牌。例如,transformer 被拆分为两个令牌:transform##er

From tokens to input IDs

到输入ID的转换是由 convert_tokens_to_ids() 分词器方法处理:

ids = tokenizer.convert_tokens_to_ids(tokens)

print(ids)
[7993, 170, 11303, 1200, 2443, 1110, 3014]

这些输出一旦转换为适当的框架张量,就可以像本章前面部分所示那样用作模型的输入。

📝 动手试试! 请在第2部分使用的输入句子(“I’ve been waiting for a HuggingFace course my whole life.” 和 “I hate this so much!”)上复制最后两个步骤(分词和转换为输入ID)。检查你得到的输入ID是否与我们之前的一致!

Decoding

解码 是反向操作:从词汇索引,我们想要得到一个字符串。这可以通过 decode() 方法完成:

decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)
'Using a Transformer network is simple'

请注意,decode 方法不仅将索引还原为令牌,还会将属于同一单词的令牌组合在一起,生成可读的句子。当我们使用预测新文本(无论是基于提示生成的文本,还是用于翻译或摘要等序列到序列问题)的模型时,这种行为将非常有用。

到目前为止,你应该理解了分词器可以处理的基本操作:分词、转换为ID和将ID转换回字符串。然而,我们只是触及了冰山一角。在下一节中,我们将探讨我们的方法的局限性,并看看如何克服它们。

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HITzwx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值