Skip to content

预训练范式演变 - 从BERT到ChatGPT的训练策略

发布时间:2024-09-10
作者:AI技术研究者
标签:预训练, BERT, GPT, 训练策略, 自监督学习

前言

如果说Transformer是大模型的"硬件基础",那么预训练范式就是大模型的"软件系统"。从2018年BERT的横空出世,到2022年ChatGPT的现象级成功,预训练技术经历了从理念创新到工程突破的完整演进。作为一个深度参与这个过程的研究者,我见证了每一次范式转变带来的震撼。

我记得第一次看到BERT在11个NLP任务上全面刷榜时的惊讶,也记得GPT-3展现出few-shot能力时的兴奋,更记得ChatGPT发布后整个AI圈的沸腾。每一次突破都不是偶然,而是预训练技术不断演进的必然结果。今天,让我们深入探讨这个改变AI历史的技术范式。

预训练的理论基础

迁移学习的启发

预训练的核心思想来源于迁移学习,但在深度学习时代得到了全新的诠释:

传统迁移学习

特征工程时代:
- 手工设计特征
- 在不同任务间复用特征
- 依赖领域专家知识
- 泛化能力有限

深度学习早期:
- 在ImageNet上预训练CNN
- 微调到特定视觉任务
- 证明了预训练的有效性
- 但主要限于监督学习

自监督预训练的突破

核心洞察:
- 语言本身包含丰富的监督信号
- 无需人工标注即可学习表示
- 大规模无标注数据容易获得
- 学到的表示具有通用性

技术优势:
- 数据规模:无标注数据远超标注数据
- 成本效益:避免昂贵的人工标注
- 通用性:一个模型适用多个任务
- 可扩展性:支持大规模模型训练

语言模型的演进

统计语言模型时代

N-gram模型:
- P(w_i | w_{i-n+1}, ..., w_{i-1})
- 基于词频统计
- 数据稀疏问题严重
- 无法捕获长距离依赖

神经语言模型:
- 词向量表示
- 循环神经网络
- 缓解数据稀疏问题
- 但计算效率低

Transformer时代的语言模型

技术突破:
- 并行化训练
- 长距离依赖建模
- 可扩展到大规模
- 为预训练奠定基础

预训练目标:
- 自回归语言模型(GPT系列)
- 掩码语言模型(BERT系列)
- 序列到序列(T5系列)

BERT:双向编码的革命

技术创新

2018年10月,Google发布BERT(Bidirectional Encoder Representations from Transformers),开启了预训练的新时代。

核心创新

双向上下文:
- 同时利用左右上下文信息
- 突破传统语言模型的单向限制
- 更好地理解语言语义

掩码语言模型(MLM):
- 随机掩盖输入中的部分词汇
- 预测被掩盖的词汇
- 学习双向语言表示

下一句预测(NSP):
- 判断两个句子是否连续
- 学习句子间的关系
- 适用于句子对任务

预训练过程

python
def bert_pretraining_loss(model, input_ids, attention_mask, labels):
    """
    BERT预训练损失函数
    """
    # 掩码语言模型损失
    mlm_outputs = model(
        input_ids=input_ids,
        attention_mask=attention_mask,
        labels=labels['mlm_labels']
    )
    mlm_loss = mlm_outputs.loss
    
    # 下一句预测损失
    nsp_outputs = model(
        input_ids=input_ids,
        attention_mask=attention_mask,
        next_sentence_label=labels['nsp_labels']
    )
    nsp_loss = nsp_outputs.loss
    
    # 总损失
    total_loss = mlm_loss + nsp_loss
    return total_loss

def create_masked_lm_predictions(tokens, masked_lm_prob=0.15):
    """
    创建掩码语言模型的训练数据
    """
    cand_indexes = []
    for i, token in enumerate(tokens):
        if token == "[CLS]" or token == "[SEP]":
            continue
        cand_indexes.append(i)
    
    random.shuffle(cand_indexes)
    num_to_predict = min(max(1, int(round(len(tokens) * masked_lm_prob))),
                        len(cand_indexes))
    
    masked_lms = []
    covered_indexes = set()
    
    for index in cand_indexes:
        if len(masked_lms) >= num_to_predict:
            break
        if index in covered_indexes:
            continue
        
        covered_indexes.add(index)
        
        # 80%的时间替换为[MASK]
        if random.random() < 0.8:
            masked_token = "[MASK]"
        else:
            # 10%的时间保持原词
            if random.random() < 0.5:
                masked_token = tokens[index]
            # 10%的时间替换为随机词
            else:
                masked_token = random.choice(vocab_list)
        
        masked_lms.append({
            "index": index,
            "label": tokens[index]
        })
        tokens[index] = masked_token
    
    return tokens, masked_lms

