趋近智
为了有效地评估预训练 (pre-training)大语言模型(LLM)在下游任务上的表现,我们通常需要根据目标任务的特定格式和目标对其进行一些调整。这个过程称为微调 (fine-tuning)。虽然零样本或少样本评估(稍后讨论)测试模型固有的泛化能力,但微调使模型能够从标注数据中学习任务特定模式,通常能获得更高表现,并更清晰地呈现预训练模型在该任务上的潜在作用。
微调使用预训练期间学到的强大表征,并使用相对少量任务特定的标注数据进行调整。核心观点是预训练模型已经理解语言结构、语义和语境;微调只是教它如何将这些知识应用于新的问题形式。
对用于评估下游任务的LLM进行微调的标准流程包含以下步骤:
流程图如下:
预训练LLM的核心层为新添加的任务特定头部提供输入表征。该头部进行预测,然后使用任务特定的损失函数与任务数据集中的标签进行比较。此损失产生的梯度会更新头部的权重,通常也会更新部分或全部预训练层的权重。
调整步骤主要涉及添加正确的头部和适当的数据格式化。我们来看看常见例子:
[CLS] token,或因果模型中的最后一个token)的最终隐藏状态,并将其投射到输出类别的数量。在线性层之前通常会添加一个dropout层用于正则化 (regularization)。[CLS] text_sequence [SEP]。对于因果模型(如GPT),输入可能只是text_sequence,并使用最后一个token的表征。import torch
import torch.nn as nn
from transformers import ( # Example using Hugging Face Transformers
AutoModel, AutoTokenizer
)
# 加载预训练模型(例如BERT)
model_name = "bert-base-uncased"
pretrained_model = AutoModel.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 定义分类头部
num_labels = 3 # 示例:积极、消极、中性情感
hidden_size = pretrained_model.config.hidden_size
classification_head = nn.Sequential(
nn.Dropout(0.1),
nn.Linear(hidden_size, num_labels)
)
# 示例输入处理
text = "This is an example sentence."
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
# 前向传播(简化)
# 从预训练模型获取隐藏状态
outputs = pretrained_model(**inputs)
# 使用[CLS] token(第一个token)的表征
cls_representation = outputs.last_hidden_state[:, 0, :]
# 经过分类头部
logits = classification_head(cls_representation)
# 'logits'现在包含每个类别的原始分数
# 训练期间使用这些logits和目标标签应用交叉熵损失
[CLS] question_text [SEP] context_text [SEP]。# (接续之前的导入)
# 定义问答头部
qa_head = nn.Linear(hidden_size, 2)
# 输出:每个token的start_logit, end_logit
# 示例输入处理
question = "What is the capital of Malaysia?"
context = "Malaysia is a Southeast Asian country. Kuala Lumpur is its capital and largest city."
inputs = tokenizer(
question,
context,
return_tensors="pt",
padding=True,
truncation=True
)
# 前向传播(简化)
outputs = pretrained_model(**inputs)
sequence_output = outputs.last_hidden_state
# 形状:(批量大小, 序列长度, 隐藏维度)
# 将序列输出通过问答头部
logits = qa_head(sequence_output) # 形状:(批量大小, 序列长度, 2)
start_logits, end_logits = logits.split(1, dim=-1)
start_logits = start_logits.squeeze(-1) # 形状:(批量大小, 序列长度)
end_logits = end_logits.squeeze(-1) # 形状:(批量大小, 序列长度)
# 'start_logits'和'end_logits'包含起始/结束位置的分数
# 训练期间使用这些以及目标起始/结束索引应用交叉熵损失
# during training
input_article。对于翻译:translate English to French: input_english_sentence。目标序列在训练期间用作标签。微调涉及训练,但与预训练 (pre-training)相比有一些差异:
import torch
from torch.optim import AdamW
from torch.utils.data import DataLoader, Dataset
from transformers import get_linear_schedule_with_warmup
# 假设'model'包含预训练骨干和任务特定头部
# 假设'train_dataset'是一个PyTorch数据集,生成格式化、
# 分词后的输入和标签
# 假设'loss_fn'是适合的损失函数(例如nn.CrossEntropyLoss)
learning_rate = 3e-5
num_epochs = 3
batch_size = 16
warmup_steps = 100
total_training_steps = len(train_dataset) * num_epochs // batch_size # 近似值
optimizer = AdamW(model.parameters(), lr=learning_rate)
scheduler = get_linear_schedule_with_warmup(optimizer,
num_warmup_steps=warmup_steps,
num_training_steps=total_training_steps)
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
model.train()
for epoch in range(num_epochs):
for batch in train_dataloader:
# 将批次数据移动到设备
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device) # 假设标签是批次的一部分
# 清除之前的梯度
optimizer.zero_grad()
# 前向传播 - 获取模型预测(logits)
# 获取logits的确切方法取决于模型和头部结构
# 对于分类:logits = model(
# input_ids=input_ids, attention_mask=attention_mask).logits
# 对于问答:outputs = model(...); start_logits, end_logits =
# outputs.start_logits, outputs.end_logits
# 这需要根据具体的模型包装器和任务进行调整
outputs = model(input_ids=input_ids,
attention_mask=attention_mask)
logits = outputs.logits # 根据实际模型输出结构调整
# 计算损失
# 损失计算取决于任务(例如,logits与标签的形状)
# 对于分类:loss = loss_fn(
# logits.view(-1, num_labels), labels.view(-1))
# 对于问答:loss = (loss_fn(start_logits, start_positions) +
# loss_fn(end_logits, end_positions)) / 2
loss = loss_fn(logits, labels) # 根据特定任务损失需求调整
# 反向传播
loss.backward()
# 更新权重
optimizer.step()
scheduler.step()
print(f"周期 {epoch + 1} 完成。上一个批次的损失:{loss.item()}")
# 训练后,使用任务特定指标在测试集上进行评估
重要的是,微调 (fine-tuning)后,模型使用下游任务特有的指标进行评估。对于分类,这可能是准确率或F1分数。对于问答,精确匹配(EM)和预测答案token的F1分数是常见的。对于摘要,ROUGE分数(ROUGE-1、ROUGE-2、ROUGE-L)是标准指标,衡量与参考摘要的重叠度。对于翻译,BLEU分数常被使用。这些外部指标直接衡量模型在其所适应任务上的表现,补充了来自困惑度等内部指标的信息。
微调是评估和调整LLM的有效方法。尽管它需要标注数据和计算资源(虽然远少于预训练 (pre-training)),但它使我们能够评估预训练模型所学知识如何有效地迁移以解决特定实际问题。通过微调获得的结果通常代表着该特定任务中潜在的预训练模型具有的强劲表现基准。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•