评估指标#
分类任务#
1. 准确率(Accuracy)#
$$ \text{Accuracy} = \frac{TP + TN}{TP + TN + FP + FN} $$特点:
- 直观易懂
- 问题:类别不平衡时不准确
2. 精确率(Precision)#
$$ \text{Precision} = \frac{TP}{TP + FP} $$含义:预测为正的样本中,真正为正的比例。
3. 召回率(Recall)#
$$ \text{Recall} = \frac{TP}{TP + FN} $$含义:真正为正的样本中,被正确预测的比例。
4. F1分数#
$$ \text{F1} = \frac{2 \cdot \text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}} $$特点:平衡精确率和召回率。
5. ROC曲线和AUC#
ROC曲线:以假正率(FPR)为横轴,真正率(TPR)为纵轴。
AUC:ROC曲线下的面积,值越大越好。
回归任务#
1. 均方误差(MSE)#
$$ \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 $$2. 均方根误差(RMSE)#
$$ \text{RMSE} = \sqrt{\text{MSE}} $$3. 平均绝对误差(MAE)#
$$ \text{MAE} = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i| $$4. R²分数#
$$ R^2 = 1 - \frac{SS_{\text{res}}}{SS_{\text{tot}}} $$含义:模型解释的方差比例,越接近1越好。
数据集划分#
基本划分#
全部数据
├─ 训练集(60-80%):训练模型
├─ 验证集(10-20%):调超参数
└─ 测试集(10-20%):最终评估注意事项#
- 随机划分:确保数据分布一致
- 分层划分:保持类别比例
- 时间序列:按时间顺序划分
- 测试集隔离:只在最后使用
交叉验证#
K折交叉验证#
将数据分成K份,每次用K-1份训练,1份验证:
Fold 1: [Train] [Train] [Train] [Train] [Val]
Fold 2: [Train] [Train] [Train] [Val] [Train]
Fold 3: [Train] [Train] [Val] [Train] [Train]
Fold 4: [Train] [Val] [Train] [Train] [Train]
Fold 5: [Val] [Train] [Train] [Train] [Train]优点:
- 充分利用数据
- 减少随机性影响
留一法(LOOCV)#
每次留一个样本作为验证集(K=N)。
特点:
- 计算量大
- 结果最稳定
超参数调优#
网格搜索(Grid Search)#
遍历所有超参数组合:
param_grid = {
'learning_rate': [0.001, 0.01, 0.1],
'batch_size': [32, 64, 128],
'hidden_size': [64, 128, 256]
}问题:计算量大。
随机搜索(Random Search)#
随机采样超参数组合。
优点:比网格搜索更高效。
贝叶斯优化#
使用概率模型指导搜索。
工具:
- Optuna
- Hyperopt
- Ray Tune
模型选择#
欠拟合 vs 过拟合#
欠拟合#
- 表现:训练和验证损失都高
- 原因:模型太简单
- 解决:增加模型复杂度
过拟合#
- 表现:训练损失低,验证损失高
- 原因:模型太复杂
- 解决:正则化、增加数据
学习曲线#
绘制训练和验证损失随epoch的变化:
损失
↑
| 训练损失
| ╱
| ╱
| ╱
|╱─────────── 验证损失
└────────────────────→ epoch早停(Early Stopping)#
在验证损失不再下降时停止训练。
代码实现#
评估指标计算#
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import confusion_matrix, classification_report
# 分类指标
y_true = [0, 1, 1, 0, 1]
y_pred = [0, 1, 0, 0, 1]
accuracy = accuracy_score(y_true, y_pred)
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)
print(f"Accuracy: {accuracy:.4f}")
print(f"Precision: {precision:.4f}")
print(f"Recall: {recall:.4f}")
print(f"F1: {f1:.4f}")
# 混淆矩阵
cm = confusion_matrix(y_true, y_pred)
print(cm)
# 分类报告
report = classification_report(y_true, y_pred)
print(report)数据集划分#
from sklearn.model_selection import train_test_split
# 基本划分
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 分层划分
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42
)K折交叉验证#
from sklearn.model_selection import KFold, cross_val_score
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=kfold, scoring='accuracy')
print(f"Mean: {scores.mean():.4f}, Std: {scores.std():.4f}")超参数调优#
网格搜索#
from sklearn.model_selection import GridSearchCV
param_grid = {
'learning_rate': [0.001, 0.01, 0.1],
'hidden_size': [64, 128, 256]
}
grid_search = GridSearchCV(
model, param_grid, cv=5, scoring='accuracy', n_jobs=-1
)
grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.4f}")Optuna优化#
import optuna
def objective(trial):
lr = trial.suggest_loguniform('lr', 1e-5, 1e-1)
hidden_size = trial.suggest_categorical('hidden_size', [64, 128, 256])
model = create_model(lr=lr, hidden_size=hidden_size)
score = train_and_evaluate(model)
return score
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
print(f"Best params: {study.best_params}")学习曲线#
import matplotlib.pyplot as plt
def plot_learning_curves(history):
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# 损失曲线
axes[0].plot(history['train_loss'], label='Train Loss')
axes[0].plot(history['val_loss'], label='Val Loss')
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('Loss')
axes[0].legend()
axes[0].set_title('Loss Curves')
# 准确率曲线
axes[1].plot(history['train_acc'], label='Train Acc')
axes[1].plot(history['val_acc'], label='Val Acc')
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('Accuracy')
axes[1].legend()
axes[1].set_title('Accuracy Curves')
plt.tight_layout()
plt.show()早停实现#
class EarlyStopping:
def __init__(self, patience=10, min_delta=0):
self.patience = patience
self.min_delta = min_delta
self.best_loss = None
self.counter = 0
def __call__(self, val_loss):
if self.best_loss is None:
self.best_loss = val_loss
elif val_loss < self.best_loss - self.min_delta:
self.best_loss = val_loss
self.counter = 0
else:
self.counter += 1
return self.counter >= self.patience
# 使用
early_stopping = EarlyStopping(patience=10)
for epoch in range(epochs):
train_loss = train_one_epoch()
val_loss = validate()
if early_stopping(val_loss):
print("Early stopping triggered")
break总结#
- 评估指标:根据任务选择合适的指标
- 数据划分:训练/验证/测试集
- 交叉验证:充分利用数据
- 超参数调优:网格搜索、随机搜索、贝叶斯优化
- 模型选择:避免欠拟合和过拟合
关键要点:
- 准确率不是万能的,要看具体任务
- 交叉验证减少随机性
- 超参数调优需要系统方法
- 早停防止过拟合