微调策略

BERT的成功很大程度上归功于其优雅的微调策略:

任务适配方法

分类任务:
- 在[CLS]位置添加分类头
- 微调整个模型参数
- 适用于情感分析、文本分类等

序列标注:
- 在每个token位置添加标注头
- 预测每个token的标签
- 适用于命名实体识别、词性标注等

句子对任务:
- 利用[SEP]分隔两个句子
- 在[CLS]位置进行分类
- 适用于自然语言推理、问答等

抽取式问答:
- 预测答案的起始和结束位置
- 两个线性层分别预测start和end
- 适用于阅读理解任务

微调技巧

python
def bert_fine_tuning(model, train_dataloader, num_epochs=3):
    """
    BERT微调训练循环
    """
    # 使用较小的学习率
    optimizer = AdamW(model.parameters(), lr=2e-5)
    
    # 学习率预热
    total_steps = len(train_dataloader) * num_epochs
    scheduler = get_linear_schedule_with_warmup(
        optimizer,
        num_warmup_steps=int(0.1 * total_steps),
        num_training_steps=total_steps
    )
    
    model.train()
    for epoch in range(num_epochs):
        for batch in train_dataloader:
            optimizer.zero_grad()
            
            outputs = model(
                input_ids=batch['input_ids'],
                attention_mask=batch['attention_mask'],
                labels=batch['labels']
            )
            
            loss = outputs.loss
            loss.backward()
            
            # 梯度裁剪
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
            
            optimizer.step()
            scheduler.step()

影响与局限

BERT的影响

技术影响:
- 在11个NLP任务上达到SOTA
- 证明了预训练+微调范式的有效性
- 推动了预训练模型的发展
- 成为NLP的新基线

产业影响:
- 降低了NLP应用的门槛
- 推动了NLP技术的产业化
- 催生了大量基于BERT的应用
- 改变了NLP研究的方向

BERT的局限

生成能力不足:
- 主要适用于理解类任务
- 生成任务表现一般
- 无法进行开放式生成

计算效率问题:
- 双向注意力计算复杂度高
- 推理速度相对较慢
- 不适合实时应用

预训练目标限制:
- MLM目标与生成任务不匹配
- NSP任务的有效性存疑
- 预训练和微调存在差异

GPT:自回归生成的崛起

技术路线

OpenAI选择了与BERT完全不同的技术路线:

自回归语言模型

核心思想:
- 从左到右生成文本
- 每个位置只能看到之前的信息
- 符合自然的文本生成过程

预训练目标:
- 最大化似然估计
- P(x) = ∏P(x_i | x_1, ..., x_{i-1})
- 简单而有效的目标函数

架构选择:
- 仅使用Transformer Decoder
- 因果掩码注意力
- 单向信息流动

GPT-1的探索

python
def gpt_pretraining_loss(model, input_ids, attention_mask):
    """
    GPT预训练损失函数
    """
    # 输入和标签错位一个位置
    inputs = input_ids[:, :-1]
    labels = input_ids[:, 1:]
    
    outputs = model(
        input_ids=inputs,
        attention_mask=attention_mask[:, :-1]
    )
    
    # 计算交叉熵损失
    loss_fct = CrossEntropyLoss()
    loss = loss_fct(
        outputs.logits.view(-1, outputs.logits.size(-1)),
        labels.view(-1)
    )
    
    return loss

def gpt_generation(model, prompt, max_length=100, temperature=1.0):
    """
    GPT文本生成
    """
    model.eval()
    input_ids = tokenizer.encode(prompt, return_tensors='pt')
    
    with torch.no_grad():
        for _ in range(max_length):
            outputs = model(input_ids)
            logits = outputs.logits[:, -1, :] / temperature
            probs = F.softmax(logits, dim=-1)
            next_token = torch.multinomial(probs, 1)
            input_ids = torch.cat([input_ids, next_token], dim=-1)
            
            if next_token.item() == tokenizer.eos_token_id:
                break
    
    return tokenizer.decode(input_ids[0])

