阶段3
|
NLP传统 · 第18课/共30课
📖 第18课:分类模型评估
🎯 课程目标
评估是机器学习的"仪表盘"。选择错误的指标可能导致看似优秀实则无用的模型。本课系统掌握分类评估体系。
- 掌握精确率、召回率、F1的计算与含义
- 理解macro/micro/weighted的区别
- 掌握ROC-AUC和PR曲线
- 学会根据业务需求选择评估指标
📐 核心原理
评估指标选择指南
| 场景 | 关键指标 | 原因 |
| 垃圾邮件 | 精确率 | 误杀正常邮件代价大 |
| 疾病筛查 | 召回率 | 漏诊代价远大于误诊 |
| 情感分析 | F1 | 正负类都重要 |
| 类别不平衡 | AUC-PR | ROC可能过于乐观 |
💡 macro vs micro:macro平均每个类别权重相同,关注少数类;micro让多数类主导,反映整体准确率。类别不平衡时一定要看macro。
🔬 完整评估报告
多分类评估
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import (classification_report, confusion_matrix,
precision_recall_fscore_support, accuracy_score)
import numpy as np
texts = [
"这部电影非常好看剧情精彩", "太棒了强烈推荐", "令人感动的故事",
"音乐优美画面精致", "经典之作百看不厌", "温馨治愈心情愉悦",
"这部电影太差了浪费时间", "剧情无聊看不下去", "演技尴尬令人失望",
"太烂了简直受不了", "无聊至极毫无亮点", "烂片无疑别去看",
"还行吧一般般", "有点无聊但能看", "中规中矩没什么惊喜",
]
labels = ['pos'] * 6 + ['neg'] * 6 + ['neu'] * 3
vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b')
X = vectorizer.fit_transform(texts)
clf = LogisticRegression(max_iter=1000, random_state=42)
y_pred = cross_val_predict(clf, X, labels, cv=3)
print("=== 分类评估报告 ===")
print(classification_report(labels, y_pred))
# 混淆矩阵
cm = confusion_matrix(labels, y_pred, labels=['pos', 'neg', 'neu'])
print("混淆矩阵:")
print(f"{'':8s} pred_pos pred_neg pred_neu")
for i, true_label in enumerate(['pos', 'neg', 'neu']):
print(f"{true_label:8s} {cm[i][0]:8d} {cm[i][1]:8d} {cm[i][2]:8d}")
# 逐指标分析
p, r, f1, s = precision_recall_fscore_support(labels, y_pred, average=None, labels=['pos', 'neg', 'neu'])
print("\n=== 逐类别指标 ===")
for i, label in enumerate(['pos', 'neg', 'neu']):
print(f" {label}: P={p[i]:.3f} R={r[i]:.3f} F1={f1[i]:.3f} support={s[i]}")
# macro vs micro
p_macro, r_macro, f1_macro, _ = precision_recall_fscore_support(labels, y_pred, average='macro')
p_micro, r_micro, f1_micro, _ = precision_recall_fscore_support(labels, y_pred, average='micro')
print(f"\nmacro: P={p_macro:.3f} R={r_macro:.3f} F1={f1_macro:.3f}")
print(f"micro: P={p_micro:.3f} R={r_micro:.3f} F1={f1_micro:.3f}")
=== 分类评估报告 ===
precision recall f1-score support
neg 0.40 1.00 0.57 6
neu 0.00 0.00 0.00 3
pos 0.00 0.00 0.00 6
accuracy 0.40 15
macro avg 0.13 0.33 0.19 15
weighted avg 0.16 0.40 0.23 15
混淆矩阵:
pred_pos pred_neg pred_neu
pos 0 6 0
neg 0 6 0
neu 0 3 0
=== 逐类别指标 ===
pos: P=0.000 R=0.000 F1=0.000 support=6
neg: P=0.400 R=1.000 F1=0.571 support=6
neu: P=0.000 R=0.000 F1=0.000 support=3
macro: P=0.133 R=0.333 F1=0.190
micro: P=0.400 R=0.400 F1=0.400
✅ 验证通过:代码实机运行结果如上
🔧 ROC与PR曲线
ROC-AUC与精确率召回率权衡
import numpy as np
from sklearn.metrics import roc_curve, auc
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
# 二分类ROC
texts = [
"这部电影好看剧情精彩", "太棒了推荐", "感动故事",
"优美精致经典", "满意治愈愉悦", "好看得停不下来",
"太差了浪费时间", "无聊看不下去", "尴尬失望",
"烂片受不了", "廉价混乱", "后悔别去",
]
labels = [1]*6 + [0]*6 # 1=pos, 0=neg
vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b')
X = vectorizer.fit_transform(texts)
X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.3, random_state=42)
clf = LogisticRegression(max_iter=1000, random_state=42)
clf.fit(X_train, y_train)
y_prob = clf.predict_proba(X_test)[:, 1]
fpr, tpr, thresholds = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
print("=== ROC曲线数据 ===")
print(f"AUC = {roc_auc:.4f}")
print(f"{'阈值':8s} {'FPR':8s} {'TPR':8s}")
for t, f, tp in zip(thresholds, fpr, tpr):
print(f"{t:8.4f} {f:8.4f} {tp:8.4f}")
# 精确率-召回率权衡
print("\n=== 精确率-召回率权衡 ===")
for threshold in [0.3, 0.5, 0.7]:
y_pred_t = (y_prob >= threshold).astype(int)
tp = sum((y_pred_t == 1) & (np.array(y_test) == 1))
fp = sum((y_pred_t == 1) & (np.array(y_test) == 0))
fn = sum((y_pred_t == 0) & (np.array(y_test) == 1))
prec = tp / (tp + fp) if (tp + fp) > 0 else 0
rec = tp / (tp + fn) if (tp + fn) > 0 else 0
print(f" 阈值={threshold:.1f}: P={prec:.3f} R={rec:.3f} F1={2*prec*rec/(prec+rec) if (prec+rec) > 0 else 0:.3f}")
=== ROC曲线数据 ===
AUC = 0.5000
阈值 FPR TPR
inf 0.0000 0.0000
0.6290 1.0000 1.0000
=== 精确率-召回率权衡 ===
阈值=0.3: P=0.250 R=1.000 F1=0.400
阈值=0.5: P=0.250 R=1.000 F1=0.400
阈值=0.7: P=0.000 R=0.000 F1=0.000
✅ 验证通过:代码实机运行结果如上
📝 练习
动手练习
- 实现一个类别不平衡的数据集(正负比1:10),对比macro/micro F1和AUC
- 绘制ROC曲线和PR曲线,分析两者的差异
- 实现分层K折交叉验证,确保每折类别分布一致
📊
🏆 成就解锁:评估专家
掌握了分类评估的完整体系,能准确衡量模型性能并选择合适的指标。
🧪 扩展:交叉验证策略
K折交叉验证的变体
- Stratified K-Fold:每折保持类别比例,类别不平衡时必须用
- Group K-Fold:同一组的样本不会同时出现在训练和测试中
- Time Series Split:时间序列数据,只能用过去预测未来
- Repeated K-Fold:重复多次K折,减少随机性影响
分类评估的常见陷阱
- 数据泄露:测试数据的信息泄露到训练中
- 类别不平衡:99:1的数据,全预测多数类就有99%准确率
- 过拟合指标:在测试集上反复调参,测试集变成了验证集
- 混淆指标:micro-F1在高不平衡数据上可能过于乐观
📚 参考文献
推荐阅读
- Jurafsky & Martin. Speech and Language Processing (3rd ed). 第18课相关章节
- Manning, Raghavan & Schutze. Introduction to Information Retrieval
- Goodfellow, Bengio & Courville. Deep Learning. 自然语言处理章节
在线资源
- NLTK文档: https://www.nltk.org/
- spaCy文档: https://spacy.io/
- gensim文档: https://radimrehurek.com/gensim/
- sklearn文本处理: https://scikit-learn.org/stable/modules/feature_extraction.html
分类评估 - 关键概念速查
| 概念 | 定义 | 应用 |
| Pipeline | 处理流水线,各步骤串行执行 | 文本处理系统 |
| Tokenization | 将文本切分为词/子词单元 | 分词、子词分割 |
| Embedding | 将离散符号映射到连续向量空间 | 词向量、句向量 |
| Feature Engineering | 从原始数据提取有效数值特征 | 传统ML模型输入 |
| Fine-tuning | 在预训练模型基础上微调 | BERT等模型适配 |
本课要点回顾
- 分类评估是NLP技术栈的重要组件
- 理解原理比调用API更重要——知道为什么比知道怎么做更有价值
- 代码验证是学习的好方法——动手跑一遍胜过读十遍
- 从简单方法开始,逐步过渡到复杂方法——简单方法的基线作用不可替代
- 实践出真知——用自己的数据跑一遍,发现课本上没写的问题
❓ 常见问题FAQ
- Q: 这个方法的数据需求量是多少?
- A: 传统方法通常需要几百到几千条标注数据。深度学习方法通常需要万级以上。数据量少时优先用传统方法。
- Q: 如何处理中文的特殊情况?
- A: 中文没有天然词边界,分词质量直接影响下游效果。建议:1)使用领域词典优化分词 2)比较不同分词工具的效果 3)对关键任务做分词错误分析。
- Q: 传统方法和深度学习如何选择?
- A: 考虑三个因素:1)数据量——少数据用传统方法 2)可解释性——需要解释选传统方法 3)计算资源——资源受限选传统方法。深度学习在大数据+强算力场景下优势明显。
- Q: 如何评估和调优?
- A: 1)建立基线:用最简单方法确定性能下限 2)错误分析:找出模型最常犯的错误类型 3)针对性改进:根据错误模式选择改进策略 4)避免过拟合:在独立测试集上验证。
- Q: 生产环境部署要注意什么?
- A: 1)延迟要求:简单模型推理更快 2)吞吐量:批处理提升吞吐 3)模型版本:做好版本管理和回滚 4)监控:持续跟踪线上模型性能 5)A/B测试:新模型上线前小流量验证。
🛠️ 实用工具箱
| 工具 | 用途 | 安装 |
| jieba | 中文分词 | pip install jieba |
| gensim | 词向量训练 | pip install gensim |
| scikit-learn | 传统ML | pip install scikit-learn |
| nltk | 英文NLP工具 | pip install nltk |
| spaCy | 工业NLP管线 | pip install spacy |
| hanlp | 中文NLP工具包 | pip install hanlp |
| fasttext | Facebook文本分类 | pip install fasttext |
| textblob | 简单NLP接口 | pip install textblob |