BERT与GPT实战:预训练语言模型应用指南

4 阅读1分钟

在前面的章节中,我们深入学习了自注意力机制和Transformer架构,这些是现代预训练语言模型的基础。BERT和GPT作为最具代表性的预训练语言模型,分别采用了不同的训练策略,在各类自然语言处理任务中取得了突破性成果。

BERT(Bidirectional Encoder Representations from Transformers)采用双向编码器结构,通过掩码语言模型和下一句预测任务进行预训练。而GPT(Generative Pre-trained Transformer)采用解码器-only结构,通过自回归语言模型进行预训练。

本节将通过实战项目,带你深入了解BERT和GPT的原理、特点以及在实际任务中的应用方法。

BERT与GPT的核心差异

架构差异

graph TD
    A[BERT] --> B[双向Transformer编码器]
    C[GPT] --> D[单向Transformer解码器]
    
    style A fill:#2a9d8f,stroke:#333
    style B fill:#2a9d8f,stroke:#333
    style C fill:#e76f51,stroke:#333
    style D fill:#e76f51,stroke:#333

训练目标差异

  1. BERT

    • 掩码语言模型(Masked Language Model, MLM):随机掩盖输入中的一些词,让模型预测被掩盖的词
    • 下一句预测(Next Sentence Prediction, NSP):预测两个句子是否为连续的句子
  2. GPT

    • 自回归语言模型:从左到右预测下一个词

应用场景差异

  1. BERT:更适合理解任务,如文本分类、问答、命名实体识别等
  2. GPT:更适合生成任务,如文本生成、对话系统等

BERT详解与实战

BERT的核心创新

BERT的主要创新在于双向编码和预训练+微调的范式:

  1. 双向编码:通过掩码机制,BERT能够同时利用一个词左右两侧的上下文信息
  2. 预训练+微调:在大规模语料上预训练,然后在特定任务上微调

BERT的输入表示

BERT的输入由三部分组成:

  1. Token Embeddings:词嵌入
  2. Segment Embeddings:句子嵌入(A句或B句)
  3. Position Embeddings:位置嵌入
graph LR
    A[Token Embeddings] --> D[输入表示]
    B[Segment Embeddings] --> D
    C[Position Embeddings] --> D
    
    style A fill:#a8dadc
    style B fill:#457b9d
    style C fill:#f4a261
    style D fill:#e76f51

BERT实战:文本分类任务

让我们使用Hugging Face Transformers库来实践BERT在文本分类任务中的应用:

import torch
from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from torch.utils.data import DataLoader, Dataset
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
import pandas as pd

# 检查CUDA是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# 创建示例数据集
class TextDataset(Dataset):
    def __init__(self, texts, labels, tokenizer, max_length=128):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_length = max_length
    
    def __len__(self):
        return len(self.texts)
    
    def __getitem__(self, idx):
        text = str(self.texts[idx])
        label = self.labels[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(),
            'labels': torch.tensor(label, dtype=torch.long)
        }

# 创建示例数据
sample_texts = [
    "这部电影真是太棒了,演员表演出色,剧情紧凑。",
    "这个产品质量很差,完全不值得购买。",
    "今天的天气真好,适合出去散步。",
    "服务态度恶劣,再也不会来了。",
    "这本书内容丰富,值得一读。",
    "交通拥堵严重,上班迟到了。",
    "新产品功能强大,用户体验很好。",
    "餐厅环境脏乱,食物也不新鲜。"
]

sample_labels = [1, 0, 1, 0, 1, 0, 1, 0]  # 1表示正面情感,0表示负面情感

# 加载预训练的BERT模型和分词器
model_name = 'bert-base-chinese'  # 使用中文BERT模型
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
model.to(device)

# 创建数据集和数据加载器
dataset = TextDataset(sample_texts, sample_labels, tokenizer)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

# 设置优化器
optimizer = AdamW(model.parameters(), lr=2e-5)

# 训练函数
def train_model(model, dataloader, optimizer, epochs=3):
    model.train()
    for epoch in range(epochs):
        total_loss = 0
        for batch in dataloader:
            # 将数据移到GPU
            input_ids = batch['input_ids'].to(device)
            attention_mask = batch['attention_mask'].to(device)
            labels = batch['labels'].to(device)
            
            # 清零梯度
            optimizer.zero_grad()
            
            # 前向传播
            outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels)
            loss = outputs.loss
            
            # 反向传播
            loss.backward()
            optimizer.step()
            
            total_loss += loss.item()
        
        avg_loss = total_loss / len(dataloader)
        print(f"Epoch {epoch+1}/{epochs}, Average Loss: {avg_loss:.4f}")

# 训练模型
print("开始训练BERT模型...")
train_model(model, dataloader, optimizer)

# 评估函数
def evaluate_model(model, texts, labels, tokenizer):
    model.eval()
    predictions = []
    
    with torch.no_grad():
        for text in texts:
            inputs = tokenizer(text, return_tensors='pt', truncation=True, 
                             padding='max_length', max_length=128).to(device)
            
            outputs = model(**inputs)
            predicted_class = torch.argmax(outputs.logits, dim=1).item()
            predictions.append(predicted_class)
    
    accuracy = accuracy_score(labels, predictions)
    print(f"Accuracy: {accuracy:.4f}")
    print("\nClassification Report:")
    print(classification_report(labels, predictions, target_names=['Negative', 'Positive']))
    
    return predictions

# 评估模型
print("\n评估模型性能...")
test_texts = [
    "这个产品非常好用,推荐购买。",
    "服务太差了,浪费时间。",
    "今天心情不错,阳光明媚。"
]
test_labels = [1, 0, 1]

