第3篇 预处理

Preprocessing

在你可以将你的数据放入模型之前,数据需要处理成一个模型可以接受的形式。一个模型不能理解原始的文本、图像或音频。这些输入需要被转换成数字并组装成张量。在这篇教程当中,我们只介绍用分词器处理文本数据。

NLP

处理文本数据的主要工具是分词器。分词器一开始会根据若干规则将文本切分成tokens。这些tokens会被转换成数字,用于构建输入到模型中的张量。任何模型要求的额外的输入都会被分词器添加。

如果你打算使用一个预训练模型,使用相关联的预训练分词器是很重要的。这保证了文本与预训练使用的语料库使用相同的切分方式和词表。

分词

使用AutoTokenizer.from_pretrained()加载一个预训练分词器。

from transformers import AutoTokenizer

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

然后将你的句子放进分词器。

encoded_input = tokenizer("Do not meddle in the affairs of wizards, for they are subtle and quick to anger.")
print(encoded_input)

'''
output:
{'input_ids': [101, 2079, 2025, 19960, 10362, 1999, 1996, 3821, 1997, 16657, 1010, 2005, 2027, 2024, 11259, 1998, 4248, 2000, 4963, 1012, 102], 
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
'''

分词器返回一个包含三个重要信息的字典:

  • input_ids:句子中每个token对应的数字表示
  • attention_mask:一个token是否需要被关注
  • token_type_ids:在多个序列的情况下,每个token属于哪个句子

你可以通过解码input_ids得到原始的输入:

tokenizer.decode(encoded_input["input_ids"])

'''
output:
'[CLS] Do not meddle in the affairs of wizards, for they are subtle and quick to anger. [SEP]'
'''

正如你看到的那样,分词器添加了两个特殊的token:CLS和SEP。不是所有的模型需要特殊token,但是如果需要的话,分词器就会自动添加。

如果有好几个句子你想要的处理,将这些句子放入一个列表中传入分词器即可

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_inputs = tokenizer(batch_sentences)
print(encoded_inputs)

'''
{'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102], 
               [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], 
               [101, 1327, 1164, 5450, 23434, 136, 102]], 
 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0], 
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
                    [0, 0, 0, 0, 0, 0, 0]], 
 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1], 
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
                    [1, 1, 1, 1, 1, 1, 1]]}
'''

pad

当你处理一批次的句子,它们可能不是等长的。这是一个问题,因为输入到模型的张量必须是统一形状的。填充是一个通过添加padding token的方法确保张量的形状是长方形的策略。

设置padding参数为True,使得较短的句子可以去匹配最长的序列。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True)
print(encoded_input)

'''
output:
{'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], 
               [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], 
               [101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0, 0, 0, 0, 0, 0]], 
 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], 
 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], 
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
                    [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]]}
'''

注意到分词器用0填充了第1个句子和第3个句子,因为它们较短。

Truncation

在另一方面,有时一个序列对于模型来说太长了。在这种情况下,你需要截断序列使其变短。

设置truncation参数为True,使得将序列截断为模型可以接受的最大长度。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_input = tokenizer(batch_sentences, padding=True, truncation=True)
print(encoded_input)

'''
output:
{'input_ids': [[101, 1252, 1184, 1164, 1248, 6462, 136, 102, 0, 0, 0, 0, 0, 0, 0], 
               [101, 1790, 112, 189, 1341, 1119, 3520, 1164, 1248, 6462, 117, 21902, 1643, 119, 102], 
               [101, 1327, 1164, 5450, 23434, 136, 102, 0, 0, 0, 0, 0, 0, 0, 0]], 
 'token_type_ids': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], 
 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], 
                    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
                    [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]]}
'''

建立张量

最后,你想要分词器返回能够输入到模型中的张量。设置return_tensors参数为pt即可。

batch_sentences = [
    "But what about second breakfast?",
    "Don't think he knows about second breakfast, Pip.",
    "What about elevensies?",
]
encoded_input = tokenizer(batch, padding=True, truncation=True, return_tensors="pt")
print(encoded_input)

'''
output:
{'input_ids': tensor([[  101,   153,  7719, 21490,  1122,  1114,  9582,  1623,   102],
                      [  101,  5226,  1122,  9649,  1199,  2610,  1236,   102,     0]]), 
 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0],
                           [0, 0, 0, 0, 0, 0, 0, 0, 0]]), 
 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1],
                           [1, 1, 1, 1, 1, 1, 1, 1, 0]])}
'''

所有关于填充和截断你想知道的事情

上文中介绍的内容已经适合绝大多数场景了,但是API还提供了更多的策略。你需要知道的三个参数:padding、truncation、max_length

  • padding控制填充。它可以是一个布尔值,也可以是一个字符串:
    • True或者'longest'可以将句子填充到批次中最长句子的长度(如果你只提供了一个句子,那就不进行填充)
    • 'max_length'可以将句子填充到max_length参数指定的长度,如果max_length没有提供,那么就填充到模型可以接受的最大长度。如果你只提供一个句子,填充也是会进行的。
    • False或'do_not_pad'不进行填充。这是默认的行为。
  • truncation控制truncation。它可以是一个布尔值,也可以是一个字符串:
    • True或'longest_first'将序列截断到max_length参数指定的最大长度,如果max_length没有设置的话,则截断到模型可以接受的最大长度。过程中会逐个token截断,移除序列对中最长序列的一个token,直到整个序列达到合适的长度为止。
    • 'only_second'将序列截断到max_length参数指定的最大长度,如果max_length没有设置的话,则截断到模型可以接受的最大长度。过程中如果提供的是一对文本,那么对第二个文本进行截断。
    • 'only_first'将序列截断到max_length参数指定的最大长度,如果max_length没有设置的话,则截断到模型可以接受的最大长度。过程中如果提供的是一对文本,那么对第一个文本进行截断。
    • False或'do_not_truncate'不对序列进行截断。这是默认的行为。
  • max_length控制填充或截断的长度。它可以是一个整数或None,如果为None,则默认为模型可以接受的最大长度。
posted @ 2022-02-12 14:50  pbc的成长之路  阅读(261)  评论(0)    收藏  举报