Skip to content

Transformers 操作篇

来自:AiGC面试宝典

作者:宁静致远

日期:2024年01月28日


1. 如何利用 Transformers 加载 Bert 模型?

python
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配置文件内容如下:

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
}

如果需要在代码中动态设置,可以通过修改配置实现:

python
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:相当于"注意力分布图",可以看到模型在处理每个词时更关注哪些词

获取每一层网络的向量输出

python
# 最后一层的所有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面试宝典 提供优质内容!

基于 MIT 许可发布