文本分类是NLP最基础的任务之一——情感分析、垃圾邮件检测、新闻分类都是文本分类。本课实现一个完整的文本分类pipeline。
原始文本 → 分词 → 词嵌入 → 编码器 → 分类头 → 类别
维度=词表大小,稀疏,无法表示语义关系。
将词映射到低维稠密向量
相似词 → 相似向量
King - Man + Woman ≈ Queen
| 模型 | 优点 | 缺点 |
|---|---|---|
| TextCNN | 快,捕获局部n-gram特征 | 忽略长距离依赖 |
| LSTM | 建模序列依赖 | 较慢 |
| Transformer | 并行,全局注意力 | 数据量少时容易过拟合 |
import torch
import torch.nn as nn
# 文本分类实现
print("=== 文本分类实战 ===")
# 模拟文本数据
vocab_size = 5000
embed_dim = 64
num_classes = 4
class_names = ['体育', '科技', '娱乐', '财经']
# 生成模拟数据
torch.manual_seed(42)
seq_len = 30
X_train = torch.randint(0, vocab_size, (500, seq_len))
y_train = torch.randint(0, num_classes, (500,))
X_test = torch.randint(0, vocab_size, (100, seq_len))
y_test = torch.randint(0, num_classes, (100,))
# TextCNN
class TextCNN(nn.Module):
def __init__(self, vocab_size, embed_dim, num_classes, num_filters=100, filter_sizes=[3,4,5]):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.convs = nn.ModuleList([
nn.Conv2d(1, num_filters, (fs, embed_dim)) for fs in filter_sizes
])
self.fc = nn.Linear(num_filters * len(filter_sizes), num_classes)
self.dropout = nn.Dropout(0.5)
def forward(self, x):
x = self.embedding(x).unsqueeze(1) # B×1×L×D
conv_outs = []
for conv in self.convs:
c = torch.relu(conv(x)).squeeze(3) # B×F×(L-fs+1)
c = torch.max(c, dim=2)[0] # B×F
conv_outs.append(c)
x = torch.cat(conv_outs, dim=1)
x = self.dropout(x)
return self.fc(x)
# LSTM分类器
class LSTMClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True, bidirectional=True)
self.fc = nn.Linear(hidden_dim * 2, num_classes)
def forward(self, x):
x = self.embedding(x)
_, (h, _) = self.lstm(x)
h = torch.cat([h[-2], h[-1]], dim=1) # 双向最后隐状态
return self.fc(h)
# 对比训练
models = {
"TextCNN": TextCNN(vocab_size, embed_dim, num_classes),
"BiLSTM": LSTMClassifier(vocab_size, embed_dim, 64, num_classes),
}
for name, model in models.items():
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
loss_fn = nn.CrossEntropyLoss()
for epoch in range(50):
optimizer.zero_grad()
loss = loss_fn(model(X_train), y_train)
loss.backward()
optimizer.step()
with torch.no_grad():
train_acc = (model(X_train).argmax(1) == y_train).float().mean()
test_acc = (model(X_test).argmax(1) == y_test).float().mean()
params = sum(p.numel() for p in model.parameters())
print(f"{name}: train_acc={train_acc:.2%}, test_acc={test_acc:.2%}, params={params:,}")
# 词嵌入可视化概念
print("\n=== 词嵌入关系 ===")
# 模拟预训练词向量
embedding = nn.Embedding(vocab_size, embed_dim)
word_pairs = [("国王", "女王"), ("男人", "女人"), ("中国", "北京")]
print("词向量类比: King - Man + Woman ≈ Queen")
print("v(国王) - v(男人) + v(女人) ≈ v(女王)")
print("这种语义关系是词嵌入的核心价值")
本课的核心知识点构成了实战项目学习的重要一环。让我们从更高维度审视这些知识之间的关系:
⚠️ 初学者常犯的错误:
💡 最佳实践:
深度学习是一个整体——每一课的知识都不是孤立的:
💡 继续深入的学习路径:
用AG News或IMDB数据集训练文本分类器。
用预训练BERT做文本分类,对比TextCNN。
实现Focal Loss和过采样,处理类别不平衡问题。
⚠️ 需要避免的典型错误:
💡 提升模型性能的系统方法:
理解本课内容需要将其置于更大的知识网络中。每个核心概念都不是孤立的,它们相互关联、相互支撑:
建立正确的直觉比记住公式更重要:
1. 维度直觉:高维空间中,数据分布与低维直觉截然不同(维度诅咒)
2. 信息瓶颈:神经网络逐层压缩信息,保留任务相关的、丢弃无关的
3. 流形假设:高维数据实际分布在低维流形上,学习即发现流形结构
4. 偏差-方差权衡:简单模型欠拟合(高偏差),复杂模型过拟合(高方差)
本课作为实战项目阶段的核心内容,与前后课程紧密关联:
| 关联课程 | 关联点 | 协同效应 |
|---|---|---|
| 前序课程 | 提供了本课的基础知识 | 循序渐进的理解 |
| 后续课程 | 本课内容是后续的基础 | 逐步构建能力 |
| 平行课程 | 同一阶段的互补知识 | 多角度深入理解 |
| 实战项目 | 综合运用所有知识 | 理论与实践结合 |
💡 准备面试时,确保能回答以下问题:
掌握本课内容后,可以通过以下方式继续深入:
文本分类全流程——NLP的基础你已掌握!