predictions = evaluate_model(model, test_texts, test_labels, tokenizer)

GPT详解与实战

GPT的核心特点

GPT系列模型的主要特点是:

  1. 单向语言模型:只能利用左侧上下文信息
  2. 生成能力强:擅长文本生成任务
  3. 零样本和少样本学习:能够在没有微调的情况下执行新任务

GPT实战:文本生成任务

让我们使用GPT模型进行文本生成任务:

from transformers import GPT2LMHeadModel, GPT2Tokenizer
import torch

# 检查CUDA是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# 加载预训练的GPT-2模型和分词器(使用英文模型)
model_name = 'gpt2'
tokenizer = GPT2Tokenizer.from_pretrained(model_name)
model = GPT2LMHeadModel.from_pretrained(model_name)
model.to(device)

# 设置pad_token
tokenizer.pad_token = tokenizer.eos_token

# 文本生成函数
def generate_text(prompt, max_length=100, num_return_sequences=1):
    # 编码输入文本
    input_ids = tokenizer.encode(prompt, return_tensors='pt').to(device)
    
    # 生成文本
    with torch.no_grad():
        outputs = model.generate(
            input_ids,
            max_length=max_length,
            num_return_sequences=num_return_sequences,
            temperature=0.7,
            pad_token_id=tokenizer.eos_token_id,
            do_sample=True,
            top_k=50,
            top_p=0.95,
        )
    
    # 解码生成的文本
    generated_texts = []
    for output in outputs:
        generated_text = tokenizer.decode(output, skip_special_tokens=True)
        generated_texts.append(generated_text)
    
    return generated_texts

# 示例文本生成
prompts = [
    "The future of artificial intelligence",
    "Once upon a time",
    "In a world where robots can think"
]

print("使用GPT-2生成文本:")
for prompt in prompts:
    print(f"\nPrompt: {prompt}")
    generated_texts = generate_text(prompt, max_length=50, num_return_sequences=1)
    for i, text in enumerate(generated_texts):
        print(f"Generated text {i+1}: {text}")

使用中文预训练模型

对于中文任务,我们可以使用专门的中文预训练模型:

# 使用中文BERT进行文本分类
from transformers import BertTokenizer, BertForSequenceClassification

# 加载中文BERT模型
chinese_bert_name = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(chinese_bert_name)
model = BertForSequenceClassification.from_pretrained(chinese_bert_name, num_labels=2)

# 中文情感分析示例
chinese_texts = [
    "这个电影真的很好看,剧情紧凑,演员演技在线。",
    "产品质量太差了,完全不值这个价格。",
    "今天天气不错,心情也很好。"
]

# 使用中文GPT模型进行文本生成
from transformers import GPT2LMHeadModel, GPT2Tokenizer

# 注意:需要使用专门的中文GPT模型,如CDial-GPT
# 这里仅作示例展示
try:
    chinese_gpt_name = 'thu-coai/CDial-GPT_LCCC-base'
    gpt_tokenizer = GPT2Tokenizer.from_pretrained(chinese_gpt_name)
    gpt_model = GPT2LMHeadModel.from_pretrained(chinese_gpt_name)
    print("成功加载中文GPT模型")
except:
    print("无法加载中文GPT模型,使用英文模型替代")
    gpt_tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
    gpt_model = GPT2LMHeadModel.from_pretrained('gpt2')

微调预训练模型的最佳实践

数据准备

  1. 数据清洗:去除噪声和无关信息
  2. 数据增强:通过同义词替换、回译等方法增加数据多样性
  3. 数据平衡:确保各类别样本数量相对平衡

训练策略

  1. 学习率设置:通常使用较小的学习率(2e-5到5e-5)
  2. 批次大小:根据GPU内存调整,通常为16或32
  3. 训练轮数:通常为3-5轮,避免过拟合

模型选择建议

任务类型推荐模型理由
文本分类BERT双向编码,理解能力强
命名实体识别BERT上下文理解能力强
问答系统BERT能够理解问题和答案的关系
文本生成GPT自回归生成能力强
对话系统GPT生成自然流畅的对话

BERT与GPT的局限性

BERT的局限性

  1. 预训练任务与下游任务的差异:MLM任务与实际任务可能存在差异
  2. 计算资源消耗大:双向编码增加了计算复杂度
  3. 生成能力较弱:不适合直接用于文本生成任务

GPT的局限性

  1. 单向编码:无法利用右侧上下文信息
  2. 事实性问题:容易生成虚假信息
  3. 可控性差:难以控制生成内容的具体属性

总结

BERT和GPT作为预训练语言模型的代表,各自具有独特的优势和适用场景。本节我们:

  1. 深入理解了BERT和GPT的核心差异
  2. 学习了BERT在文本分类任务中的应用
  3. 掌握了GPT在文本生成任务中的使用方法
  4. 了解了中文预训练模型的使用
  5. 熟悉了微调预训练模型的最佳实践

预训练语言模型的出现极大地推动了自然语言处理领域的发展,掌握它们的使用方法对于构建现代NLP应用至关重要。

在下一节中,我们将深入探讨生成式AI的更多应用,包括对话系统和多模态生成模型。

练习题

  1. 在更大的数据集上微调BERT模型,观察性能提升
  2. 尝试不同的GPT模型(如GPT-3、ChatGPT)进行文本生成
  3. 比较BERT和RoBERTa在相同任务上的表现
  4. 研究如何使用预训练模型进行命名实体识别任务