BERT 模型微调与传统机器学习的对比

发布于:2025-06-12 ⋅ 阅读:(18) ⋅ 点赞:(0)

BERT 微调与传统机器学习的区别和联系:

传统机器学习流程

传统机器学习处理文本分类通常包含以下步骤:

  1. 特征工程:手动设计特征(如 TF-IDF、词袋模型)
  2. 模型训练:使用分类器(如 SVM、随机森林、逻辑回归)
  3. 特征和模型调优:反复调整特征和超参数

BERT 微调流程

BERT 微调的典型流程:

  1. 预训练:使用大规模无标注数据预训练 BERT 模型
  2. 数据准备:将文本转换为 BERT 输入格式(tokenize、添加特殊标记)
  3. 模型微调:冻结大部分 BERT 层,只训练分类头(或少量 BERT 层)
  4. 评估与部署:在验证集上评估,保存模型

两者的主要区别

对比项 传统机器学习 BERT 微调
特征表示 手动设计特征(如 TF-IDF) 自动学习上下文相关表示
模型复杂度 简单到中等(如 SVM、RF) 非常复杂(Transformer 架构)
数据依赖 需要大量标注数据 可以用较少数据达到好效果
领域适应性 迁移到新领域需要重新设计特征 可以快速适应新领域(通过微调)
计算资源 通常较低 需要 GPU/TPU

您代码中的 BERT 微调关键点

  1. 数据预处理

    • 使用BertTokenizer将文本转换为 token IDs
    • 添加特殊标记([CLS]、[SEP])
    • 填充和截断到固定长度
  2. 模型架构

    • 基础模型:BERT 预训练模型(bert-base-chinese)
    • 分类头:在 BERT 顶部添加一个全连接层(num_labels=6
    • 微调策略:更新整个模型的权重
  3. 训练优化

    • 使用AdamW优化器(带权重衰减的 Adam)
    • 小学习率(2e-5)避免灾难性遗忘
    • 批量训练(batch_size=2)处理长序列
  4. 优势

    • 利用预训练模型捕获的语言知识
    • 自动学习文本的上下文表示
    • 对训练数据量要求较低
    • 迁移到新领域更容易

何时选择 BERT 微调而非传统方法?

  1. 当您有足够的计算资源时
  2. 当任务数据量有限时
  3. 当需要处理复杂语义理解时
  4. 当需要快速适应新领域时

BERT 微调入门示例,展示了如何将预训练语言模型应用于特定的分类任务。随着 Transformer 架构的普及,这种方法已经成为 NLP 任务的主流解决方案。

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

# 1. 准备数据
data = {
    'text': [
        "我想预订明天的机票", "查询今天的天气", "帮我设置闹钟",
        "播放周杰伦的歌曲", "今天有什么新闻", "推荐几部科幻电影"
    ],
    'label': [0, 1, 2, 3, 4, 5]  # 0:订票, 1:天气, 2:闹钟, 3:音乐, 4:新闻, 5:电影
}
df = pd.DataFrame(data)

# 2. 数据集划分
train_df, val_df = train_test_split(df, test_size=0.2, random_state=42)

# 3. 创建数据集类
class TextClassificationDataset(Dataset):
    def __init__(self, texts, labels, tokenizer, max_len=128):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len
        
    def __len__(self):
        return len(self.texts)
    
    def __getitem__(self, idx):
        text = str(self.texts[idx])
        label = self.labels[idx]
        
        encoding = self.tokenizer(
            text,
            add_special_tokens=True,
            max_length=self.max_len,
            return_token_type_ids=False,
            padding='max_length',
            truncation=True,
            return_attention_mask=True,
            return_tensors='pt'
        )
        
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'label': torch.tensor(label, dtype=torch.long)
        }

# 4. 初始化tokenizer和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained(
    'bert-base-chinese',
    num_labels=6
)

# 5. 创建数据加载器
train_dataset = TextClassificationDataset(
    train_df['text'].values,
    train_df['label'].values,
    tokenizer
)
val_dataset = TextClassificationDataset(
    val_df['text'].values,
    val_df['label'].values,
    tokenizer
)

train_loader = DataLoader(train_dataset, batch_size=2, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=2)

# 6. 训练模型
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

optimizer = AdamW(model.parameters(), lr=2e-5)
epochs = 3

for epoch in range(epochs):
    model.train()
    train_loss = 0
    
    for batch in train_loader:
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['label'].to(device)
        
        optimizer.zero_grad()
        outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
        loss = outputs.loss
        train_loss += loss.item()
        
        loss.backward()
        optimizer.step()
    
    avg_train_loss = train_loss / len(train_loader)
    print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_train_loss:.4f}")

# 7. 评估模型
model.eval()
predictions = []
true_labels = []

with torch.no_grad():
    for batch in val_loader:
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['label'].to(device)
        
        outputs = model(input_ids, attention_mask=attention_mask)
        preds = torch.argmax(outputs.logits, dim=1)
        
        predictions.extend(preds.cpu().numpy())
        true_labels.extend(labels.cpu().numpy())

accuracy = accuracy_score(true_labels, predictions)
print(f"Validation Accuracy: {accuracy:.4f}")

# 8. 保存模型
model.save_pretrained('./intent_classifier')
tokenizer.save_pretrained('./intent_classifier')