NLP-学习笔记(一)
自然语言处理NLP的学习,不可能有一步登天的方法。博客用于记录自然语言处理的学习路程和知识点。
相信很多初学的同学,一开接触NLP,都会知道Transformers和BERT,性子比较急切的同学开始了死磕BERT源码,我一开始也是这样,从源码中获取知识,但是总是对网络的相关输入困惑,这篇学习笔记主要记录NLP数据输入处理的相关概念。
Model inputs
Token
Token的理解比较容易,是一串自然语言符号的最小语言单位(单词、词组、字符),将输入的一句话根据规定的最小语言单位划分开。 例如:“I am Chinese, I love China”
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
sequence = "I am Chinese, I love China"
tokenized_sequence = tokenizer.tokenize(sequence)
print("tokenized sequence:", tokenized_sequence)
[output] tokenized sequence: ['I', 'am', 'Chinese', ',', 'I', 'love', 'China']
每个tokenizer的工作方式不同,但其底层机制是相同的,上面是一个使用BERT分词器的例子,它是一个WordPiece分词器。
对tokenizer更好的解释:
The tokenizer takes care of splitting the sequence into tokens available in the tokenizer vocabulary.
input IDs
自然语言,是符号记录,需要将这种符号记录输入模型,使得模型能够“看得懂”这串记录,也就是说,input IDs 是作为输入传递给模型的唯一必需参数,它们是符号索引,是符号的数字表示形式,用于构建将被模型用作输入的序列。有了上述得到的token,将每个token转换成IDs
input_ids = tokenizer.convert_tokens_to_ids(tokenized_sequence)
print("ids sequence :", input_ids)
[output] ids sequence : [146, 1821, 1922, 117, 146, 1567, 1975]
也就是说,[146, 1821, 1922, 117, 146, 1567, 1975]这串IDs序列,是符号索引,对应着例句"I am Chinese, I love China",将这串IDs序列解码,即可得到相应的自然语言表示。
decode_output = tokenizer.decode(input_ids)
print("decode output :", decode_output)
decode output : I am Chinese, I love China
[output] decode output : I am Chinese, I love China
Attention mask
输入模型的句子都应该是等长的,但是句子有长有短,短的句子应该padding到等长,比如:
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
sequence_a = "This is a short sequence."
sequence_b = "This is a rather long sequence. It is at least longer than the sequence A."
encoded_sequence_a = tokenizer(sequence_a)["input_ids"]
encoded_sequence_b = tokenizer(sequence_b)["input_ids"]
len(encoded_sequence_a), len(encoded_sequence_b)
[output] (8,19)
句子A长度为8,句子B长度为19,因此句子A后面将padding一串0,使得长度等于19
padded_sequences = tokenizer([sequence_a, sequence_b], padding=True)
print("A ids:", padded_sequences["input_ids"][0])
print("B ids:", padded_sequences["input_ids"][1])
[output] A ids: [101, 1188, 1110, 170, 1603, 4954, 119, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
B ids: [101, 1188, 1110, 170, 1897, 1263, 4954, 119, 1135, 1110, 1120, 1655, 2039, 1190, 1103, 4954, 138, 119, 102]
这样可以在PyTorch或TensorFlow中转换成一个张量。此时需要Attention mask告诉模型,哪些是原句,哪些是填充,换句话说,Attention mask是一个二元张量,表示填充指标的位置。对于BertTokenizer, 1表示应该关注的值,而0表示填充值。
print("A attention mask:", padded_sequences["attention_mask"][0])
print("B attention mask:", padded_sequences["attention_mask"][1])
[output] A attention mask: [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
B attention mask: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Token Type IDs
当模型的目的是对句子进行分类或回答问题,此时一次输入模型的句子就不止一句,Token Type IDs就是标识不同句子序列。通常"[CLS]"表示一个句子的开始,“[SEP]”表示一个句子的结束。
sequence_a = "HuggingFace is based in NYC"
sequence_b = "Where is HuggingFace based?"
encoded_dict = tokenizer(sequence_a, sequence_b)
decoded = tokenizer.decode(encoded_dict["input_ids"])
decoded
[output] '[CLS] HuggingFace is based in NYC [SEP] Where is HuggingFace based? [SEP]'
此时的Token Type IDs 是:
encoded_dict['token_type_ids']
[output] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
开篇都是基础知识。
欲速则不达,千里筑基第一步
[1]: https://huggingface.co/docs/transformers/glossary