评估指标#

分类任务#

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%):最终评估

注意事项#

  1. 随机划分:确保数据分布一致
  2. 分层划分:保持类别比例
  3. 时间序列:按时间顺序划分
  4. 测试集隔离:只在最后使用

交叉验证#

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)。

特点

  • 计算量大
  • 结果最稳定

超参数调优#

遍历所有超参数组合:

param_grid = {
    'learning_rate': [0.001, 0.01, 0.1],
    'batch_size': [32, 64, 128],
    'hidden_size': [64, 128, 256]
}

问题:计算量大。

随机采样超参数组合。

优点:比网格搜索更高效。

贝叶斯优化#

使用概率模型指导搜索。

工具

  • 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

总结#

  1. 评估指标:根据任务选择合适的指标
  2. 数据划分:训练/验证/测试集
  3. 交叉验证:充分利用数据
  4. 超参数调优:网格搜索、随机搜索、贝叶斯优化
  5. 模型选择:避免欠拟合和过拟合

关键要点

  • 准确率不是万能的,要看具体任务
  • 交叉验证减少随机性
  • 超参数调优需要系统方法
  • 早停防止过拟合