GPT-2的突破

GPT-2展现了规模化的力量:

Zero-shot能力的涌现

任务格式化:
- 将任务描述为文本生成
- 通过prompt引导模型行为
- 无需任务特定的微调

能力表现:
- 阅读理解:在CoQA上达到55 F1
- 摘要生成:生成连贯的摘要
- 翻译任务:英法翻译达到可用水平
- 问答系统:回答常识性问题

In-context Learning的雏形

学习机制:
- 在输入中提供示例
- 模型从示例中学习模式
- 无需参数更新

示例格式:
Input: "Translate English to French:
Hello → Bonjour
Goodbye → Au revoir
Thank you → "

Output: "Merci"

GPT-3的飞跃

GPT-3将规模化推向极致:

Few-shot Learning

python
def few_shot_prompting(model, task_description, examples, query):
    """
    Few-shot学习示例
    """
    prompt = task_description + "\n\n"
    
    # 添加示例
    for example in examples:
        prompt += f"Input: {example['input']}\n"
        prompt += f"Output: {example['output']}\n\n"
    
    # 添加查询
    prompt += f"Input: {query}\n"
    prompt += "Output:"
    
    # 生成回答
    response = model.generate(prompt, max_length=50)
    return response

# 使用示例
examples = [
    {"input": "I love this movie!", "output": "Positive"},
    {"input": "This film is terrible.", "output": "Negative"},
    {"input": "Not bad, could be better.", "output": "Neutral"}
]

result = few_shot_prompting(
    model=gpt3,
    task_description="Classify the sentiment of the following text:",
    examples=examples,
    query="This movie is amazing!"
)

涌现能力

数学推理:
- 基本算术运算
- 简单的代数问题
- 逻辑推理

代码生成:
- 多种编程语言
- 算法实现
- 代码解释

创意写作:
- 故事创作
- 诗歌生成
- 剧本编写

知识问答:
- 广泛的知识覆盖
- 常识推理
- 事实查询

InstructGPT/ChatGPT:对齐的突破

RLHF技术

2022年,OpenAI发布InstructGPT,引入了RLHF(Reinforcement Learning from Human Feedback)技术:

三阶段训练流程

阶段1:监督微调(SFT)
- 收集高质量的指令-回答对
- 在GPT-3基础上进行监督微调
- 学习遵循指令的基本能力

阶段2:奖励模型训练(RM)
- 收集人类偏好数据
- 训练奖励模型预测人类偏好
- 将人类价值观编码到模型中

阶段3:强化学习优化(PPO)
- 使用PPO算法优化策略
- 最大化奖励模型的分数
- 平衡性能和对齐

技术实现

python
def train_reward_model(model, preference_data):
    """
    训练奖励模型
    """
    optimizer = AdamW(model.parameters(), lr=1e-5)
    
    for batch in preference_data:
        chosen_inputs = batch['chosen']
        rejected_inputs = batch['rejected']
        
        # 计算奖励分数
        chosen_rewards = model(chosen_inputs)
        rejected_rewards = model(rejected_inputs)
        
        # 偏好损失
        loss = -torch.log(torch.sigmoid(chosen_rewards - rejected_rewards)).mean()
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

def ppo_training(policy_model, reward_model, prompts):
    """
    PPO强化学习训练
    """
    for prompt_batch in prompts:
        # 生成回答
        responses = policy_model.generate(prompt_batch)
        
        # 计算奖励
        rewards = reward_model(prompt_batch, responses)
        
        # 计算优势函数
        advantages = compute_advantages(rewards)
        
        # PPO更新
        policy_loss = compute_policy_loss(
            policy_model, prompt_batch, responses, advantages
        )
        
        optimizer.zero_grad()
        policy_loss.backward()
        optimizer.step()

对齐目标

HHH原则

Helpful(有用性):
- 能够帮助用户解决问题
- 提供准确和相关的信息
- 理解用户的真实意图

Harmless(无害性):
- 避免生成有害内容
- 拒绝不当请求
- 保护用户和社会安全

Honest(诚实性):
- 承认知识的局限性
- 不编造虚假信息
- 表达不确定性

效果提升

指令遵循能力:
- 更好地理解和执行指令
- 减少无关或错误的回答
- 提高任务完成质量

对话连贯性:
- 保持多轮对话的一致性
- 理解上下文和历史信息
- 自然的对话流程

