训练完 ≠ 完事。没有评估的训练是自嗨
混淆矩阵、Precision/Recall/F1、ROC/AUC、交叉验证、过拟合诊断全掌握。
[toc]
模型评估与验证
1️⃣ 分类任务评估指标
混淆矩阵 (Confusion Matrix)
预测值
正类 负类
实际 正类 TP FN ← 灵敏度 (Recall)
值 负类 FP TN ← 特异度
↑ ↑
精确率 负类精确率
(Precision)
| 指标 | 公式 | 含义 |
|---|---|---|
| 准确率 (Accuracy) | (TP+TN)/(TP+TN+FP+FN) | 整体对了多少 |
| 精确率 (Precision) | TP/(TP+FP) | 判为对的里面,真对的比例 |
| 召回率 (Recall) | TP/(TP+FN) | 真的里面找出来了多少 |
| F1 Score | 2×P×R/(P+R) | 精确率+召回率的调和平均 |
from sklearn.metrics import (
accuracy_score, precision_score, recall_score,
f1_score, confusion_matrix, classification_report,
roc_auc_score
)
# 假設有预测和真实标签
y_true = [0, 1, 1, 0, 1, 0, 1, 1]
y_pred = [0, 1, 0, 0, 1, 0, 1, 0]
print(f"Accuracy: {accuracy_score(y_true, y_pred):.4f}")
print(f"Precision: {precision_score(y_true, y_pred):.4f}")
print(f"Recall: {recall_score(y_true, y_pred):.4f}")
print(f"F1 Score: {f1_score(y_true, y_pred):.4f}")
print("\n分类报告:")
print(classification_report(y_true, y_pred))
print(f"混淆矩阵:\n{confusion_matrix(y_true, y_pred)}")ROC 曲线与 AUC
from sklearn.metrics import RocCurveDisplay
# ROC 曲线衡量模型在不同阈值下的性能
# AUC = Area Under Curve,越接近 1 越好
y_scores = model.predict_proba(X_test)[:, 1] # 概率
auc = roc_auc_score(y_test, y_scores)
print(f"AUC: {auc:.4f}")
RocCurveDisplay.from_predictions(y_test, y_scores)什么时候用哪个指标?
| 场景 | 关注指标 | 例子 |
|---|---|---|
| 类别均衡 | Accuracy | 手写数字识别 |
| 类别严重不平衡 | Precision/Recall/F1 | 欺诈检测(正例很少) |
| 宁错杀不放过的场景 | 高 Recall | 癌症筛查 |
| 宁放过不错杀的场景 | 高 Precision | 垃圾邮件过滤(误判不可接受) |
| 综合评估 | F1 Score/AUC | 通用场景 |
2️⃣ 回归任务评估指标
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
y_true = [1.0, 2.5, 3.2, 4.1, 5.6]
y_pred = [1.1, 2.3, 3.0, 4.3, 5.8]
mse = mean_squared_error(y_true, y_pred) # 均方误差
rmse = np.sqrt(mse) # 根均方误差(更直观)
mae = mean_absolute_error(y_true, y_pred) # 平均绝对误差
r2 = r2_score(y_true, y_pred) # R² 决定系数
print(f"MSE: {mse:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")
print(f"R²: {r2:.4f}") # 越接近 1 越好3️⃣ 交叉验证 (Cross-Validation)
K-Fold Cross Validation
from sklearn.model_selection import KFold, cross_val_score
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = []
for fold, (train_idx, val_idx) in enumerate(kf.split(X)):
X_train, X_val = X[train_idx], X[val_idx]
y_train, y_val = y[train_idx], y[val_idx]
# 训练和验证
model = train_model(X_train, y_train)
acc = evaluate_model(model, X_val, y_val)
scores.append(acc)
print(f"Fold {fold+1}: {acc:.4f}")
print(f"平均: {np.mean(scores):.4f} ± {np.std(scores):.4f}")K 的选择
| K 值 | 适用场景 |
|---|---|
| K=3 | 大数据集 (>100K),节省时间 |
| K=5 | ⭐ 通用推荐 |
| K=10 | 小数据集,更稳定但更慢 |
| K=N (留一法) | 极少量数据 (<100) |
4️⃣ 过拟合诊断
如何判断过拟合?
Train Loss ↘↘↘ (一直下降)
Val Loss ↘→↗ (先降后升) ← ⚠️ 过拟合信号!
过拟合典型症状:
# 训练准确率 99%,验证准确率 70% → 严重过拟合
# 训练 Loss 一直在降,验证 Loss 开始上升 → 过拟合开始
# 模型在训练集上表现完美,泛化差 → 过拟合解决过拟合的方法
| 方法 | 说明 | 效果 |
|---|---|---|
| ✅ 增加数据 | 收集更多/数据增强 | ⭐⭐⭐ 最佳方案 |
| ✅ 降低模型复杂度 | 减少层数/隐藏层维度 | ⭐⭐⭐ |
| ✅ 正则化 | L1/L2 正则化 | ⭐⭐ |
| ✅ Dropout | 随机丢弃神经元 | ⭐⭐⭐ |
| ✅ 早停 | 验证 Loss 不再下降时停 | ⭐⭐⭐ |
| ✅ Batch Normalization | 每层归一化 | ⭐⭐ |
5️⃣ 模型对比实验规范
import json
from datetime import datetime
class ExperimentTracker:
"""规范化记录实验"""
def __init__(self, experiment_name):
self.experiment_name = experiment_name
self.results = {}
def log_params(self, **params):
self.results['params'] = params
def log_metrics(self, **metrics):
self.results['metrics'] = metrics
self.results['timestamp'] = datetime.now().isoformat()
def save(self, path='experiments.json'):
try:
with open(path, 'r') as f:
all_exp = json.load(f)
except:
all_exp = {}
all_exp[self.experiment_name] = self.results
with open(path, 'w') as f:
json.dump(all_exp, f, indent=2)🔗 下一篇
→ 模型保存与部署