transformers 库

Quick tour

Under the hood: pretrained models

创建 model 和 tokenizer

使用 from_pretrained() 方法 创建 model and tokenizer

from transformers import AutoTokenizer, AutoModelForSequenceClassification
model_name = "distilbert-base-uncased-finetuned-sst-2-english"
pt_model = AutoModelForSequenceClassification.from_pretrained(model_name) # 创建 model
tokenizer = AutoTokenizer.from_pretrained(model_name) # 创建 Tokenizer

using Tokenizer

Tokenizer 主要用来 处理数据:

  1. 分词:将 sentence 分成多个 tokens;
  2. token 嵌入:获取分词后 token 在 词典 vocab 中的 index
    将这些 tokens 转换为它们在 vocab 中对应的 index,以便能够从中构建张量并将它们提供给模型。
    每个 model 的 Tokenizer 都有一个 词汇表(词典,vocab),vocab 会在使用 from_pretrained 方法实例化它的时候自动下载,因为我们需要使用与模型预训练时相同的词汇表。
inputs = tokenizer("We are very happy to show you the  Transformers library.") # 

tokenizer() 方法:返回一个 字典(dict),其中 key 是 string;value 为 list 类型。

  • input_ids:句子中 tokens 在 词典 vocab 中对应的 index;
  • attention_mask:为了 model 将用于更好地理解 sequence。
>>> print(inputs)
{'input_ids': [101, 2057, 2024, 2200, 3407, 2000, 2265, 2017, 1996, 100, 19081, 3075, 1012, 102],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}

也可以同时处理 多条句子:

 ## PYTORCH CODE
pt_batch = tokenizer(
	["We are very happy to show you the  Transformers library.", "We hope you don't hate it."],
	padding=True,
	truncation=True,
	max_length=512,
	return_tensors="pt"
	)
## PYTORCH CODE
for key, value in pt_batch.items():
	print(f"{key}: {value.numpy().tolist()}")

# input_ids: [[101, 2057, 2024, 2200, 3407, 2000, 2265, 2017, 1996, 100, 19081, 3075, 1012, 102], [101, 2057, 3246, 2017, 2123, 1005, 1056, 5223, 2009, 1012, 102, 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, 0, 0, 0]]

更多使用 Tokenizer 的 细节

using model

一旦您的输入被 Tokenizer 预处理,就可以将其直接输入到 model。

对于 PyTorch 模型,您需要通过 添加**来解压字典

pt_outputs = pt_model(**pt_batch) #  添加`**`来解压字典

print(pt_outputs)
# SequenceClassifierOutput(loss=None, logits=tensor([[-4.0833,  4.3364],
#        [ 0.0818, -0.0418]], grad_fn=), hidden_states=None, attentions=None)

所有 Transformers 模型的输出结果都未经过最终激活函数(如 SoftMax)激活,因为这个最终激活函数通常与损失融合。
下面使用 output 的 logits 属性,进行激活,得到最终的 预测标签。

from torch import nn

pt_predictions = nn.functional.softmax(pt_outputs.logits, dim=-1) # 使用 softmax 对 output 进行 激活
print(pt_predictions)
# tensor([[2.2043e-04, 9.9978e-01],
#        [5.3086e-01, 4.6914e-01]], grad_fn=)

model 的 保存/加载

一旦模型被 is fine-tuned 后,便可以保存 Tokenizer

  1. 保存模型
tokenizer.save_pretrained(save_directory)
model.save_pretrained(save_directory)
  1. 加载模型
    仍然利用 from_pretrained() 方法来 加载保存后的 model,但是此时把 model_name 换成 保存模型的 “模型文件目录” save_directory
from transformers import AutoTokenizer, AutoModelForSequenceClassification
tokenizer = AutoTokenizer.from_pretrained(save_directory)
model = AutoModelForSequenceClassification.from_pretrained(save_directory)

也可以使用 torch 加载 tf 保存的 transformers 模型;反之,亦可以。详见

Main concepts

transformers 库中的每个 model 对应 3 个类型的 class:

  • Model class:预训练模型(该库中大概有 30+ 个)。比如,BertModel
  • Configuration class:用于存储建立 model 所需的所有参数。比如,BertConfig
    • 不需要每次都实例化 Configuration class;
    • 当使用一个没有修改的 model 时,创建模型时会“自动”实例化 Configuration class
  • Tokenizer class
    • 存储 model 对应的词典 vocab
    • 提供用于 encode/decode 要输入到模型的 token 嵌入索引列表中的字符串的方法。比如,BertTokenizer

以上模型 “微调”后,可以如下方法 “保存”、“加载”:

  • from_pretrained():加载“微调”后的 Model/Configuration/Tokenizer
  • save_pretrained():保存“微调”后的 Model/Configuration/Tokenizer