安全性改善:
- 显著减少有害内容生成
- 更好的内容过滤
- 提高用户信任度

预训练数据的演进

数据规模的增长

数据规模趋势

GPT-1: 5GB (BooksCorpus)
GPT-2: 40GB (WebText)
GPT-3: 570GB (Common Crawl + Books + Wikipedia)
PaLM: 780GB (高质量网页、书籍、代码)
GPT-4: 估计数TB级别

数据质量的提升

数据清洗技术

python
def data_quality_filtering(text):
    """
    数据质量过滤
    """
    # 长度过滤
    if len(text) < 100 or len(text) > 100000:
        return False
    
    # 语言检测
    if detect_language(text) != 'en':
        return False
    
    # 重复内容检测
    if is_duplicate(text):
        return False
    
    # 质量评分
    quality_score = compute_quality_score(text)
    if quality_score < 0.7:
        return False
    
    # 有害内容过滤
    if contains_harmful_content(text):
        return False
    
    return True

def compute_quality_score(text):
    """
    计算文本质量分数
    """
    score = 0.0
    
    # 语法正确性
    score += grammar_score(text) * 0.3
    
    # 信息密度
    score += information_density(text) * 0.3
    
    # 可读性
    score += readability_score(text) * 0.2
    
    # 事实准确性
    score += factual_accuracy(text) * 0.2
    
    return score

数据多样性

数据来源多样化:
- 网页内容:Common Crawl
- 书籍文献:Project Gutenberg, OpenLibrary
- 学术论文:arXiv, PubMed
- 代码仓库:GitHub
- 百科全书:Wikipedia
- 新闻文章:各大新闻网站

领域覆盖:
- 科学技术
- 人文社科
- 艺术文学
- 商业金融
- 日常生活

数据去重技术

精确去重

python
def exact_deduplication(documents):
    """
    精确去重
    """
    seen = set()
    unique_docs = []
    
    for doc in documents:
        doc_hash = hashlib.md5(doc.encode()).hexdigest()
        if doc_hash not in seen:
            seen.add(doc_hash)
            unique_docs.append(doc)
    
    return unique_docs

近似去重

python
def fuzzy_deduplication(documents, threshold=0.8):
    """
    基于MinHash的近似去重
    """
    from datasketch import MinHashLSH, MinHash
    
    lsh = MinHashLSH(threshold=threshold, num_perm=128)
    minhashes = {}
    
    for i, doc in enumerate(documents):
        m = MinHash(num_perm=128)
        for word in doc.split():
            m.update(word.encode('utf8'))
        
        # 检查是否有近似重复
        result = lsh.query(m)
        if not result:
            lsh.insert(i, m)
            minhashes[i] = doc
    
    return list(minhashes.values())

训练技术的进步

分布式训练

数据并行

python
def data_parallel_training(model, train_dataloader, num_gpus):
    """
    数据并行训练
    """
    model = nn.DataParallel(model, device_ids=list(range(num_gpus)))
    model = model.cuda()
    
    optimizer = AdamW(model.parameters(), lr=1e-4)
    
    for batch in train_dataloader:
        batch = {k: v.cuda() for k, v in batch.items()}
        
        optimizer.zero_grad()
        outputs = model(**batch)
        loss = outputs.loss
        loss.backward()
        optimizer.step()

模型并行

python
class ModelParallelTransformer(nn.Module):
    def __init__(self, config):
        super().__init__()
        self.num_layers = config.num_layers
        self.layers_per_device = self.num_layers // torch.cuda.device_count()
        
        # 将层分布到不同GPU
        self.layers = nn.ModuleList()
        for i in range(self.num_layers):
            device = i // self.layers_per_device
            layer = TransformerLayer(config).to(f'cuda:{device}')
            self.layers.append(layer)
    
    def forward(self, x):
        for i, layer in enumerate(self.layers):
            device = i // self.layers_per_device
            x = x.to(f'cuda:{device}')
            x = layer(x)
        return x

混合精度训练

python
def mixed_precision_training(model, train_dataloader):
    """
    混合精度训练
    """
    model = model.cuda()
    optimizer = AdamW(model.parameters(), lr=1e-4)
    scaler = torch.cuda.amp.GradScaler()
    
    for batch in train_dataloader:
        batch = {k: v.cuda() for k, v in batch.items()}
        
        optimizer.zero_grad()
        
        # 使用autocast进行前向传播
        with torch.cuda.amp.autocast():
            outputs = model(**batch)
            loss = outputs.loss
        
        # 缩放损失并反向传播
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update()

