Transformer实现细节
基础实现框架
使用Hugging Face Transformers
Hugging Face Transformers是最流行的Transformer实现库,提供了丰富的预训练模型和便捷的API。
from transformers import AutoModel, AutoTokenizer
import torch
# 加载预训练模型
model = AutoModel.from_pretrained("bert-base-uncased")
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
# 基本推理
inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
核心组件实现
1. 多头注意力机制
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super().__init__()
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def forward(self, query, key, value, mask=None):
batch_size = query.size(0)
# 1. 计算Q, K, V
Q = self.W_q(query)
K = self.W_k(key)
V = self.W_v(value)
# 2. 重塑为多头
Q = Q.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
K = K.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
V = V.view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
# 3. 计算注意力
attention_output = self.scaled_dot_product_attention(Q, K, V, mask)
# 4. 拼接头
attention_output = attention_output.transpose(1, 2).contiguous().view(
batch_size, -1, self.d_model
)
# 5. 最终线性变换
return self.W_o(attention_output)
def scaled_dot_product_attention(self, Q, K, V, mask=None):
d_k = Q.size(-1)
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)
attention_weights = F.softmax(scores, dim=-1)
return torch.matmul(attention_weights, V)
2. 位置编码
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() *
(-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0).transpose(0, 1)
self.register_buffer('pe', pe)
def forward(self, x):
return x + self.pe[:x.size(0), :]
3. 前馈网络
class FeedForward(nn.Module):
def __init__(self, d_model, d_ff, dropout=0.1):
super().__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.linear2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
return self.linear2(self.dropout(F.relu(self.linear1(x))))
4. 编码器层
class EncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super().__init__()
self.self_attention = MultiHeadAttention(d_model, num_heads)
self.feed_forward = FeedForward(d_model, d_ff, dropout)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# 自注意力 + 残差连接 + 层归一化
attn_output = self.self_attention(x, x, x, mask)
x = self.norm1(x + self.dropout(attn_output))
# 前馈网络 + 残差连接 + 层归一化
ff_output = self.feed_forward(x)
x = self.norm2(x + self.dropout(ff_output))
return x
训练实现
数据预处理
from transformers import BertTokenizer
from torch.utils.data import Dataset, DataLoader
class TextDataset(Dataset):
def __init__(self, texts, tokenizer, max_length=512):
self.texts = texts
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = self.texts[idx]
encoding = self.tokenizer(
text,
truncation=True,
padding='max_length',
max_length=self.max_length,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].flatten(),
'attention_mask': encoding['attention_mask'].flatten()
}
# 创建数据加载器
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
dataset = TextDataset(texts, tokenizer)
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
模型训练
import torch.optim as optim
from transformers import get_linear_schedule_with_warmup
def train_model(model, dataloader, num_epochs=3):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
model.train()
optimizer = optim.AdamW(model.parameters(), lr=2e-5)
total_steps = len(dataloader) * num_epochs
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=0,
num_training_steps=total_steps
)
for epoch in range(num_epochs):
total_loss = 0
for batch in dataloader:
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
optimizer.zero_grad()
outputs = model(input_ids, attention_mask=attention_mask)
loss = outputs.loss
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
scheduler.step()
total_loss += loss.item()
avg_loss = total_loss / len(dataloader)
print(f'Epoch {epoch+1}/{num_epochs}, Average Loss: {avg_loss:.4f}')
推理实现
文本分类
from transformers import AutoModelForSequenceClassification
import torch.nn.functional as F
def classify_text(text, model, tokenizer):
model.eval()
with torch.no_grad():
inputs = tokenizer(text, return_tensors='pt', truncation=True, padding=True)
outputs = model(**inputs)
predictions = F.softmax(outputs.logits, dim=-1)
return predictions.numpy()
# 使用示例
model = AutoModelForSequenceClassification.from_pretrained('bert-base-uncased')
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
text = "This is a great movie!"
predictions = classify_text(text, model, tokenizer)
print(f"Predictions: {predictions}")
文本生成
from transformers import GPT2LMHeadModel, GPT2Tokenizer
def generate_text(prompt, model, tokenizer, max_length=100):
model.eval()
with torch.no_grad():
inputs = tokenizer.encode(prompt, return_tensors='pt')
outputs = model.generate(
inputs,
max_length=max_length,
temperature=0.7,
do_sample=True,
pad_token_id=tokenizer.eos_token_id
)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
# 使用示例
model = GPT2LMHeadModel.from_pretrained('gpt2')
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
prompt = "Once upon a time"
generated_text = generate_text(prompt, model, tokenizer)
print(f"Generated text: {generated_text}")
优化技巧
1. Flash Attention
from transformers import AutoModel
# 使用Flash Attention 2
model = AutoModel.from_pretrained(
"bert-base-uncased",
attn_implementation="flash_attention_2"
)
2. 混合精度训练
from torch.cuda.amp import autocast, GradScaler
def train_with_mixed_precision(model, dataloader):
scaler = GradScaler()
for batch in dataloader:
optimizer.zero_grad()
with autocast():
outputs = model(**batch)
loss = outputs.loss
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
3. 梯度检查点
import torch.utils.checkpoint as checkpoint
class CheckpointedTransformerLayer(nn.Module):
def __init__(self, layer):
super().__init__()
self.layer = layer
def forward(self, x, mask=None):
return checkpoint.checkpoint(self.layer, x, mask)
4. 模型并行
# 使用DeepSpeed进行模型并行
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir='./results',
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
num_train_epochs=3,
warmup_steps=500,
weight_decay=0.01,
logging_dir='./logs',
deepspeed="ds_config.json" # DeepSpeed配置文件
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
)
调试和监控
1. 梯度监控
def monitor_gradients(model):
for name, param in model.named_parameters():
if param.requires_grad and param.grad is not None:
grad_norm = param.grad.norm()
print(f"{name}: {grad_norm:.4f}")
2. 注意力可视化
import matplotlib.pyplot as plt
import seaborn as sns
def visualize_attention(model, tokenizer, text):
inputs = tokenizer(text, return_tensors='pt')
outputs = model(**inputs, output_attentions=True)
# 获取注意力权重
attentions = outputs.attentions
# 可视化第一层的注意力
attention = attentions[0][0].detach().numpy()
plt.figure(figsize=(10, 8))
sns.heatmap(attention, cmap='Blues')
plt.title('Attention Weights')
plt.show()
3. 模型性能分析
import time
import torch.profiler
def profile_model(model, inputs):
with torch.profiler.profile(
activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA],
schedule=torch.profiler.schedule(wait=1, warmup=1, active=3, repeat=2),
on_trace_ready=torch.profiler.tensorboard_trace_handler('./log/profiler'),
record_shapes=True,
profile_memory=True,
with_stack=True
) as prof:
for _ in range(10):
model(**inputs)
prof.step()
部署实现
1. 模型量化
from transformers import AutoModelForSequenceClassification
import torch
# 动态量化
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased")
quantized_model = torch.quantization.quantize_dynamic(
model, {torch.nn.Linear}, dtype=torch.qint8
)
2. ONNX导出
import torch.onnx
def export_to_onnx(model, tokenizer, output_path):
# 创建示例输入
text = "This is a test sentence"
inputs = tokenizer(text, return_tensors='pt')
# 导出模型
torch.onnx.export(
model,
tuple(inputs.values()),
output_path,
export_params=True,
opset_version=11,
do_constant_folding=True,
input_names=['input_ids', 'attention_mask'],
output_names=['output'],
dynamic_axes={'input_ids': {0: 'batch_size'},
'attention_mask': {0: 'batch_size'},
'output': {0: 'batch_size'}}
)
3. TensorRT优化
from transformers import pipeline
import tensorrt as trt
# 使用TensorRT优化 的推理
pipe = pipeline(
"text-classification",
model="bert-base-uncased",
accelerator="tensorrt"
)
常见问题和解决方案
1. 内存不足
# 使用梯度累积
accumulation_steps = 4
for i, batch in enumerate(dataloader):
outputs = model(**batch)
loss = outputs.loss / accumulation_steps
loss.backward()
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
2. 训练不稳定
# 使用更保守的学习率和warmup
optimizer = optim.AdamW(model.parameters(), lr=1e-5, eps=1e-8)
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=int(0.1 * total_steps),
num_training_steps=total_steps
)
3. 推理速度慢
# 使用torch.jit.script优化
@torch.jit.script
def optimized_forward(input_ids, attention_mask):
return model(input_ids, attention_mask)
总结
Transformer的实现涉及多个关键组件:
- 核心架构:注意力机制、位置编码、前馈网络
- 训练优化:混合精度、梯度累积、学习率调度
- 推理优化:模型量化、ONNX导出、TensorRT加速
- 调试工具:梯度监控、注意力可视化、性能分析
掌握这些实现细节对于深入理解和使用Transformer模型至关重要。