BERT 与 GPT 对比
BERT 与 GPT 对比
简介
BERT 和 GPT 是 Transformer 架构的两大分支:BERT 基于编码器,通过双向注意力建模上下文表示,擅长理解和分类任务;GPT 基于解码器,通过自回归方式逐 token 生成,擅长文本生成和对话任务。理解两者的架构差异、训练目标和适用场景,是选择大模型方案的基础。在大模型时代,BERT 系列代表了"理解"路线(编码器),GPT 系列代表了"生成"路线(解码器),而 T5、BART 等 Encoder-Decoder 架构则在特定序列转换任务上展现出独特优势。选择哪个模型路线,取决于你的任务类型、数据规模、推理成本和部署环境。
特点
架构差异详解
BERT 编码器架构
BERT 使用 Transformer 的编码器(Encoder)部分。编码器的核心是双向自注意力机制(Self-Attention),每个位置的表示可以同时关注输入序列中的所有其他位置。这种设计使 BERT 非常适合"理解"类任务——比如判断一句话的情感、识别文本中的人名地名、计算两个句子的语义相似度。
BERT 的预训练包含两个任务:
- MLM(Masked Language Model):随机遮蔽输入中 15% 的 token,让模型根据上下文预测被遮蔽的词。这迫使模型同时利用左右两侧的上下文信息。
- NSP(Next Sentence Prediction):输入两个句子,判断第二个是否是第一个的下一句(RoBERTa 移除了这个任务,因为它对下游任务帮助有限)。
GPT 解码器架构
GPT 使用 Transformer 的解码器(Decoder)部分。解码器的核心区别是因果注意力(Causal Attention),也称为掩码自注意力——每个位置只能看到它左侧(之前)的 token,不能看到右侧(未来)的 token。这是通过一个下三角掩码矩阵实现的。
GPT 的预训练目标只有一个:因果语言建模(Causal LM),即给定前面的 token,预测下一个 token。这个看似简单的目标,当模型规模足够大时,会涌现出各种能力(Emergent Abilities),包括少样本学习、思维链推理和指令遵循。
注意力掩码差异
# 示例4:对比 BERT 和 GPT 的注意力掩码差异
import torch
import torch.nn.functional as F
def show_attention_masks(seq_len=6):
"""展示 BERT(双向)和 GPT(因果)的注意力掩码差异"""
# BERT:双向注意力,所有位置互相可见
bert_mask = torch.ones(seq_len, seq_len)
# GPT:因果掩码,只能看到左侧(下三角矩阵)
causal_mask = torch.tril(torch.ones(seq_len, seq_len))
print("BERT 注意力掩码(双向,所有位置可见):")
print(bert_mask.int().numpy())
print("\nGPT 因果掩码(单向,只看左侧):")
print(causal_mask.int().numpy())
print()
# 模拟注意力:以位置3为例
for pos in [2, 5]:
print(f"位置 {pos} 在 BERT 中能看到: 位置 {list(range(seq_len))}")
visible = [j for j in range(seq_len) if causal_mask[pos, j] > 0]
print(f"位置 {pos} 在 GPT 中能看到: 位置 {visible}")
show_attention_masks()实现
使用 BERT 做文本分类
# 示例1:使用 HuggingFace Transformers 加载 BERT 做文本分类
import torch
from transformers import BertTokenizer, BertForSequenceClassification
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
text = "这部电影真的很不错,值得推荐"
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True, max_length=128)
with torch.no_grad():
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1)
pred = torch.argmax(probs, dim=-1)
print(f"输入: {text}")
print(f"正向概率: {probs[0][1].item():.4f}, 预测: {'正面' if pred.item() == 1 else '负面'}")
print(f"BERT 参数量: {sum(p.numel() for p in model.parameters()):,}")BERT 微调实战
# 示例1b:BERT 微调完整流程
import torch
from torch.utils.data import Dataset, DataLoader
from transformers import BertTokenizer, BertForSequenceClassification, AdamW, get_linear_schedule_with_warmup
class TextClassificationDataset(Dataset):
def __init__(self, texts, labels, tokenizer, max_length=128):
self.encodings = tokenizer(texts, truncation=True, padding=True,
max_length=max_length, return_tensors='pt')
self.labels = torch.tensor(labels)
def __len__(self):
return len(self.labels)
def __getitem__(self, idx):
return {k: v[idx] for k, v in self.encodings.items()}, self.labels[idx]
# 模拟数据
train_texts = ["这个产品非常好", "质量太差了", "服务态度很棒", "完全不推荐"]
train_labels = [1, 0, 1, 0]
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
dataset = TextClassificationDataset(train_texts, train_labels, tokenizer)
loader = DataLoader(dataset, batch_size=2, shuffle=True)
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
optimizer = AdamW(model.parameters(), lr=2e-5)
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=len(loader) * 3)
model.train()
for epoch in range(3):
total_loss = 0
for batch, labels in loader:
optimizer.zero_grad()
outputs = model(**batch, labels=labels)
loss = outputs.loss
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
scheduler.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {total_loss/len(loader):.4f}")
# 保存微调后的模型
model.save_pretrained('./finetuned_bert')
tokenizer.save_pretrained('./finetuned_bert')使用 GPT 进行文本生成
# 示例2:使用 GPT 进行文本生成
from transformers import GPT2LMHeadModel, GPT2Tokenizer
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2LMHeadModel.from_pretrained('gpt2')
prompt = "The key to building a good AI system is"
input_ids = tokenizer.encode(prompt, return_tensors='pt')
# 自回归生成
output = model.generate(
input_ids,
max_length=100,
num_return_sequences=1,
temperature=0.7,
top_k=50,
top_p=0.9,
do_sample=True,
pad_token_id=tokenizer.eos_token_id,
)
generated_text = tokenizer.decode(output[0], skip_special_tokens=True)
print(f"Prompt: {prompt}")
print(f"Generated: {generated_text}")
# 查看自回归过程:每步预测下一个 token
with torch.no_grad():
logits = model(input_ids).logits # (1, seq_len, vocab_size)
next_token_logits = logits[0, -1, :] # 最后位置的 logits
top5 = torch.topk(next_token_logits, 5)
print("\n下一个 token 预测 Top-5:")
for prob, idx in zip(top5.values, top5.indices):
print(f" {tokenizer.decode(idx)}: {prob.item():.2f}")生成参数调优
# 示例2b:不同生成策略对比
import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2LMHeadModel.from_pretrained('gpt2')
prompt = "Artificial intelligence will"
input_ids = tokenizer.encode(prompt, return_tensors='pt')
# 策略1:贪心搜索(每步选概率最高的 token)
greedy = model.generate(input_ids, max_length=50, do_sample=False)
print("贪心:", tokenizer.decode(greedy[0], skip_special_tokens=True))
# 策略2:Top-k 采样(从前 k 个高概率 token 中随机选)
topk = model.generate(input_ids, max_length=50, do_sample=True, top_k=40, temperature=0.8)
print("Top-k:", tokenizer.decode(topk[0], skip_special_tokens=True))
# 策略3:Top-p (Nucleus) 采样(从累积概率前 p 的最小 token 集中采样)
topp = model.generate(input_ids, max_length=50, do_sample=True, top_p=0.9, temperature=0.7)
print("Top-p:", tokenizer.decode(topp[0], skip_special_tokens=True))
# 策略4:Beam Search(束搜索,适合翻译等确定性任务)
beam = model.generate(input_ids, max_length=50, num_beams=5, early_stopping=True)
print("Beam:", tokenizer.decode(beam[0], skip_special_tokens=True))生成参数选择建议:
- temperature:控制随机性。0.0-0.3 生成确定性高,0.7-1.0 生成多样化,>1.0 容易生成不连贯内容。
- top_k:限制每步只从概率最高的 k 个 token 中采样。通常设为 40-50。
- top_p:从累积概率达到 p 的最小 token 集中采样(核采样)。通常设为 0.9。
- repetition_penalty:惩罚重复生成的 token,避免输出重复内容。通常设为 1.1-1.2。
BERT 的掩码语言模型(MLM)预测
# 示例3:BERT 的掩码语言模型(MLM)预测
from transformers import BertForMaskedLM, BertTokenizer
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForMaskedLM.from_pretrained('bert-base-chinese')
text = "北京是 createdBy谁。"
inputs = tokenizer(text, return_tensors='pt')
with torch.no_grad():
outputs = model(**inputs)
mask_token_idx = (inputs.input_ids == tokenizer.mask_token_id).nonzero()[0, 1]
mask_logits = outputs.logits[0, mask_token_idx]
top5 = torch.topk(torch.softmax(mask_logits, dim=-1), 5)
print(f"原文: {text}")
print(f"createdBy 预测 Top-5:")
for prob, idx in zip(top5.values, top5.indices):
token = tokenizer.decode(idx)
print(f" {token}: {prob.item():.4f}")CLS 向量与句子嵌入
# 示例5:提取 BERT 的 [CLS] 向量用于语义匹配
import torch
from transformers import BertTokenizer, BertModel
from torch.nn.functional import cosine_similarity
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertModel.from_pretrained('bert-base-chinese')
def get_cls_embedding(text):
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True, max_length=128)
with torch.no_grad():
outputs = model(**inputs)
return outputs.last_hidden_state[:, 0, :] # [CLS] 位置的向量
# 语义相似度计算
emb1 = get_cls_embedding("今天天气真好")
emb2 = get_cls_embedding("今天阳光明媚")
emb3 = get_cls_embedding("这个电影很无聊")
sim_12 = cosine_similarity(emb1, emb2).item()
sim_13 = cosine_similarity(emb1, emb3).item()
print(f"相似句子相似度: {sim_12:.4f}")
print(f"不相似句子相似度: {sim_13:.4f}")LoRA 参数高效微调
# 示例6:使用 LoRA 进行参数高效微调(以 PEFT 库为例)
# pip install peft
from transformers import AutoModelForSequenceClassification, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
model_name = 'bert-base-chinese'
tokenizer = AutoTokenizer.from_pretrained(model_name)
base_model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
# LoRA 配置:只在注意力层注入低秩适配器
lora_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
r=8, # 秩(低秩矩阵的维度)
lora_alpha=16, # 缩放因子
lora_dropout=0.1, # Dropout
target_modules=["query", "value"], # 只对 Q 和 V 矩阵做 LoRA
)
model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
# 输出类似:trainable params: 888,578 || all params: 109,486,082 || trainable%: 0.8112
# LoRA 只训练约 0.8% 的参数,显存需求大幅降低LoRA(Low-Rank Adaptation)的核心思想是在预训练模型的权重矩阵旁添加低秩分解矩阵(A 和 B),只训练这两个小矩阵,原始权重冻结。这可以将可训练参数减少到原来的 0.1%-1%,同时保持接近全量微调的效果。
Encoder-Decoder 架构:T5 与 BART
T5 和 BART 代表了 Transformer 的第三条路线——编码器-解码器(Encoder-Decoder)架构。编码器双向理解输入序列,解码器自回归生成输出序列,天然适合序列到序列(Seq2Seq)任务。
# T5 用于翻译任务
from transformers import T5ForConditionalGeneration, T5Tokenizer
tokenizer = T5Tokenizer.from_pretrained('t5-small')
model = T5ForConditionalGeneration.from_pretrained('t5-small')
# T5 采用文本到文本格式,所有任务统一为 "输入文本 -> 输出文本"
input_text = "translate English to French: The weather is nice today"
inputs = tokenizer(input_text, return_tensors='pt', max_length=128, truncation=True)
outputs = model.generate(inputs.input_ids, max_length=64, num_beams=4, early_stopping=True)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
# BART 用于文本摘要
from transformers import BartForConditionalGeneration, BartTokenizer
bart_tokenizer = BartTokenizer.from_pretrained('facebook/bart-large-cnn')
bart_model = BartForConditionalGeneration.from_pretrained('facebook/bart-large-cnn')
article = """
Natural language processing (NLP) is a subfield of linguistics, computer science,
and artificial intelligence concerned with the interactions between computers and human language.
"""
inputs = bart_tokenizer(article, return_tensors='pt', max_length=1024, truncation=True)
summary_ids = bart_model.generate(inputs.input_ids, max_length=80, min_length=30,
num_beams=4, length_penalty=2.0)
print(bart_tokenizer.decode(summary_ids[0], skip_special_tokens=True))BERT 命名实体识别(NER)实战
# BERT 用于 NER 任务
from transformers import BertForTokenClassification, BertTokenizer
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForTokenClassification.from_pretrained('uer/roberta-base-finetuned-cluener2020-chinese')
text = "张三在北京大学学习计算机科学"
inputs = tokenizer(text, return_tensors='pt', return_offsets_mapping=True)
offsets = inputs.pop('offset_mapping')[0]
with torch.no_grad():
outputs = model(**inputs)
predictions = torch.argmax(outputs.logits, dim=-1)[0]
# 解码实体
labels = model.config.id2label
entities = []
current_entity = None
for idx, (pred, offset) in enumerate(zip(predictions, offsets)):
if offset[0] == offset[1]: # 跳过特殊 token
continue
label = labels[pred.item()]
if label != 'O':
tag_type, tag_name = label.split('-')
char = text[offset[0]:offset[1]]
if tag_type == 'B':
if current_entity:
entities.append(current_entity)
current_entity = {'type': tag_name, 'text': char, 'start': offset[0].item()}
elif tag_type == 'I' and current_entity:
current_entity['text'] += char
else:
if current_entity:
entities.append(current_entity)
current_entity = None
if current_entity:
entities.append(current_entity)
for ent in entities:
print(f"实体: {ent['text']}, 类型: {ent['type']}, 位置: {ent['start']}")Sentence-BERT 语义检索
原生 BERT 的 [CLS] 向量语义表示能力有限,Sentence-BERT 通过孪生网络(Siamese Network)微调来获得更好的句子嵌入。
# 使用 Sentence-Transformers 做语义检索
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
# 构建知识库
corpus = [
"Python 是一种解释型高级编程语言",
"JavaScript 是 Web 前端开发的核心语言",
"机器学习是人工智能的一个重要分支",
"深度学习使用多层神经网络进行特征学习",
"Docker 是一个开源的容器化平台",
"Kubernetes 用于自动化部署和管理容器化应用",
]
corpus_embeddings = model.encode(corpus, convert_to_tensor=True)
# 查询
queries = ["什么是容器技术?", "AI 的学习方法有哪些?"]
for query in queries:
query_embedding = model.encode(query, convert_to_tensor=True)
hits = util.semantic_search(query_embedding, corpus_embeddings, top_k=3)
print(f"\n查询: {query}")
for hit in hits[0]:
print(f" [{hit['score']:.4f}] {corpus[hit['corpus_id']]}")GPT 的 Few-Shot Learning(上下文学习)
GPT 模型的一个关键能力是上下文学习(In-Context Learning),不需要梯度更新,仅通过在提示中展示几个示例即可适配新任务。
# Few-Shot Prompting 实战
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
tokenizer = AutoTokenizer.from_pretrained('gpt2')
model = AutoModelForCausalLM.from_pretrained('gpt2')
# 构建 Few-Shot Prompt
few_shot_prompt = """Classify the sentiment of each review:
Review: This movie is amazing!
Sentiment: Positive
Review: Terrible experience, waste of time.
Sentiment: Negative
Review: The food was okay, nothing special.
Sentiment: Neutral
Review: I love this product, highly recommend!
Sentiment:"""
input_ids = tokenizer.encode(few_shot_prompt, return_tensors='pt')
output = model.generate(input_ids, max_length=len(input_ids[0]) + 5,
temperature=0.1, do_sample=True)
result = tokenizer.decode(output[0][len(input_ids[0]):], skip_special_tokens=True)
print(f"预测情感: {result.strip()}")
# Chain-of-Thought Prompting(思维链提示)
cot_prompt = """Solve step by step:
Q: A store had 23 apples. They sold 15 and received a new shipment of 8. How many apples now?
A: Starting with 23 apples.
After selling 15: 23 - 15 = 8 apples.
After receiving 8: 8 + 8 = 16 apples.
The answer is 16.
Q: A train travels 120 km in 2 hours, then 180 km in 3 hours. What is the average speed?
A:"""
input_ids = tokenizer.encode(cot_prompt, return_tensors='pt')
output = model.generate(input_ids, max_length=len(input_ids[0]) + 80,
temperature=0.3, do_sample=True)
print(tokenizer.decode(output[0], skip_special_tokens=True))模型选择决策流程
选择 BERT 还是 GPT(或其他架构)应基于任务类型、资源约束和部署环境:
任务类型判断流程:
1. 任务是否需要生成文本?
是 -> GPT/解码器路线
否 -> 继续
2. 任务是否需要序列到序列转换(翻译、摘要)?
是 -> T5/BART(Encoder-Decoder)
否 -> 继续
3. 任务是否是分类、NER、匹配等理解任务?
是 -> BERT/RoBERTa(编码器路线)
否 -> 重新评估任务定义
资源约束考虑:
- 标注数据少于 1000 条 -> 优先使用预训练模型 + 少样本学习
- GPU 显存 < 8GB -> BERT-base 或 GPT-2 + LoRA
- 需要实时推理(< 50ms) -> BERT-base + ONNX 优化
- 需要高质量生成 -> GPT-4 API 或开源大模型 + vLLMONNX Runtime 部署 BERT
# 将 BERT 导出为 ONNX 格式以加速推理
from transformers import BertTokenizer, BertForSequenceClassification
from optimum.onnxruntime import ORTModelForSequenceClassification
from transformers import pipeline
# 导出 ONNX 模型
model_id = 'bert-base-chinese'
ort_model = ORTModelForSequenceClassification.from_pretrained(
model_id,
export=True,
provider='CPUExecutionProvider'
)
tokenizer = BertTokenizer.from_pretrained(model_id)
# 创建推理 pipeline
classifier = pipeline('text-classification', model=ort_model, tokenizer=tokenizer)
result = classifier("这个产品非常好用")
print(result)
# 对比推理速度
import time
texts = ["测试文本"] * 100
start = time.time()
for text in texts:
classifier(text)
onnx_time = time.time() - start
print(f"ONNX 推理 100 条耗时: {onnx_time:.2f}s")
# ONNX 通常比 PyTorch 推理快 2-4 倍QLoRA 量化低秩微调
QLoRA 在 LoRA 基础上引入 4-bit 量化,进一步降低显存需求,使得在消费级 GPU 上微调大模型成为可能。
# QLoRA 微调流程
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# 4-bit 量化配置
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_quant_type="nf4", # Normal Float 4-bit
bnb_4bit_use_double_quant=True, # 双重量化进一步节省显存
)
# 加载量化模型
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=quantization_config,
device_map="auto",
)
# 准备模型进行训练
model = prepare_model_for_kbit_training(model)
# LoRA 配置
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 7B 模型在 QLoRA 下可训练参数约 13M,总显存需求约 12GB
# 训练循环(简化版)
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./qlora_output",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
max_steps=500,
logging_steps=10,
save_steps=100,
fp16=True,
optim="paged_adamw_8bit",
)
# trainer = Trainer(model=model, args=training_args, train_dataset=dataset)
# trainer.train()常见训练问题与解决方案
问题1:训练 loss 不下降
- 检查学习率(BERT 微调推荐 2e-5 ~ 5e-5,GPT 微调推荐 1e-5 ~ 5e-5)
- 检查数据质量和标签分布
- 尝试增大 batch_size 或增加 warmup steps
- 确认 tokenizer 和模型匹配(中文必须用中文 tokenizer)
问题2:训练 loss 下降但验证集效果差(过拟合)
- 减少训练轮数(BERT 微调通常 3-5 epoch 足够)
- 增大 dropout(默认 0.1,可尝试 0.2-0.3)
- 增加数据增强(同义词替换、回译增强)
- 使用早停(Early Stopping)
问题3:显存不足(OOM)
- 减小 batch_size,增大 gradient_accumulation_steps
- 使用混合精度训练(fp16 或 bf16)
- 使用 LoRA/QLoRA 减少可训练参数
- 对长序列使用梯度检查点(gradient_checkpointing=True)
问题4:GPT 生成重复或低质量
- 调高 temperature(0.7-1.0)
- 设置 repetition_penalty=1.1-1.2
- 使用 top_p=0.9 替代 top_k
- 增加提示词的约束和格式说明模型对比
关键指标对比
| 对比维度 | BERT | GPT |
|---|---|---|
| 架构 | Transformer Encoder | Transformer Decoder |
| 注意力方向 | 双向(Bidirectional) | 因果/单向(Causal) |
| 预训练任务 | MLM + NSP | Next Token Prediction |
| 擅长任务 | 分类、NER、匹配、检索 | 生成、对话、代码、问答 |
| 推理方式 | 一次前向传播 | 逐 token 自回归生成 |
| 推理速度 | 快(单次推理) | 慢(生成越长越慢) |
| KV Cache | 不需要 | 需要(显存开销大) |
| 上下文长度 | 512 tokens | GPT-2: 1024, GPT-4: 128K |
| 规模效应 | 中等规模后饱和 | 持续提升(Scaling Law) |
模型变体演进
BERT 系列:
- BERT:基础版本,110M 参数
- RoBERTa:移除 NSP,更大 batch 和更长时间训练
- ALBERT:参数共享和矩阵分解,大幅减少参数
- DeBERTa:解耦注意力机制,效果优于 BERT
- macBERT:针对中文优化的 BERT 变体
GPT 系列:
- GPT-1:1.17 亿参数,验证了预训练-微调范式
- GPT-2:15 亿参数,展示了零样本生成能力
- GPT-3:1750 亿参数,涌现少样本学习能力
- GPT-4:多模态,推理能力大幅提升
优点
缺点
推理优化
GPT 推理中的 KV Cache
# 示例7:KV Cache 原理演示
import torch
seq_len = 10
hidden_dim = 64
num_layers = 4
# 不使用 KV Cache:每生成一个 token,都要重新计算之前所有 token 的 KV
# 时间复杂度:O(n^2)
# 使用 KV Cache:缓存已计算的 Key 和 Value,只需计算新 token 的 KV
# 时间复杂度:O(n)
kv_cache = {
layer: {
'key': torch.randn(1, seq_len, hidden_dim),
'value': torch.randn(1, seq_len, hidden_dim),
}
for layer in range(num_layers)
}
# 生成新 token 时,只需将新的 KV 追加到缓存中
new_key = torch.randn(1, 1, hidden_dim)
new_value = torch.randn(1, 1, hidden_dim)
kv_cache[0]['key'] = torch.cat([kv_cache[0]['key'], new_key], dim=1)
kv_cache[0]['value'] = torch.cat([kv_cache[0]['value'], new_value], dim=1)
print(f"KV Cache 更新后 key 形状: {kv_cache[0]['key'].shape}")模型量化部署
# 示例8:BERT INT8 量化(减少模型体积和推理延迟)
from transformers import BertTokenizer, BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained('bert-base-chinese', num_labels=2)
# 动态量化:将 Linear 层权重从 FP32 转为 INT8
quantized_model = torch.quantization.quantize_dynamic(
model,
{torch.nn.Linear}, # 只量化 Linear 层
dtype=torch.qint8
)
import os
def get_model_size(model):
torch.save(model.state_dict(), 'temp.pt')
size = os.path.getsize('temp.pt') / 1024 / 1024
os.remove('temp.pt')
return size
print(f"原始模型大小: {get_model_size(model):.2f} MB")
print(f"量化模型大小: {get_model_size(quantized_model):.2f} MB")总结
BERT 和 GPT 代表了 Transformer 的两个发展方向:编码器路线侧重理解,解码器路线侧重生成。在实际项目中,理解型任务优先选择 BERT 类模型,生成型任务优先选择 GPT 类模型,二者不是替代关系而是互补关系。对于资源受限的场景,可以使用 LoRA 等参数高效微调技术降低训练成本,使用 INT8 量化降低推理成本。
关键知识点
- BERT 的预训练任务包括 MLM(掩码语言模型)和 NSP(下一句预测),RoBERTa 移除了 NSP
- GPT 的预训练目标是下一个 token 预测(Causal LM),通过大规模数据和参数实现涌现能力
- In-Context Learning 和指令微调使 GPT 类模型无需梯度更新即可适配新任务
- BERT 的 [CLS] token 表示整句语义,[SEP] 用于分隔句子对
项目落地视角
- 分类/检索/匹配任务用 BERT/RoBERTa,生成/对话/摘要任务用 GPT/LLaMA
- 微调时冻结底层、只训练顶层是控制成本的有效策略
- 推理时 GPT 类模型需要管理 KV Cache,注意 max_length 和 batch_size 的显存约束
常见误区
- 拿 GPT 做分类任务不做适配——直接取 hidden state 效果不如 BERT
- 在 BERT 上做生成任务——BERT 的双向注意力不适合自回归生成
- 混淆预训练模型大小和微调数据量的关系——小模型+足够领域数据往往胜过大模型+少量数据
进阶路线
- 学习 RoBERTa、ALBERT、DeBERTa 等编码器改进模型的设计思路
- 深入理解 LLaMA、Mistral 等现代解码器模型的架构优化(GQA、SwiGLU、RoPE)
- 掌握 LoRA、QLoRA 等参数高效微调技术
- 探索 Encoder-Decoder 模型(T5、BART)在特定任务上的优势
适用场景
- BERT:文本分类、命名实体识别、语义匹配、信息检索
- GPT:文本生成、对话系统、代码生成、知识问答
- T5/BART:翻译、摘要等序列到序列任务
落地建议
- 优先尝试中文预训练模型(如 macBERT、ChatGLM),效果通常优于直接用英文模型
- 微调前先在目标数据上评估零样本和少样本基线,确定微调的必要性
- 部署时考虑模型量化(INT8/INT4)降低推理成本
排错清单
- BERT 微调后效果没提升:检查标签分布、数据量和学习率(通常 2e-5 ~ 5e-5)
- GPT 生成重复内容:调高 temperature 或增加 repetition_penalty
- 中英文混合文本效果差:检查 tokenizer 是否覆盖了目标语言的字符
复盘问题
- 你的任务是理解型还是生成型?选择 BERT 或 GPT 的依据是什么?
- 微调时冻结了多少层?是否对比了不同微调策略的效果?
- 在推理成本和效果之间,你的取舍标准是什么?
