Pytorch Transformer Tokenizer 常见输入输出实战详解
文章目录
本文仅用作记录,引用自【Pytorch Transformer Tokenizer常见输入输出实战详解】- yosemite1998 - CSDN,请关注原作者,给原作者点赞。
Tokenizer 简介和工作流程
Transformers,以及基于BERT家族的预训练模型+微调模式已经成为NLP领域的标配。而作为文本数据预处理的主要方法-Tokenizer(分词器)则成为了必不可少的工具。本篇文章以Transformers中使用的AutoTokenizer为例说明其用法。但如果实际场景中使用BERT、ALBERT等预训练模型,原理类似,但需要使用模型相对应的Tokenizer,例如transformers.BertModel对应的Tokenizer是transformers.BertTokenizer
通常,我们会直接使用Transformers包中的AutoTokenizer类来创建并使用分词器。分词器的工作流程如下:
- 将给定的文本拆分为称为Token(词或标记)的单词(或部分单词、标点符号等)。
- 将这些Token转换为数字编码,以便构建张量并将其提供给模型。
- 添加模型正常工作所需的任何输入数据。例如特殊字符[CLS],[SEP]等
下面会分别介绍Tokenizer常见的几种输入,以及输出中的三个常用字段。
Tokenizer 的单句输入,以及输出中的“input_ids”字段
我们先看一下tokenizer的单句输入和对应输出。tokenizer.tokenize(sequence)方法接受单句的输入,完成上述的第1步任务,将文本拆分为token的数字。而tokenizer(sequence)则直接完成第1到第3步的工作。缺省情况下,输出的数字编码(encoding)是至少包含一个组成元素"input_ids"的字典。“input_ids”字段是输出的数字编码字典中唯一不可少的字段,即以数组形式存储的标记索引(token indices),比如下面输出中的数字138对应"A"、18696对应"Titan"。以字典方式可以直接访问并输出句子对应的token索引序列。当然,我们也可以反过来,用tokenizer.decode()方法逆向解码一个token索引序列,并返回原始句子(实际是原始句子的一个超集)。
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
# Transformer's tokenizer - input_ids
sequence = "A Titan RTX has 24GB of VRAM"
print("Original sequence: ",sequence)
tokenized_sequence = tokenizer.tokenize(sequence)
print("Tokenized sequence: ",tokenized_sequence)
encodings = tokenizer(sequence)
encoded_sequence = encodings['input_ids']
print("Encoded sequence: ", encoded_sequence)
decoded_encodings=tokenizer.decode(encoded_sequence)
print("Decoded sequence: ", decoded_encodings)
输出结果并注意:
大写字母组成的单词会分解为一个或多个语义独立的token,但tokenizer也会用##来表示其并非单独存在。
解码后的句子跟原句子不完全一致,而是多了两个特殊字符。这两个特殊字符是tokenizer()方法在第3步任务时自动添加的,例如[CLS]表示句子开始或分类标志,[SEP]表示句子结束或分隔标志。当调用方法逆向解码时,也会一并显示多加的字符。
Tokenizer 的多句子输入,以及输出中的Attention_mask
多句子输入的时候,需要标准化,使其能够整齐划一的进行比较。例如,输入的两个句子长度不一致,需要通过短句填充(padding)的方式让其长度一致。不过这样也会带来额外的问题。比如,在训练的时候,机器并不知道填充的内容无意义,仍然会忠实的按照填充的数值来计算,从而可能引入错误的信息。为了避免这个问题,toknenizer的缺省输出会返回第二个字段"attention_mask"。它会告诉机器返回的数字编码中哪些是需要注意的实际数据,哪些是不需要关心的填充数据。
# Transformer's tokenizer - attention_mask
sequence_a = "This is a short sequence."
sequence_b = "This is a rather long sequence. It is at least longer than the sequence A."
print("Sequence a: ",sequence_a)
print("Sequence b: ",sequence_b)
encoded_sequence_a = tokenizer(sequence_a)["input_ids"]
encoded_sequence_b = tokenizer(sequence_b)["input_ids"]
print("A's encoding length={}. \nB's encoding length={}".format(len(encoded_sequence_a),len(encoded_sequence_b)))
padded_sequence_ab = tokenizer([sequence_a,sequence_b],padding=True)
print("Padded sequence(A,B):", padded_sequence_ab["input_ids"])
print("Attention mask(A,B):", padded_sequence_ab["attention_mask"])
输出结果:
Tokenizer 两个句子拼接输入和输出的token_type_ids
有些NLP任务需要将两个句子拼接在一起,比如序列标注/分类和问答。例如问答时,需要第一个作为上下文,第二个句子作为问题,要求模型输出答案。这时tokenizer接受两个句子的顺序输入并输出数字编码。虽然返回的数字编码中也包含了句子的分隔信息,Tokenizer的输出仍然提供可选的第3个常用字段"token_type_ids"。它用来表明返回的数字编码中哪些属于第一个句子,哪些属于第二个句子。
# Transformer's tokenizer - token type id
encodings_ab = tokenizer(sequence_a, sequence_b)
print("Encoded sequence(AB):", encodings_ab["input_ids"])
decoded_ab = tokenizer.decode(encodings_ab["input_ids"])
print("Decoded sequence(AB):", decoded_ab)
print("Token type ids(AB):", encodings_ab["token_type_ids"])
输出结果:
本文简单总结了Tokenizer在基于Transformers应用中的主要作用和工作流程,并详细解释了tokenizer()的三种常用输入,包括单句、多句和两句拼接输入。另外,详细解释了三个最常用的输出字段"input_idx"、“attention_mask”,“token_type_ids”,供大家参考。