BERT

Model inputs

Input IDs

input_ids 是 句子 sentence 中 tokens 在 词典 vocab 中对应的 index 序列。

input_ids 是 model 的 唯一 输入

from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
sequence = "A Titan RTX has 24GB of VRAM"
  1. 分词
    Tokenizer 可以用来 “分词”。
tokenized_sequence = tokenizer.tokenize(sequence) # 分词
print(tokenized_sequence)
# ['A', 'Titan', 'R', '##T', '##X', 'has', '24', '##GB', 'of', 'V', '##RA', '##M']

“分词”的结果,要么是 词典 vocab 中的 word、要么是 词典vocab 中的 子词subword。
比如,“VRAM” 不在 词典中,它被分为 “V”, “RA” and “M”。为了表示它们是来自同一个 word,在 “RA” and “M” 前面加上 ## 前缀。

  1. encode 句子
    将 sentence 中的 tokens 转化为它们在 词典 vocab 中对应的 index 序列

    使用 tokenizer() 方法,得到的是一个 字典dict,而 model 的输入通常只需要 input_ids 即可。

inputs = tokenizer(sequence)
print('inputs: ', inputs) # [101, 1037, 16537, 19387, 2595, 2038, 2484, 18259, 1997, 27830, 8067, 11057, 102]
encoded_sequence = inputs['input_ids'] # model 输入只要 
  1. decode 句子
    vocab 中对应的 index 序列 还原为 句子。
decoded_sequence = tokenizer.decode(encoded_sequence)
print(decoded_sequence) # [CLS] a titan rtx has 24gb of vramaaa [SEP],这就是 BERT 输入的格式

Attention mask

  • Attention mask 是将序列一起 batch批处理 使用的可选参数。
  • Attention mask 向模型表明应该 “关注”(attention)哪些 tokens,哪些不应该 “注意”。

在进行 batch批处理 句子时,需要利用 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"]

print('len_a = ', len(encoded_sequence_a), # 8
      ', len_b = ', len(encoded_sequence_b)) # 19
  • sequence_a 中只有 6 个 token,而 encoded_sequence_a 长度却为 8,是因为在首尾多了 CLS 和 SEP.
  • 在 BERT 模型中,CLS 在 vocab 中的 index 为 101,而 SEP 为 102.
  1. padding 操作:
padded_sequences = tokenizer([sequence_a, sequence_b], padding=True)
print(len(padded_sequences['input_ids'][0])) # sequence_a 经过 padding 后,len 也是 19(和sequence_b长度相同)
  1. attention_mask
    经过 padding 后,句子中被 padding 的位置对应的 attention_mask 为 0未 padding,则为 1。
    比如,sequence_a 中只有 8 个 为1,表示 sequence_a 中原来只有 8 个 word,而剩下 19 - 8 个 0 都是 padding 部分(不用“关注” padding 部分)。

    attention_mask 的意思是,只用关注 sequence 中未 padding 之前的 token(即,attention_mask 为 1 的 token),而不用关注 padding 的 toekn(即,为0的)

print(padded_sequences['attention_mask'])
# [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 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]]

Token Type IDs

当需要将 多个句子 拼接成 一个input_ids 时,需要使用 [CLS][SEP]

比如,

 [CLS] SEQUENCE_A [SEP] SEQUENCE_B [SEP]

具体做法,仍然是使用 tokenizer() 方法,只不过需要注意的是:

  • 在进行 batch处理 句子时,使用的是 列表 []
padded_sequences = tokenizer([sequence_a, sequence_b], padding=True) # 这里是 列表[]
  • 而此时拼接 句子是,使用的是 逗号,
tokenizer(sequence_a, sequence_b) # 这里是逗号 ,

具体代码如下:

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
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"])  # 使用 [SEP] 拼接2个句子
# [CLS] this is a short sequence. [SEP] this is a rather long sequence. it is at least longer than the sequence a. [SEP]

其中,[SEP] 标记上一个 sentence 的 end,以及下一个 sentence 的 start。

BERT 中还使用 token_type_ids 来标记,区分同一个 input_ids 中的不同 sentence。

Summary of the tasks

transformer 库,常用功能:

  • 序列分类:Sequence Classification
  • 抽取式问答:Extractive Question Answering
  • 语言建模:Language Modeling
  • 文本生成:Text Generation
  • 命名实体识别:Named Entity Recognition
  • 摘要:Summarization
  • 翻译:Translation

这里以 Sequence Classification 为例,说明一下用法。

你可能感兴趣的:(nlp,transformer,深度学习,人工智能)