梯度累积

python
def gradient_accumulation_training(model, train_dataloader, accumulation_steps=4):
    """
    梯度累积训练
    """
    optimizer = AdamW(model.parameters(), lr=1e-4)
    
    for i, batch in enumerate(train_dataloader):
        outputs = model(**batch)
        loss = outputs.loss / accumulation_steps
        loss.backward()
        
        if (i + 1) % accumulation_steps == 0:
            optimizer.step()
            optimizer.zero_grad()

评估与基准测试

内在评估

困惑度(Perplexity)

python
def compute_perplexity(model, test_dataloader):
    """
    计算困惑度
    """
    model.eval()
    total_loss = 0
    total_tokens = 0
    
    with torch.no_grad():
        for batch in test_dataloader:
            outputs = model(**batch)
            loss = outputs.loss
            
            # 计算有效token数量
            attention_mask = batch['attention_mask']
            num_tokens = attention_mask.sum().item()
            
            total_loss += loss.item() * num_tokens
            total_tokens += num_tokens
    
    avg_loss = total_loss / total_tokens
    perplexity = math.exp(avg_loss)
    return perplexity

外在评估

GLUE基准

任务类型:
- CoLA: 语法可接受性判断
- SST-2: 情感分析
- MRPC: 释义检测
- STS-B: 语义相似度
- QQP: 问题对等价性
- MNLI: 自然语言推理
- QNLI: 问答自然语言推理
- RTE: 文本蕴含识别
- WNLI: 代词消歧

SuperGLUE基准

更具挑战性的任务:
- BoolQ: 布尔问答
- CB: 承诺银行
- COPA: 常识因果推理
- MultiRC: 多句阅读理解
- ReCoRD: 阅读理解与常识推理
- RTE: 文本蕴含识别
- WiC: 上下文中的词义
- WSC: 代词消歧挑战

未来发展趋势

预训练目标的创新

自监督学习新方法

对比学习:
- SimCLR, MoCo等方法
- 学习更好的表示
- 减少对标注数据的依赖

掩码自编码:
- MAE (Masked Autoencoder)
- 重建被掩盖的内容
- 学习更丰富的表示

多模态预训练:
- CLIP, DALL-E等
- 跨模态表示学习
- 统一的多模态理解

效率优化

模型压缩

知识蒸馏:
- 大模型指导小模型
- 保持性能的同时减少参数
- 适合部署到边缘设备

剪枝技术:
- 结构化剪枝
- 非结构化剪枝
- 动态剪枝

量化技术:
- 权重量化
- 激活量化
- 混合精度量化

长上下文建模

技术方向

稀疏注意力:
- Longformer, BigBird
- 减少注意力计算复杂度
- 支持更长的序列

分层注意力:
- Hierarchical Transformer
- 多尺度信息处理
- 提高长序列建模能力

记忆机制:
- Transformer-XL
- 外部记忆模块
- 持续学习能力

总结

预训练范式的演进是AI发展史上的重要篇章,它不仅改变了NLP领域,也为整个AI技术的发展指明了方向:

技术演进脉络

  • BERT:双向编码,理解为王
  • GPT:自回归生成,规模制胜
  • InstructGPT/ChatGPT:人类对齐,价值导向

核心技术突破

  • 自监督学习:无需标注数据的表示学习
  • 规模化定律:更大的模型,更强的能力
  • 人类反馈:将人类价值观融入AI系统

工程技术进步

  • 分布式训练:支持大规模模型训练
  • 数据工程:高质量数据的重要性
  • 评估体系:全面的模型评估框架

关键启示

  1. 数据是基础:高质量、大规模的数据是成功的前提
  2. 规模是关键:模型规模的增长带来能力的跃迁
  3. 对齐是方向:技术发展必须与人类价值观对齐
  4. 工程是保障:强大的工程能力支撑技术突破
  5. 评估是指南:科学的评估体系指导技术发展

预训练技术的发展还在继续,我们正站在通用人工智能的门槛上。未来的预训练技术将更加高效、更加智能、更加安全,为人类社会带来更大的价值。


相关文章推荐:

想了解更多预训练技术的实现细节,欢迎关注后续文章!