Transformers 操作篇
来自:AiGC面试宝典
作者:宁静致远
日期:2024年01月28日
1. 如何利用 Transformers 加载 Bert 模型?
import torch
from transformers import BertModel, BertTokenizer
# 调用bert-base模型,词典经过小写处理
model_name = 'bert-base-uncased'
# 读取模型对应的tokenizer
tokenizer = BertTokenizer.from_pretrained(model_name)
# 载入模型
model = BertModel.from_pretrained(model_name)
# 输入文本
input_text = "Here is some text to encode"
# 通过tokenizer把文本变成 token_id
input_ids = tokenizer.encode(input_text, add_special_tokens=True)
# input_ids: [101, 2182, 2003, 2070, 3793, 2000, 4372, 16044, 102]
input_ids = torch.tensor([input_ids])
# 获得BERT模型最后一个隐层结果
with torch.no_grad():
last_hidden_states = model(input_ids)[0] # Model outputs are now tuples输出结果:
tensor([[[-0.0549, 0.1053, -0.1065, ..., -0.3550, 0.0686, 0.6506],
[-0.5759, -0.3650, -0.1383, ..., -0.6782, 0.2092, -0.1639],
[-0.1641, -0.5597, 0.0150, ..., -0.1603, -0.1346, 0.6216],
...,
[ 0.2448, 0.1254, 0.1587, ..., -0.2749, -0.1163, 0.8809],
[ 0.0481, 0.4950, -0.2827, ..., -0.6097, -0.1212, 0.2527],
[ 0.9046, 0.2137, -0.5897, ..., 0.3040, -0.6172, -0.1950]]])
shape: (1, 9, 768)可以看到,包括import在内的不到十行代码,我们就实现了读取一个预训练过的BERT模型,来encode我们指定的一个文本,对文本的每一个token生成768维的向量。如果是二分类任务,我们接下来就可以把第一个token也就是[CLS]的768维向量,接一个linear层,预测出分类的logits,或者根据标签进行训练。
📝通俗解释:这段代码就像给BERT模型装上"眼睛",让它能读懂文本。tokenizer把文字转换成数字(token_id),BERT模型再把这些数字转换成768维的向量(就像把每个词翻译成一段密码)。对于分类任务,我们通常取第一个特殊标记[CLS]的向量,因为它包含了整个句子的"总结信息"。
2. 如何利用 Transformers 输出 Bert 指定 hidden_state?
BERT默认是十二层,但有时候预训练时并不需要利用全部层,只需要前面几层即可,此时该怎么做呢?
下载的bert-base-uncased模型目录里包含配置文件config.json,可以通过设置output_hidden_states参数来控制是否输出所有隐藏层。
config.json配置文件内容如下:
{
"architectures": [
"BertForMaskedLM"
],
"attention_probs_dropout_prob": 0.1,
"hidden_act": "gelu",
"hidden_dropout_prob": 0.1,
"hidden_size": 768,
"initializer_range": 0.02,
"intermediate_size": 3072,
"max_position_embeddings": 512,
"num_attention_heads": 12,
"num_hidden_layers": 12,
"type_vocab_size": 2,
"vocab_size": 30522,
"output_hidden_states": true
}如果需要在代码中动态设置,可以通过修改配置实现:
from transformers import BertModel, BertConfig
# 方法一:加载配置后修改
config = BertConfig.from_pretrained('bert-base-uncased')
config.output_hidden_states = True
model = BertModel.from_pretrained('bert-base-uncased', config=config)
# 方法二:直接使用from_pretrained的参数
model = BertModel.from_pretrained(
'bert-base-uncased',
output_hidden_states=True,
output_attentions=True
)📝通俗解释:BERT模型有12层"处理车间",每层都会对文本进行处理并输出结果。默认情况下,我们只得到最后一层的结果。但有时候我们想看中间每一层的输出,或者只用到前面几层,这时就需要设置
output_hidden_states=True来"打开"所有隐藏层的门,让它们都能被访问到。
3. BERT 获取最后一层或每一层网络的向量输出
模型输出的主要成分
| 输出名称 | Shape | 说明 |
|---|---|---|
last_hidden_state | (batch_size, sequence_length, hidden_size) | 模型最后一层输出的隐藏状态,hidden_size=768 |
pooler_output | (batch_size, hidden_size) | 序列第一个token([CLS])的最后一层隐藏状态,经过线性层和Tanh激活函数处理 |
hidden_states | 元组,包含13个tensor | 可选项,需要设置output_hidden_states=True。第一个是embedding,其余是各层输出 |
attentions | 元组 | 可选项,需要设置output_attentions=True,每一层的注意力权重 |
📝通俗解释:
last_hidden_state:相当于"最终答案",是模型最后一层给出的结果pooler_output:相当于"CLS标记"的"特制版本",经过了额外处理,但作者建议用平均池化效果更好hidden_states:相当于"完整解题过程",可以看到模型每一步的思考attentions:相当于"注意力分布图",可以看到模型在处理每个词时更关注哪些词
获取每一层网络的向量输出
# 最后一层的所有token向量
outputs.last_hidden_state
# cls向量(经过池化的)
outputs.pooler_output
# hidden_states,包含13层
# 索引0是输入embedding向量,索引1-12是每层的输出向量
hidden_states = outputs.hidden_states
embedding_output = hidden_states[0] # 嵌入层输出
attention_hidden_states = hidden_states[1:] # 12层Transformer的输出📝通俗解释:获取各层输出就像查看"解题过程的每一步":
outputs.last_hidden_state= 只看最终答案outputs.pooler_output= 只看开头那个[CLS]标记的最终状态hidden_states[0]= 原始的词嵌入(还没经过任何处理)hidden_states[1:]= 依次经过12层Transformer处理后的结果
有时候用中间层的效果比最后一层更好,因为最后一层可能过于"适应"预训练任务,而中间层保留了更多通用特征。
致谢
感谢 AiGC面试宝典 提供优质内容!