从零开始,详解GPT、ChatGPT等大语言模型的训练全流程
目录
- 基础知识
- Transformer架构
- 注意力机制
- 位置编码
- 阶段一:预训练
- 阶段二:监督微调(SFT)
- 阶段三:奖励模型训练
- 阶段四:RLHF训练
- 完整代码示例
1. 基础知识
1.1 Transformer架构详解
什么是Transformer?
Transformer是现代大语言模型(GPT、BERT、LLaMA等)的核心架构,由Google在2017年提出。它完全抛弃了传统的循环神经网络(RNN)和卷积神经网络(CNN),仅使用注意力机制来处理序列数据。
重要说明:现代大语言模型(GPT系列、LLaMA等)不使用卷积层。卷积层主要用于计算机视觉模型(如ResNet、VGG)。大语言模型基于Transformer,使用的是自注意力机制。
Transformer整体架构
输入文本:"Hello World"
↓
[词嵌入 Embedding + 位置编码 Positional Encoding]
↓
┌─────────────────────────────────┐
│ 编码器(Encoder)- 可选 │
│ ┌───────────────────────────┐ │
│ │ 多头自注意力 │ │
│ │ (Multi-Head Attention) │ │
│ └───────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────┐ │
│ │ 前馈神经网络(FFN) │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ 解码器(Decoder) │
│ ┌───────────────────────────┐ │
│ │ 掩码多头自注意力 │ │
│ │ (Masked Attention) │ │
│ └───────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────┐ │
│ │ 前馈神经网络(FFN) │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
↓
[输出层 Linear + Softmax]
↓
输出:预测下一个词的概率分布
核心组件
1. 词嵌入(Token Embedding)
将每个词转换为固定维度的向量:
import torch.nn as nn
vocab_size = 50000 # 词汇表大小
d_model = 512 # 嵌入维度
embedding = nn.Embedding(vocab_size, d_model)
# 示例:将"Hello World"转换为向量
input_ids = torch.tensor([15496, 2159]) # token IDs
embedded = embedding(input_ids) # 形状: [2, 512]
2. 位置编码(Positional Encoding)
由于Transformer没有循环结构,需要添加位置信息:
公式:
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
代码实现:
import numpy as np
import torch
def get_positional_encoding(max_len, d_model):
"""生成位置编码"""
position = np.arange(max_len)[:, np.newaxis]
div_term = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
pe = np.zeros((max_len, d_model))
pe[:, 0::2] = np.sin(position * div_term) # 偶数维度
pe[:, 1::2] = np.cos(position * div_term) # 奇数维度
return torch.FloatTensor(pe)
# 生成位置编码
pos_encoding = get_positional_encoding(max_len=512, d_model=512)
1.2 自注意力机制(Self-Attention)
核心思想
让每个词都能"看到"句子中的所有其他词,并计算相关性。
例子:
句子: "The animal didn't cross the street because it was too tired."
问题: "it" 指的是什么?
注意力分布:
- "it" 对 "animal" 的注意力:0.8 (高)
- "it" 对 "street" 的注意力:0.1 (低)
数学原理
核心公式:
Attention(Q, K, V) = softmax(Q × K^T / √d_k) × V
三个关键矩阵:
- Q (Query):查询向量,"我想找什么信息?"
- K (Key):键向量,"我能提供什么信息?"
- V (Value):值向量,"实际的信息内容"
完整代码实现
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, dropout=0.1):
super().__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
# Q、K、V的线性变换
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)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
batch_size, seq_len, d_model = x.size()
# 1. 线性变换并分割成多个头
Q = self.W_Q(x).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
K = self.W_K(x).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
V = self.W_V(x).view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
# 2. 计算注意力分数
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
# 3. 应用掩码(防止看到未来信息)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
# 4. Softmax归一化
attention_weights = F.softmax(scores, dim=-1)
attention_weights = self.dropout(attention_weights)
# 5. 加权求和
attention_output = torch.matmul(attention_weights, V)
# 6. 合并多个头
attention_output = attention_output.transpose(1, 2).contiguous()
attention_output = attention_output.view(batch_size, seq_len, d_model)
# 7. 输出线性变换
output = self.W_O(attention_output)
return output
为什么使用多头注意力?
单个注意力头只能关注一种模式。多头注意力可以同时关注:
- 头1:语法关系(如主谓关系)
- 头2:语义关系(如同义词)
- 头3:位置关系(如相邻词)
2. 阶段一:预训练
2.1 什么是预训练?
预训练是大语言模型训练的第一阶段,在海量无标注文本上训练模型,让模型学习语言的基本规律。
训练目标:预测下一个词(Next Token Prediction)
示例:
输入: "The cat sat on the"
目标: 预测 "mat"
2.2 预训练流程
原始文本(维基百科、网页、书籍)
↓
[数据清洗]
↓
[分词Tokenization]
↓
[转换为Token ID]
↓
┌──────────────────────┐
│ Transformer模型 │
└──────────────────────┘
↓
[预测下一个词]
↓
[计算交叉熵损失]
↓
[反向传播更新参数]
↓
重复数百万步
↓
得到Base模型
2.3 完整预训练代码
"""
预训练完整示例
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torch.optim import AdamW
from transformers import GPT2LMHeadModel, GPT2Tokenizer, GPT2Config
from tqdm import tqdm
# ============ 1. 定义数据集 ============
class PretrainDataset(Dataset):
"""预训练数据集"""
def __init__(self, texts, tokenizer, max_length=512):
self.encodings = []
for text in texts:
tokens = tokenizer.encode(text, truncation=True, max_length=max_length)
if len(tokens) == max_length:
self.encodings.append(tokens)
def __len__(self):
return len(self.encodings)
def __getitem__(self, idx):
return {'input_ids': torch.tensor(self.encodings[idx])}
# ============ 2. 训练函数 ============
def train_epoch(model, dataloader, optimizer, device):
"""训练一个epoch"""
model.train()
total_loss = 0
for batch in tqdm(dataloader, desc="Training"):
input_ids = batch['input_ids'].to(device)
# 前向传播(目标就是输入本身)
outputs = model(input_ids=input_ids, labels=input_ids)
loss = outputs.loss
# 反向传播
optimizer.zero_grad()
loss.backward()
# 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 更新参数
optimizer.step()
total_loss += loss.item()
return total_loss / len(dataloader)
# ============ 3. 主训练流程 ============
def main():
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")
# 创建小模型(用于演示)
config = GPT2Config(
vocab_size=50257,
n_positions=512,
n_embd=256, # 嵌入维度
n_layer=6, # Transformer层数
n_head=8, # 注意力头数
)
model = GPT2LMHeadModel(config).to(device)
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# 打印模型信息
total_params = sum(p.numel() for p in model.parameters())
print(f"模型参数量: {total_params:,}")
# 准备训练数据
train_texts = [
"Machine learning is a subset of artificial intelligence. " * 50,
"Python is a popular programming language for data science. " * 50,
"Deep learning uses neural networks with multiple layers. " * 50,
] * 10
# 创建数据集
dataset = PretrainDataset(train_texts, tokenizer, max_length=128)
dataloader = DataLoader(dataset, batch_size=8, shuffle=True)
# 创建优化器
optimizer = AdamW(model.parameters(), lr=5e-4, weight_decay=0.01)
# 训练
num_epochs = 3
print(f"\n开始预训练 {num_epochs} 个epochs...")
for epoch in range(num_epochs):
avg_loss = train_epoch(model, dataloader, optimizer, device)
perplexity = math.exp(avg_loss)
print(f"Epoch {epoch+1}/{num_epochs}")
print(f" 平均损失: {avg_loss:.4f}")
print(f" 困惑度: {perplexity:.2f}")
# 保存模型
model.save_pretrained("./pretrained_model")
tokenizer.save_pretrained("./pretrained_model")
print("\n✓ 预训练完成!模型已保存")
if __name__ == "__main__":
main()
2.4 关键技术
1. 损失函数
# 交叉熵损失
def compute_loss(logits, labels):
"""
logits: [batch_size, seq_len, vocab_size]
labels: [batch_size, seq_len]
"""
# Shift操作:预测下一个词
shift_logits = logits[:, :-1, :].contiguous()
shift_labels = labels[:, 1:].contiguous()
loss = F.cross_entropy(
shift_logits.view(-1, shift_logits.size(-1)),
shift_labels.view(-1)
)
return loss
2. 学习率调度
from torch.optim.lr_scheduler import CosineAnnealingLR
# 余弦退火调度器
scheduler = CosineAnnealingLR(optimizer, T_max=num_epochs)
# 每个epoch后更新
scheduler.step()
3. 混合精度训练
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
# 训练循环
with autocast():
outputs = model(input_ids)
loss = outputs.loss
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
3. 阶段二:监督微调(SFT)
3.1 什么是SFT?
监督微调是大语言模型训练的第二阶段,使用高质量的指令-回答数据对预训练模型进行微调,让模型学会遵循人类指令。
对比:
| 阶段 | 预训练 | 监督微调 |
|---|---|---|
| 数据 | 海量无标注文本 | 高质量指令-回答对 |
| 目标 | 预测下一个词 | 生成符合指令的回答 |
| 数据量 | 数TB | 数GB |
| 结果 | Base模型 | Instruct模型 |
3.2 SFT数据格式
[
{
"instruction": "解释什么是机器学习",
"input": "",
"output": "机器学习是人工智能的一个分支,它使计算机系统能够从数据中学习并改进,而无需明确编程。主要分为监督学习、无监督学习和强化学习三大类。"
},
{
"instruction": "将下面的句子翻译成英文",
"input": "今天天气很好。",
"output": "The weather is very nice today."
}
]
3.3 SFT训练关键点
核心区别:只对回答部分计算损失,不对指令部分计算。
def create_labels(input_ids, response_start_idx):
"""
创建labels,只对回答部分计算损失
"""
labels = input_ids.clone()
labels[:response_start_idx] = -100 # 忽略指令部分
return labels
3.4 完整SFT代码
"""
监督微调(SFT)完整示例
"""
import torch
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torch.optim import AdamW
from transformers import GPT2LMHeadModel, GPT2Tokenizer
from tqdm import tqdm
# ============ 1. SFT数据集 ============
class InstructionDataset(Dataset):
"""指令微调数据集"""
def __init__(self, data, tokenizer, max_length=256):
self.data = data
self.tokenizer = tokenizer
self.max_length = max_length
def format_prompt(self, instruction, input_text, output_text):
"""格式化为prompt"""
if input_text:
prompt = f"""Instruction: {instruction}
Input: {input_text}
Response: {output_text}"""
else:
prompt = f"""Instruction: {instruction}
Response: {output_text}"""
return prompt
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
item = self.data[idx]
# 格式化完整文本
full_text = self.format_prompt(
item['instruction'],
item.get('input', ''),
item['output']
)
# Tokenize
encoding = self.tokenizer(
full_text,
truncation=True,
max_length=self.max_length,
padding='max_length',
return_tensors='pt'
)
input_ids = encoding['input_ids'].squeeze()
labels = input_ids.clone()
# 将padding token的label设为-100
labels[labels == self.tokenizer.pad_token_id] = -100
return {
'input_ids': input_ids,
'labels': labels
}
# ============ 2. 训练函数 ============
def train_sft(model, dataloader, optimizer, device, num_epochs=3):
"""SFT训练"""
model.train()
for epoch in range(num_epochs):
total_loss = 0
progress_bar = tqdm(dataloader, desc=f"Epoch {epoch+1}")
for batch in progress_bar:
input_ids = batch['input_ids'].to(device)
labels = batch['labels'].to(device)
# 前向传播
outputs = model(input_ids=input_ids, labels=labels)
loss = outputs.loss
# 反向传播
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
total_loss += loss.item()
progress_bar.set_postfix({'loss': f'{loss.item():.4f}'})
avg_loss = total_loss / len(dataloader)
print(f"Epoch {epoch+1} 平均损失: {avg_loss:.4f}")
# ============ 3. 主流程 ============
def main():
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 加载预训练模型
model = GPT2LMHeadModel.from_pretrained('gpt2').to(device)
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# 准备SFT数据
sft_data = [
{
"instruction": "解释什么是机器学习",
"input": "",
"output": "机器学习是人工智能的一个分支,使计算机能够从数据中学习并改进。"
},
{
"instruction": "翻译成英文",
"input": "今天天气很好。",
"output": "The weather is very nice today."
},
] * 50 # 重复以增加数据量
# 创建数据集
dataset = InstructionDataset(sft_data, tokenizer)
dataloader = DataLoader(dataset, batch_size=4, shuffle=True)
# 优化器(学习率比预训练小)
optimizer = AdamW(model.parameters(), lr=1e-5, weight_decay=0.01)
# 训练
print("开始监督微调...")
train_sft(model, dataloader, optimizer, device, num_epochs=3)
# 保存
model.save_pretrained("./sft_model")
print("\n✓ SFT完成!")
if __name__ == "__main__":
main()
3.5 最佳实践
- 数据质量 > 数据数量:1000条高质量数据胜过10000条低质量数据
- 防止过拟合:使用较小学习率(1e-5),少量epoch(1-3)
- 保留通用能力:混合70% SFT数据 + 30% 预训练数据
4. 阶段三:奖励模型训练
4.1 什么是奖励模型?
奖励模型是RLHF训练的核心组件,它学习人类的偏好,对模型生成的回答进行评分。
作用:
- 输入:问题 + 回答
- 输出:标量分数(越高越好)
示例:
问题: "解释量子力学"
回答A: "量子力学是物理学的一个分支..."(详细准确)
回答B: "量子力学很复杂。"(过于简单)
奖励模型(问题, 回答A) → 8.5分
奖励模型(问题, 回答B) → 3.2分
4.2 训练数据格式
{
"prompt": "解释什么是机器学习",
"chosen": "机器学习是人工智能的一个分支,它使计算机系统能够从数据中学习并改进...",
"rejected": "机器学习就是让机器学习。"
}
每条数据包含:
- Prompt(问题/指令)
- Chosen(更好的回答)
- Rejected(较差的回答)
4.3 奖励模型架构
import torch.nn as nn
from transformers import GPT2Model
class RewardModel(nn.Module):
"""奖励模型 - 对输入文本打分"""
def __init__(self, base_model_name='gpt2'):
super().__init__()
# 加载预训练的Transformer
self.transformer = GPT2Model.from_pretrained(base_model_name)
hidden_size = self.transformer.config.hidden_size
# 奖励头(输出标量分数)
self.reward_head = nn.Linear(hidden_size, 1, bias=False)
def forward(self, input_ids, attention_mask=None):
"""
返回: rewards [batch_size] 标量奖励分数
"""
# 通过Transformer
outputs = self.transformer(
input_ids=input_ids,
attention_mask=attention_mask
)
# 获取最后一个token的hidden state
last_hidden_states = outputs.last_hidden_state
# 找到最后一个非padding token的位置
if attention_mask is not None:
sequence_lengths = attention_mask.sum(dim=1) - 1
else:
sequence_lengths = input_ids.shape[1] - 1
# 提取最后一个token的表示
batch_size = input_ids.shape[0]
last_hidden = last_hidden_states[
torch.arange(batch_size, device=input_ids.device),
sequence_lengths
]
# 计算奖励分数
rewards = self.reward_head(last_hidden).squeeze(-1)
return rewards
4.4 训练损失函数
排序损失(Ranking Loss):
def compute_reward_loss(r_chosen, r_rejected):
"""
计算奖励模型损失
目标: r_chosen > r_rejected
参数:
r_chosen: [batch_size] chosen回答的奖励分数
r_rejected: [batch_size] rejected回答的奖励分数
"""
# 对数sigmoid损失
loss = -F.logsigmoid(r_chosen - r_rejected).mean()
return loss
数学公式:
Loss = -log(sigmoid(r_chosen - r_rejected))
4.5 完整训练代码
"""
奖励模型训练完整示例
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torch.optim import AdamW
from tqdm import tqdm
# ============ 1. 数据集 ============
class RewardDataset(Dataset):
"""奖励模型数据集"""
def __init__(self, data, tokenizer, max_length=256):
self.data = data
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
item = self.data[idx]
# Tokenize chosen和rejected
chosen_text = item['prompt'] + " " + item['chosen']
rejected_text = item['prompt'] + " " + item['rejected']
chosen_enc = self.tokenizer(
chosen_text,
truncation=True,
max_length=self.max_length,
padding='max_length',
return_tensors='pt'
)
rejected_enc = self.tokenizer(
rejected_text,
truncation=True,
max_length=self.max_length,
padding='max_length',
return_tensors='pt'
)
return {
'chosen_ids': chosen_enc['input_ids'].squeeze(),
'chosen_mask': chosen_enc['attention_mask'].squeeze(),
'rejected_ids': rejected_enc['input_ids'].squeeze(),
'rejected_mask': rejected_enc['attention_mask'].squeeze(),
}
# ============ 2. 训练函数 ============
def train_reward_model(model, dataloader, optimizer, device, num_epochs=3):
"""训练奖励模型"""
for epoch in range(num_epochs):
model.train()
total_loss = 0
total_accuracy = 0
progress_bar = tqdm(dataloader, desc=f"Epoch {epoch+1}")
for batch in progress_bar:
# 获取chosen和rejected的分数
r_chosen = model(
batch['chosen_ids'].to(device),
batch['chosen_mask'].to(device)
)
r_rejected = model(
batch['rejected_ids'].to(device),
batch['rejected_mask'].to(device)
)
# 计算损失
loss = -F.logsigmoid(r_chosen - r_rejected).mean()
# 计算准确率
accuracy = (r_chosen > r_rejected).float().mean()
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_loss += loss.item()
total_accuracy += accuracy.item()
progress_bar.set_postfix({
'loss': f'{loss.item():.4f}',
'acc': f'{accuracy.item():.2%}'
})
avg_loss = total_loss / len(dataloader)
avg_acc = total_accuracy / len(dataloader)
print(f"Epoch {epoch+1} - 损失: {avg_loss:.4f}, 准确率: {avg_acc:.2%}")
# ============ 3. 主流程 ============
def main():
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 创建奖励模型
reward_model = RewardModel('gpt2').to(device)
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# 准备数据
preference_data = [
{
'prompt': '解释什么是机器学习',
'chosen': '机器学习是人工智能的一个分支,使计算机能够从数据中学习...',
'rejected': '机器学习就是让机器学习。'
},
# ... 更多数据
] * 50
# 创建数据集
dataset = RewardDataset(preference_data, tokenizer)
dataloader = DataLoader(dataset, batch_size=4, shuffle=True)
# 优化器
optimizer = AdamW(reward_model.parameters(), lr=1e-5)
# 训练
print("开始训练奖励模型...")
train_reward_model(reward_model, dataloader, optimizer, device, num_epochs=3)
# 保存
torch.save(reward_model.state_dict(), "reward_model.pt")
print("\n✓ 奖励模型训练完成!")
if __name__ == "__main__":
main()
5. 阶段四:RLHF训练
5.1 什么是RLHF?
RLHF(Reinforcement Learning from Human Feedback)是大语言模型训练的第三阶段,使用强化学习算法,根据奖励模型的反馈优化模型。
核心思想:
模型生成回答 → 奖励模型打分 → 根据分数调整模型参数
5.2 RLHF训练循环
1. 输入Prompt: "解释量子力学"
↓
2. 策略模型生成回答
↓
3. 奖励模型评分: 7.5
↓
4. 计算优势函数: Advantage = 7.5 - baseline
↓
5. PPO算法优化
↓
重复1-5数千次
↓
得到对齐模型
5.3 PPO算法详解
PPO(Proximal Policy Optimization)是RLHF中最常用的强化学习算法。
核心思想
限制策略更新幅度,防止模型偏离太多。
PPO损失函数
def compute_ppo_loss(log_probs, old_log_probs, advantages, epsilon=0.2):
"""
PPO裁剪损失
参数:
log_probs: 新策略的对数概率
old_log_probs: 旧策略的对数概率
advantages: 优势函数值
epsilon: 裁剪范围(通常0.2)
"""
# 计算概率比
ratio = torch.exp(log_probs - old_log_probs)
# 未裁剪的损失
unclipped_loss = ratio * advantages
# 裁剪的损失
clipped_ratio = torch.clamp(ratio, 1 - epsilon, 1 + epsilon)
clipped_loss = clipped_ratio * advantages
# 取最小值(保守更新)
loss = -torch.min(unclipped_loss, clipped_loss).mean()
return loss
KL散度惩罚
防止模型偏离原始SFT模型太远:
def compute_kl_penalty(log_probs, ref_log_probs, beta=0.1):
"""
KL散度惩罚
参数:
log_probs: 当前模型的对数概率
ref_log_probs: 参考模型(SFT)的对数概率
beta: KL惩罚系数
"""
kl = (log_probs - ref_log_probs).mean()
return beta * kl
完整损失函数
Total Loss = PPO Loss + KL Penalty
5.4 RLHF关键组件
# 1. 策略模型(Policy Model)- 需要训练
policy_model = GPT2LMHeadModel.from_pretrained('sft-model')
# 2. 参考模型(Reference Model)- 冻结的SFT模型
ref_model = GPT2LMHeadModel.from_pretrained('sft-model')
ref_model.eval()
for param in ref_model.parameters():
param.requires_grad = False
# 3. 奖励模型(Reward Model)- 已训练好
reward_model = RewardModel.from_pretrained('reward-model')
reward_model.eval()
# 4. 价值模型(Value Model)- 预测奖励基线
value_model = ValueModel(...)
5.5 完整RLHF代码(简化版)
"""
RLHF训练完整示例(简化版)
"""
import torch
import torch.nn.functional as F
from transformers import GPT2LMHeadModel, GPT2Tokenizer
from tqdm import tqdm
# ============ 1. 生成函数 ============
@torch.no_grad()
def generate_responses(model, tokenizer, prompts, max_new_tokens=50, device='cpu'):
"""
生成回答并返回log概率
"""
model.eval()
responses = []
log_probs = []
for prompt in prompts:
input_ids = tokenizer.encode(prompt, return_tensors='pt').to(device)
generated_ids = []
generated_log_probs = []
current_ids = input_ids
for _ in range(max_new_tokens):
outputs = model(current_ids)
logits = outputs.logits[:, -1, :]
# 采样
probs = F.softmax(logits, dim=-1)
next_token = torch.multinomial(probs, num_samples=1)
# 记录log prob
log_prob = F.log_softmax(logits, dim=-1)
selected_log_prob = log_prob[0, next_token[0]]
generated_ids.append(next_token[0].item())
generated_log_probs.append(selected_log_prob)
current_ids = torch.cat([current_ids, next_token], dim=1)
if next_token[0].item() == tokenizer.eos_token_id:
break
response = tokenizer.decode(generated_ids, skip_special_tokens=True)
responses.append(response)
log_probs.append(torch.stack(generated_log_probs))
return responses, log_probs
# ============ 2. RLHF训练步骤 ============
def rlhf_training_step(
policy_model,
ref_model,
reward_model,
tokenizer,
prompts,
optimizer,
device
):
"""执行一步RLHF训练"""
# 1. 生成回答
responses, old_log_probs = generate_responses(
policy_model, tokenizer, prompts, device=device
)
# 2. 计算奖励
rewards = []
for prompt, response in zip(prompts, responses):
text = prompt + " " + response
input_ids = tokenizer.encode(text, return_tensors='pt').to(device)
reward = reward_model(input_ids)
rewards.append(reward.item())
rewards = torch.tensor(rewards, device=device)
# 3. 计算优势(简化版:直接使用奖励)
advantages = rewards
advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)
# 4. PPO更新
policy_model.train()
# 重新计算log probs(需要梯度)
new_log_probs = []
for prompt, response in zip(prompts, responses):
# 这里需要完整实现,简化省略...
pass
# 计算PPO损失
# ppo_loss = compute_ppo_loss(new_log_probs, old_log_probs, advantages)
# 计算KL惩罚
# kl_loss = compute_kl_penalty(new_log_probs, ref_log_probs)
# total_loss = ppo_loss + kl_loss
# 反向传播
# optimizer.zero_grad()
# total_loss.backward()
# optimizer.step()
return {
'reward_mean': rewards.mean().item(),
'reward_std': rewards.std().item(),
}
# ============ 3. 主训练流程 ============
def main():
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 加载模型
policy_model = GPT2LMHeadModel.from_pretrained('gpt2').to(device)
ref_model = GPT2LMHeadModel.from_pretrained('gpt2').to(device)
ref_model.eval()
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
# 简化:使用随机奖励模型
class DummyRewardModel:
def __call__(self, input_ids):
return torch.randn(1) * 2 + 5
reward_model = DummyRewardModel()
# 优化器
optimizer = torch.optim.AdamW(policy_model.parameters(), lr=1e-6)
# 训练prompts
prompts = [
"解释什么是机器学习",
"什么是深度学习?",
"描述Python编程语言",
]
# 训练循环
num_iterations = 20
print("开始RLHF训练...")
for iteration in range(num_iterations):
stats = rlhf_training_step(
policy_model, ref_model, reward_model,
tokenizer, prompts, optimizer, device
)
print(f"Iteration {iteration+1}/{num_iterations}")
print(f" 平均奖励: {stats['reward_mean']:.2f}")
# 保存
policy_model.save_pretrained("./rlhf_model")
print("\n✓ RLHF训练完成!")
if __name__ == "__main__":
main()
5.6 RLHF关键挑战
1. 奖励黑客(Reward Hacking)
问题:模型学会利用奖励模型的漏洞获得高分
解决:
- KL惩罚:限制偏离程度
- 奖励归一化和裁剪
- 使用多个奖励模型
2. 训练不稳定
解决:
- 使用很小的学习率(1e-6)
- 梯度裁剪
- 较小的epsilon(0.1-0.2)
6. 完整代码示例
6.1 端到端训练流程
"""
完整的端到端大语言模型训练流程
包含:预训练 → SFT → 奖励模型 → RLHF
"""
import torch
import torch.nn as nn
from transformers import GPT2LMHeadModel, GPT2Tokenizer, GPT2Config
from torch.utils.data import Dataset, DataLoader
from torch.optim import AdamW
from tqdm import tqdm
class CompleteLLMTraining:
"""完整的LLM训练流程"""
def __init__(self, device='cpu'):
self.device = device
self.tokenizer = None
self.model = None
# ===== 阶段1: 预训练 =====
def pretrain(self, texts, num_epochs=3):
"""预训练阶段"""
print("\n" + "="*80)
print("阶段1: 预训练")
print("="*80)
# 创建模型
config = GPT2Config(
vocab_size=50257,
n_embd=256,
n_layer=4,
n_head=4
)
self.model = GPT2LMHeadModel(config).to(self.device)
self.tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
if self.tokenizer.pad_token is None:
self.tokenizer.pad_token = self.tokenizer.eos_token
print(f"模型参数量: {sum(p.numel() for p in self.model.parameters()):,}")
# 训练(代码省略,参考前面章节)
print("✓ 预训练完成")
# ===== 阶段2: 监督微调 =====
def supervised_finetune(self, instruction_data, num_epochs=2):
"""监督微调阶段"""
print("\n" + "="*80)
print("阶段2: 监督微调")
print("="*80)
# 训练(代码省略)
print("✓ 监督微调完成")
# ===== 阶段3: 训练奖励模型 =====
def train_reward_model(self, preference_data, num_epochs=3):
"""训练奖励模型"""
print("\n" + "="*80)
print("阶段3: 训练奖励模型")
print("="*80)
# 训练(代码省略)
print("✓ 奖励模型训练完成")
# ===== 阶段4: RLHF训练 =====
def rlhf_training(self, prompts, num_iterations=10):
"""RLHF训练"""
print("\n" + "="*80)
print("阶段4: RLHF训练")
print("="*80)
# 训练(代码省略)
print("✓ RLHF训练完成")
# ===== 生成函数 =====
@torch.no_grad()
def generate(self, prompt, max_length=100):
"""生成文本"""
self.model.eval()
input_ids = self.tokenizer.encode(prompt, return_tensors='pt').to(self.device)
output_ids = self.model.generate(
input_ids,
max_length=max_length,
temperature=0.7,
top_p=0.9,
do_sample=True,
pad_token_id=self.tokenizer.eos_token_id
)
return self.tokenizer.decode(output_ids[0], skip_special_tokens=True)
# ===== 保存模型 =====
def save(self, path):
"""保存模型"""
self.model.save_pretrained(path)
self.tokenizer.save_pretrained(path)
print(f"✓ 模型已保存到: {path}")
# ===== 主流程 =====
def main():
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print("大语言模型端到端训练流程")
trainer = CompleteLLMTraining(device=device)
# 1. 预训练数据
pretrain_texts = [
"Machine learning is a subset of AI. " * 20,
] * 10
# 2. SFT数据
sft_data = [
{
"instruction": "解释机器学习",
"output": "机器学习是AI的一个分支..."
}
] * 10
# 3. 偏好数据
preference_data = [
{
"prompt": "什么是AI?",
"chosen": "人工智能是模拟人类智能的技术...",
"rejected": "AI就是人工智能。"
}
] * 10
# 4. RLHF prompts
rlhf_prompts = ["什么是机器学习?", "解释深度学习"]
# 执行完整流程
trainer.pretrain(pretrain_texts, num_epochs=2)
trainer.supervised_finetune(sft_data, num_epochs=2)
trainer.train_reward_model(preference_data, num_epochs=2)
trainer.rlhf_training(rlhf_prompts, num_iterations=5)
# 测试生成
print("\n最终模型测试:")
response = trainer.generate("什么是人工智能?", max_length=80)
print(f"Response: {response}")
# 保存
trainer.save("./final_model")
print("\n✓ 完整训练流程结束!")
if __name__ == "__main__":
main()
6.2 模型推理
"""
训练好的模型推理示例
"""
import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer
class LLMInference:
"""大语言模型推理"""
def __init__(self, model_path, device='cpu'):
self.device = device
self.model = GPT2LMHeadModel.from_pretrained(model_path).to(device)
self.tokenizer = GPT2Tokenizer.from_pretrained(model_path)
if self.tokenizer.pad_token is None:
self.tokenizer.pad_token = self.tokenizer.eos_token
self.model.eval()
@torch.no_grad()
def generate(self, prompt, max_length=100, temperature=0.7):
"""生成文本"""
input_ids = self.tokenizer.encode(prompt, return_tensors='pt').to(self.device)
output_ids = self.model.generate(
input_ids,
max_length=max_length,
temperature=temperature,
top_p=0.9,
do_sample=True,
pad_token_id=self.tokenizer.pad_token_id
)
return self.tokenizer.decode(output_ids[0], skip_special_tokens=True)
@torch.no_grad()
def chat(self, instruction, max_length=150):
"""对话式生成"""
prompt = f"Instruction: {instruction}\nResponse:"
response = self.generate(prompt, max_length=max_length)
if "Response:" in response:
response = response.split("Response:")[-1].strip()
return response
# 使用示例
def main():
# 加载模型
inference = LLMInference('./final_model', device='cpu')
# 测试生成
prompts = [
"解释什么是机器学习",
"列出三种编程语言",
"写一首关于春天的诗"
]
for prompt in prompts:
response = inference.chat(prompt)
print(f"\n指令: {prompt}")
print(f"回答: {response}")
print("-" * 80)
if __name__ == "__main__":
main()
总结
完整训练流程回顾
┌──────────────┐
│ 1. 预训练 │ 海量文本 → Base模型
│ │ 目标:学习语言基础
└──────┬───────┘
↓
┌──────────────┐
│ 2. 监督微调 │ 指令-回答对 → Instruct模型
│ (SFT) │ 目标:遵循指令
└──────┬───────┘
↓
┌──────────────┐
│ 3. 奖励模型 │ 人类偏好对比 → 评分器
│ │ 目标:学习人类偏好
└──────┬───────┘
↓
┌──────────────┐
│ 4. RLHF │ 强化学习 → 对齐模型
│ (PPO) │ 目标:符合人类偏好
└──────────────┘
↓
ChatGPT级别的模型
关键要点
Transformer架构
- 不使用卷积层,使用自注意力机制
- 核心公式:
Attention(Q,K,V) = softmax(QK^T/√d_k)V - 位置编码:
PE = sin/cos(pos/10000^(2i/d_model))
预训练
- 目标:下一个词预测
- 损失:交叉熵损失
- 数据:海量无标注文本
- 学习率:5e-4
监督微调(SFT)
- 目标:遵循指令
- 数据:指令-回答对
- 关键:只对回答部分计算损失
- 学习率:1e-5(比预训练小)
奖励模型
- 目标:评估回答质量
- 损失:
-log(sigmoid(r_chosen - r_rejected)) - 数据:人类偏好对比
RLHF
- 算法:PPO
- 损失:PPO损失 + KL惩罚
- 学习率:1e-6(最小)
- 关键:限制更新幅度
常见问题
Q: 需要多少GPU?
- 学习示例:1个GPU或CPU
- 小模型(1B):1-8个GPU
- 大模型(70B+):数百个GPU
Q: 训练需要多长时间?
- 小模型示例:数小时
- 实际应用:数周到数月
Q: 如何获取训练数据?
- 预训练:Common Crawl、Wikipedia
- SFT:Alpaca、ShareGPT等开源数据集
- RLHF:需要人工标注偏好
Q: 显存不足怎么办?
- 减小批次大小
- 减小序列长度
- 使用梯度累积
- 使用LoRA等参数高效方法
进阶学习资源
论文
- Attention Is All You Need (Transformer)
- Language Models are Few-Shot Learners (GPT-3)
- Training language models to follow instructions (InstructGPT)
开源项目
- Hugging Face Transformers
- TRL (Transformer Reinforcement Learning)
- LLaMA, Alpaca
在线课程
- Stanford CS224N
- Hugging Face Course
- DeepLearning.AI
7. 主流大模型具体训练案例
7.1 GPT-3 训练案例
模型信息:
- 发布时间:2020年
- 开发公司:OpenAI
- 参数量:175B(1750亿)
- 架构:仅解码器(Decoder-only)Transformer
训练配置
# GPT-3 (175B) 配置
gpt3_config = {
# 模型架构
'n_layers': 96, # Transformer层数
'n_heads': 96, # 注意力头数
'd_model': 12288, # 嵌入维度
'd_ff': 49152, # FFN中间层维度 (4 * d_model)
'vocab_size': 50257, # 词汇表大小
'context_length': 2048, # 上下文长度
# 训练超参数
'batch_size': 3200000, # 批次大小(token数)
'learning_rate': 0.6e-4, # 学习率
'weight_decay': 0.1, # 权重衰减
'gradient_clip': 1.0, # 梯度裁剪
# 训练数据
'training_tokens': 300e9, # 300B tokens
'training_data': [
'Common Crawl (filtered): 60%',
'WebText2: 22%',
'Books1: 8%',
'Books2: 8%',
'Wikipedia: 3%'
]
}
训练流程
阶段1: 预训练
├── 数据准备
│ ├── Common Crawl: 410B tokens → 过滤后 60%
│ ├── WebText2: 高质量网页数据
│ ├── Books1 + Books2: 书籍语料
│ └── Wikipedia: 英文维基百科
│
├── 训练细节
│ ├── 训练时间: 数月
│ ├── 硬件: 数千个V100 GPU
│ ├── 混合精度: FP16训练
│ └── 优化器: Adam (β1=0.9, β2=0.95, ε=10^-8)
│
└── 结果
└── GPT-3 Base模型(能生成流畅文本,但不遵循指令)
代码示例(简化版)
"""
GPT-3 风格的预训练(简化版)
"""
from transformers import GPT2Config, GPT2LMHeadModel, GPT2Tokenizer
import torch
from torch.optim import AdamW
# GPT-3 Small配置(用于演示,实际GPT-3是175B)
config = GPT2Config(
vocab_size=50257,
n_positions=2048, # GPT-3的上下文长度
n_embd=768, # 简化版,实际是12288
n_layer=12, # 简化版,实际是96
n_head=12, # 简化版,实际是96
n_inner=3072, # 简化版,实际是49152
)
model = GPT2LMHeadModel(config)
print(f"模型参数量: {sum(p.numel() for p in model.parameters()):,}")
# 优化器配置(与GPT-3相同)
optimizer = AdamW(
model.parameters(),
lr=0.6e-4,
betas=(0.9, 0.95),
eps=1e-8,
weight_decay=0.1
)
# 训练循环(伪代码)
"""
for step in range(300_000_000_000 // batch_size): # 300B tokens
# 1. 从混合数据集采样
batch = sample_batch(
common_crawl=0.60,
webtext2=0.22,
books=0.16,
wikipedia=0.03
)
# 2. 前向传播
outputs = model(input_ids=batch, labels=batch)
loss = outputs.loss
# 3. 反向传播
loss.backward()
# 4. 梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
# 5. 更新参数
optimizer.step()
optimizer.zero_grad()
# 6. 学习率调度(余弦退火)
scheduler.step()
"""
关键技术
- 数据过滤:Common Crawl质量参差不齐,需要严格过滤
- 混合精度训练:使用FP16节省显存和加速计算
- 模型并行:175B参数无法放入单个GPU,使用张量并行和流水线并行
- 梯度累积:模拟超大批次
7.2 ChatGPT 训练案例
模型信息:
- 发布时间:2022年11月
- 开发公司:OpenAI
- 基础模型:GPT-3.5(优化版GPT-3)
- 训练方法:SFT + RLHF
完整训练流程
步骤1: 基础模型(GPT-3.5)
└── 从GPT-3改进而来,增强代码和推理能力
步骤2: 监督微调(SFT)
├── 数据收集
│ ├── 人工标注员编写高质量对话
│ ├── 涵盖各种任务类型
│ └── 约13,000条训练样本
│
├── 训练配置
│ ├── 学习率: 9.65e-6
│ ├── Epoch: 1-2
│ ├── Batch size: 32
│ └── 训练时间: 数天
│
└── 结果
└── GPT-3.5-instruct(能遵循指令)
步骤3: 奖励模型训练
├── 数据收集
│ ├── 对同一prompt生成多个回答
│ ├── 标注员对回答排序
│ └── 约33,000条比较数据
│
├── 模型架构
│ ├── 基于GPT-3.5
│ ├── 移除语言建模头
│ └── 添加标量输出头
│
└── 训练
└── 使用Ranking Loss训练
步骤4: RLHF(PPO)
├── 训练配置
│ ├── 学习率: 1.41e-6
│ ├── PPO epochs: 4
│ ├── KL系数: 0.2
│ └── 训练迭代: 数千次
│
├── 关键技术
│ ├── PPO算法优化策略
│ ├── KL散度约束防止偏离
│ └── 价值函数估计优势
│
└── 结果
└── ChatGPT(对齐人类偏好)
SFT数据示例
{
"prompt": "如何学习编程?",
"response": "学习编程可以遵循以下步骤:\n\n1. **选择一门语言**:对于初学者,推荐Python或JavaScript\n2. **学习基础语法**:变量、循环、函数等\n3. **动手实践**:通过小项目巩固知识\n4. **阅读文档**:学会查阅官方文档\n5. **参与开源**:贡献代码提升技能\n\n记住,编程需要持续练习,不要害怕犯错!"
}
RLHF训练代码(简化版)
"""
ChatGPT风格的RLHF训练(简化版)
"""
from transformers import GPT2LMHeadModel, GPT2Tokenizer
import torch
# 1. 加载SFT模型作为初始策略
policy_model = GPT2LMHeadModel.from_pretrained('gpt3.5-sft')
ref_model = GPT2LMHeadModel.from_pretrained('gpt3.5-sft') # 参考模型(冻结)
# 2. 加载训练好的奖励模型
reward_model = torch.load('reward_model.pt')
# 3. RLHF训练配置
rlhf_config = {
'learning_rate': 1.41e-6,
'ppo_epochs': 4,
'kl_coef': 0.2,
'clip_range': 0.2,
'value_loss_coef': 1.0,
'batch_size': 256,
}
# 4. 训练循环(伪代码)
"""
for iteration in range(num_iterations):
# 采样prompts
prompts = sample_prompts()
# 生成回答
responses = policy_model.generate(prompts)
# 计算奖励
rewards = reward_model(prompts + responses)
# 计算KL散度(与参考模型)
kl_div = compute_kl(policy_model, ref_model, prompts, responses)
# PPO优化
advantages = compute_advantages(rewards, values)
ppo_loss = compute_ppo_loss(policy_model, old_policy, advantages)
total_loss = ppo_loss + kl_coef * kl_div
# 更新
total_loss.backward()
optimizer.step()
"""
7.3 LLaMA 训练案例
模型信息:
- 发布时间:2023年2月
- 开发公司:Meta AI
- 参数量:7B, 13B, 33B, 65B
- 特点:开源、高效、仅预训练
训练配置(LLaMA-65B)
llama_65b_config = {
# 模型架构
'n_layers': 80,
'n_heads': 64,
'd_model': 8192,
'd_ff': 22016,
'vocab_size': 32000, # 使用BPE tokenizer
'context_length': 2048,
# 训练数据(1.4T tokens)
'training_data': {
'CommonCrawl': '67%',
'C4': '15%',
'Github': '4.5%',
'Wikipedia': '4.5%',
'Books': '4.5%',
'ArXiv': '2.5%',
'StackExchange': '2%'
},
# 训练配置
'batch_size': 4_000_000, # 4M tokens
'learning_rate': 1.5e-4,
'weight_decay': 0.1,
'gradient_clip': 1.0,
'warmup_steps': 2000,
}
LLaMA的改进技术
"""
LLaMA相比GPT-3的改进
"""
# 1. Pre-normalization (GPT-3使用Post-norm)
class LLaMATransformerBlock(nn.Module):
def forward(self, x):
# Pre-norm: 在子层之前归一化
x = x + self.attention(self.norm1(x))
x = x + self.ffn(self.norm2(x))
return x
# GPT-3使用Post-norm
class GPT3TransformerBlock(nn.Module):
def forward(self, x):
# Post-norm: 在子层之后归一化
x = self.norm1(x + self.attention(x))
x = self.norm2(x + self.ffn(x))
return x
# 2. SwiGLU激活函数(替代ReLU)
class SwiGLU(nn.Module):
def forward(self, x):
x, gate = x.chunk(2, dim=-1)
return F.silu(gate) * x
# 3. RoPE位置编码(替代绝对位置编码)
class RotaryPositionalEmbedding(nn.Module):
"""旋转位置编码"""
def forward(self, q, k):
# 应用旋转矩阵
q = self.apply_rotary_emb(q, self.sin, self.cos)
k = self.apply_rotary_emb(k, self.sin, self.cos)
return q, k
训练代码示例
"""
LLaMA风格的预训练
"""
from transformers import LlamaConfig, LlamaForCausalLM, LlamaTokenizer
# LLaMA-7B配置
config = LlamaConfig(
vocab_size=32000,
hidden_size=4096,
intermediate_size=11008,
num_hidden_layers=32,
num_attention_heads=32,
max_position_embeddings=2048,
)
model = LlamaForCausalLM(config)
tokenizer = LlamaTokenizer.from_pretrained('meta-llama/Llama-7b')
# 训练数据准备
"""
LLaMA使用高质量、多样化的数据:
- CommonCrawl: 网页数据(经过严格过滤)
- C4: Colossal Clean Crawled Corpus
- Github: 开源代码
- Wikipedia: 多语言百科
- Books: 图书语料
- ArXiv: 学术论文
- StackExchange: 问答数据
"""
# 训练循环
optimizer = torch.optim.AdamW(
model.parameters(),
lr=1.5e-4,
betas=(0.9, 0.95),
weight_decay=0.1
)
# 余弦学习率调度
from torch.optim.lr_scheduler import CosineAnnealingLR
scheduler = CosineAnnealingLR(optimizer, T_max=num_training_steps)
"""
训练1.4T tokens,约需要:
- LLaMA-7B: 82,432 GPU小时(A100)
- LLaMA-13B: 135,168 GPU小时
- LLaMA-65B: 1,022,362 GPU小时
"""
7.4 Claude 训练案例
模型信息:
- 发布时间:2023年
- 开发公司:Anthropic
- 特点:Constitutional AI(宪法AI)
- 训练方法:RLHF + CAI
Constitutional AI (CAI) 流程
传统RLHF的问题:
└── 依赖大量人工标注,成本高
Constitutional AI的解决方案:
├── 步骤1: 生成回答
│ └── 对有害prompt生成回答
│
├── 步骤2: 自我批评
│ ├── 使用"宪法原则"评估回答
│ ├── 原则示例:
│ │ - "请选择最无害的回答"
│ │ - "请选择最有帮助的回答"
│ │ - "请避免歧视性内容"
│ └── 模型自己生成改进建议
│
├── 步骤3: 自我修正
│ └── 根据批评重新生成更好的回答
│
├── 步骤4: 训练奖励模型
│ ├── 使用AI生成的偏好数据
│ └── 而非人工标注
│
└── 步骤5: RLHF
└── 使用AI训练的奖励模型进行RL优化
Constitutional AI示例
"""
Constitutional AI训练流程(概念演示)
"""
# 定义宪法原则
CONSTITUTION = [
"请选择最有帮助和无害的回答",
"避免使用歧视性或冒犯性语言",
"尊重用户隐私",
"提供准确和可验证的信息",
"承认不确定性,避免误导",
]
class ConstitutionalAI:
def __init__(self, base_model):
self.model = base_model
self.constitution = CONSTITUTION
def generate_critique(self, prompt, response):
"""让模型自我批评"""
critique_prompt = f"""
原始问题: {prompt}
模型回答: {response}
请根据以下原则评估这个回答:
{self.constitution[0]}
批评:"""
critique = self.model.generate(critique_prompt)
return critique
def revise_response(self, prompt, response, critique):
"""根据批评修正回答"""
revision_prompt = f"""
原始问题: {prompt}
原始回答: {response}
批评: {critique}
请根据批评改进回答:"""
revised_response = self.model.generate(revision_prompt)
return revised_response
def train_with_cai(self, prompts):
"""Constitutional AI训练循环"""
preference_data = []
for prompt in prompts:
# 1. 生成初始回答
initial_response = self.model.generate(prompt)
# 2. 自我批评
critique = self.generate_critique(prompt, initial_response)
# 3. 自我修正
revised_response = self.revise_response(prompt, initial_response, critique)
# 4. 构建偏好数据(修正版 > 初始版)
preference_data.append({
'prompt': prompt,
'chosen': revised_response,
'rejected': initial_response
})
# 5. 训练奖励模型
reward_model = self.train_reward_model(preference_data)
# 6. RLHF训练
self.rlhf_train(reward_model)
return self.model
# 使用示例
cai_trainer = ConstitutionalAI(base_model)
trained_model = cai_trainer.train_with_cai(prompts)
Claude的优势
- 减少人工标注:使用AI自我评估和改进
- 更好的对齐:通过宪法原则确保价值观一致
- 可解释性:明确的原则列表
- 可扩展性:容易添加新的原则
7.5 训练成本对比
| 模型 | 参数量 | 训练数据 | GPU时间 | 估算成本 |
|---|---|---|---|---|
| GPT-3 | 175B | 300B tokens | ~3640万GPU小时 | $460万 |
| LLaMA-65B | 65B | 1.4T tokens | ~100万GPU小时 | $200万 |
| LLaMA-7B | 7B | 1T tokens | ~8.2万GPU小时 | $16万 |
| GPT-4 | 未公开 | 未公开 | 估计>1亿GPU小时 | >$6000万 |
成本计算(基于A100 GPU):
- A100租用成本:约$2-3/小时
- LLaMA-7B:82,432小时 × 206,000
7.6 实践建议
对于个人学习者
# 推荐配置:小型模型用于学习
small_model_config = {
'parameters': '125M - 1B',
'training_data': '10-100GB',
'hardware': '1-4个GPU (RTX 3090 / V100)',
'training_time': '数天到数周',
'cost': '$100 - $1000',
}
# 建议
recommendations = {
'1. 使用预训练模型': 'GPT-2, LLaMA-7B等开源模型',
'2. 参数高效微调': 'LoRA, Adapter等技术',
'3. 使用小数据集': 'Alpaca (52k), Dolly (15k)',
'4. 云GPU服务': 'Google Colab, AWS, Vast.ai',
}
对于研究团队
# 推荐配置:中等模型用于研究
research_model_config = {
'parameters': '3B - 13B',
'training_data': '100GB - 1TB',
'hardware': '8-64个GPU (A100)',
'training_time': '数周到数月',
'cost': '$10,000 - $100,000',
}
# 技术栈
tech_stack = {
'framework': 'PyTorch + DeepSpeed / Megatron',
'distributed': 'DDP, FSDP, 模型并行',
'optimization': '混合精度, 梯度检查点',
'monitoring': 'WandB, TensorBoard',
}
对于企业应用
# 推荐配置:大模型用于生产
production_model_config = {
'parameters': '30B - 70B+',
'training_data': '1TB+',
'hardware': '数百个A100/H100',
'training_time': '数月',
'cost': '$100万+',
}
# 考虑因素
considerations = {
'数据质量': '投入大量资源进行数据清洗和过滤',
'安全性': '内容过滤、偏见检测',
'可扩展性': '分布式训练、推理优化',
'合规性': '隐私保护、版权问题',
}
7.7 关键经验总结
数据是关键
GPT-3教训: Common Crawl质量参差不齐
├── 解决方案: 严格过滤和去重
└── LLaMA改进: 使用更高质量的数据混合
ChatGPT教训: SFT数据质量 > 数量
├── 只用13k条高质量对话
└── 效果远超大规模低质量数据
Claude创新: Constitutional AI
└── 减少人工标注依赖
训练技巧
-
学习率调度至关重要
- 预训练:warmup + 余弦退火
- SFT:恒定小学习率
- RLHF:极小学习率
-
批次大小影响收敛
- 大批次:更稳定,但需要调整学习率
- 小批次:更快迭代,但更不稳定
- 梯度累积:模拟大批次
-
数据混合策略
- GPT-3:60% Common Crawl + 其他
- LLaMA:更均衡的数据分布
- 定期调整混合比例
-
评估很重要
- 不要只看训练loss
- 定期进行人工评估
- 使用多样化的测试集
这些案例展示了从GPT-3到ChatGPT、LLaMA、Claude等主流大模型的真实训练过程!
祝学习愉快!🚀
如有问题,欢迎查阅各个章节的详细代码和说明。