共计 3082 个字符,预计需要花费 8 分钟才能阅读完成。
传统文本处理的局限性
在自然语言处理(NLP)领域,传统方法如 TF-IDF 和词袋模型(Bag-of-Words)存在明显的局限性。例如,在情感分析任务中,这些方法无法捕捉句子中词语的顺序和上下文关系。考虑以下两个句子:

- “ 这个产品不算差 ”
- “ 这个产品不算好 ”
传统方法可能会因为 ” 不算 ” 和 ” 差 / 好 ” 的共现频率相似而给出相同的分类结果,而忽略了两者在语义上的明显差异。
另一个例子是在机器翻译中,传统方法难以处理长距离依赖关系。比如句子 ”The animal didn’t cross the street because it was too tired”,传统模型可能无法准确判断 ”it” 指代的是 ”animal” 还是 ”street”。
RNN/LSTM 与 Transformer 架构对比
循环神经网络(RNN)及其改进型长短期记忆网络(LSTM)曾是处理序列数据的标准选择。它们通过隐藏状态传递信息,但存在以下问题:
- 梯度消失 / 爆炸:长序列训练时信息难以有效传递
- 顺序计算:无法并行处理,训练效率低
Transformer 架构通过 Self-Attention 机制解决了这些问题。其核心特点包括:
- 并行计算:同时处理所有位置的输入
- 全局依赖:直接建模任意两个词之间的关系
- 多头注意力(Multi-Head Attention):从不同子空间学习多种关系
Self-Attention 的计算过程可分为三步:
- 计算 Query、Key 和 Value 矩阵
- 通过点积得到注意力分数
- 使用 softmax 归一化后加权求和
数学表达式为:
Attention(Q,K,V) = softmax(QK^T/√d_k)V
其中 d_k 是 Key 向量的维度。
BERT 预训练机制详解
BERT(Bidirectional Encoder Representations from Transformers)采用两种预训练任务:
- 掩码语言模型(MLM,Masked Language Model)
- 随机遮盖输入中 15% 的 token
- 其中 80% 替换为 [MASK],10% 随机替换,10% 保持不变
-
模型需要预测被遮盖的原始 token
-
下一句预测(NSP,Next Sentence Prediction)
- 输入两个句子 A 和 B
- 50% 情况下 B 是 A 的真实下一句,50% 为随机选取
- 模型判断 B 是否为 A 的合理后续
这种预训练方式使 BERT 能够学习深层的双向语境表示。相比单向语言模型(如 GPT),BERT 在需要理解全文的任务(如问答、实体识别)中表现更优。
文本分类实战代码
以下使用 HuggingFace 库实现中文文本分类:
from transformers import BertTokenizer, BertForSequenceClassification
from transformers import AdamW, get_linear_schedule_with_warmup
import torch
from sklearn.metrics import f1_score, accuracy_score
# 1. 加载预训练模型和分词器
model_name = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
# 2. 数据预处理
def preprocess(texts, labels, max_len=128):
inputs = tokenizer(texts, padding='max_length', truncation=True,
max_length=max_len, return_tensors='pt')
inputs['labels'] = torch.tensor(labels)
return inputs
# 示例数据
train_texts = ["这家餐厅很好吃", "服务质量很差"]
train_labels = [1, 0]
train_data = preprocess(train_texts, train_labels)
# 3. 训练配置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
optimizer = AdamW(model.parameters(), lr=2e-5)
scheduler = get_linear_schedule_with_warmup(optimizer,
num_warmup_steps=0,
num_training_steps=100)
# 4. 训练循环
for epoch in range(3):
model.train()
outputs = model(**{k:v.to(device) for k,v in train_data.items()})
loss = outputs.loss
loss.backward()
optimizer.step()
scheduler.step()
optimizer.zero_grad()
# 评估
with torch.no_grad():
logits = outputs.logits
preds = torch.argmax(logits, dim=1)
f1 = f1_score(train_labels, preds.cpu())
acc = accuracy_score(train_labels, preds.cpu())
print(f'Epoch {epoch}: Loss={loss.item():.4f}, F1={f1:.4f}, Acc={acc:.4f}')
避坑指南
小样本过拟合应对
- 使用早停法(Early Stopping)监控验证集性能
- 增加 Dropout 层(BERT 默认包含)
- 采用标签平滑(Label Smoothing)技术
GPU 显存优化
-
梯度累积(Gradient Accumulation):
accum_steps = 4 for step, batch in enumerate(train_loader): outputs = model(**batch) loss = outputs.loss / accum_steps loss.backward() if (step+1) % accum_steps == 0: optimizer.step() optimizer.zero_grad() -
使用混合精度训练:
from torch.cuda.amp import GradScaler, autocast scaler = GradScaler() with autocast(): outputs = model(**inputs) loss = outputs.loss scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()
中文处理注意事项
- 选择中文专用预训练模型(如 bert-base-chinese)
- 注意全角 / 半角符号的统一处理
- 中文分词可选但非必须(BERT 有子词切分能力)
进阶思考
- 如何处理超过 512token 的长文本?(提示:层次化处理或长文本 Transformer 变体)
- 如何设计领域自适应(Domain Adaptation)策略提升专业领域表现?
- 在多任务学习中如何平衡不同任务的损失权重?
通过本文的学习,读者应该能够理解 BERT 的核心原理,并掌握其在实际文本分类任务中的应用方法。建议在实践中逐步尝试更复杂的任务和优化技巧,以深入掌握这一强大的 NLP 工具。
