🎯 Stage 06 · 评估调参与迭代

评估调参与迭代 ← 闭环!

模型不会一次就完美,拿到 baseline → 诊断问题 → 制定改进策略 → 重训,反复循环才能逼近最优。

📊
评估
🔍
分析
⚙️
调优
🏋️
训练
闭环迭代 · 不行就再来一轮

💡 3-5 轮迭代后通常能达到上线标准。

🔁 每轮迭代问自己

1. 我的 val 分数baseline 高了吗?→ 没高 = 方向错了
2. 我的 val 分数上一轮 高了吗?→ 没高 = 这个参数不行
3. Loss 曲线 在收敛还是发散?→ 发散 = 要调 LR / 加正则
4. 还有明显的 错例模式 没解决吗?→ 有 = 下一轮继续分析

🎯 混淆矩阵 + 指标计算(交互式)

调整下面的滑块,实时观察 Accuracy/Precision/Recall/F1 的变化

预测 vs 实际(二分类)
预测: 正
预测: 负
实际: 正
85
TP
15
FN
实际: 负
10
FP
90
TN
TP (真正例)85
FN (假负例)15
FP (假正例)10
TN (真负例)90
评估指标
Accuracy
--
整体正确率
Precision
--
查准率
Recall
--
查全率
F1-Score
--
平衡指标
📐 指标公式速查
Accuracy = (TP+TN) / (TP+FP+FN+TN)
Precision = TP / (TP+FP)
Recall = TP / (TP+FN)
F1 = 2 × P × R / (P + R)
💡 这是一个均衡的混淆矩阵,Precision和Recall相对平衡

🎛️ 交互式超参数调优模拟器

调整下面的参数,观察对训练效果的模拟影响

0.001
太小:训练慢太大:震荡/发散
32
小:噪声大大:显存占用多
50
太少:欠拟合太多:过拟合
0.0001
0(无正则)强正则

📊 模拟训练效果

预计 Train Accuracy
--
预计 Val Accuracy
--
📉 Loss 趋势模拟
调整参数查看模拟效果...

🔍 误差分析:让模型真正变好

📉 欠拟合 vs 过拟合

欠拟合(高偏差)
训练集和验证集都很差
解决方案:
• 增加模型复杂度
• 增加特征维度
• 训练更久
过拟合(高方差)
训练好但验证差
解决方案:
• 增加训练数据
• 加正则化
• 早停 / 剪枝

🧩 常见错误类型

  • ⚠️
    类别混淆:模型把A类预测成B类 → 检查两类样本差异、增加区分性特征
  • ⚠️
    边界样本:分布边缘的样本容易错 → 主动学习收集更多边界样本
  • ⚠️
    困难样本:噪声/遮挡/极端角度 → 单独标注处理或数据增强
  • ⚠️
    类别不均衡:少数类召回率低 → 过采样/欠采样/加权损失
🔄
数据迭代是另一条重要提升路径:误差分析定位问题 → 补充难例/清洗噪声/重新标注 → 重新训练 → 验证提升。 行业经验:每迭代一轮效果提升 2-5%,3-5 轮通常能达到上线标准。 与上方模型迭代闭环(调参/改架构)配合使用,效果更佳。

⚙️ 超参数搜索策略 · 系统化调参方法论

调参不是"凭感觉",有系统化的搜索方法。不同策略的效率和效果天差地别。

✍️

手动调参

凭经验手动试几组值。适合小模型入门阶段,但效率低、易漏掉最优组合。

lr ∈ {0.01, 0.001}
wd ∈ {0.001, 0.0001}
精度: ⭐⭐ 速度: ⭐⭐⭐⭐⭐
🔲

网格搜索 Grid Search

给每个超参数列几个候选值,笛卡尔积跑所有组合。最全面但计算量爆炸。

GridSearchCV(estimator,
  param_grid={'lr':[0.01,0.001,0.0001],
  'batch_size':[16,32,64]},
  cv=5)
精度: ⭐⭐⭐⭐ 速度: ⭐
🎲

随机搜索 Random Search

在超参数空间随机采样,同样预算下比网格搜索往往更好(因为不是均匀分配算力)。

RandomizedSearchCV(estimator,
  param_distributions={'lr': loguniform(1e-4,1e-1),
  'batch_size': [16,32,64,128]},
  n_iter=50)
精度: ⭐⭐⭐ 速度: ⭐⭐⭐
📈

贝叶斯优化 Bayesian

用高斯过程代理函数建模 score 分布,每一步选"最值得试"的组合,计算效率最高。

from hyperopt import fmin, tpe, hp
best = fmin(fn=objective,
  space={'lr': hp.loguniform('lr', -4, 0)},
  algo=tpe.suggest, max_evals=100)
精度: ⭐⭐⭐⭐⭐ 速度: ⭐⭐⭐⭐
🍣

Optuna(最推荐)

AutoML 级别的调参框架,支持多目标、剪枝、可视化、分布式训练,PyTorch/TF/Sklearn 全兼容。

import optuna
def objective(trial):
  lr = trial.suggest_float('lr', 1e-5, 1e-1, log=True)
  bs = trial.suggest_categorical('bs', [16,32,64])
  return train(lr, bs)
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=200)
精度: ⭐⭐⭐⭐⭐ 剪枝+可视化
☢️

Ray Tune(大规模)

分布式超参数搜索框架,支持多 GPU 并行、中断恢复、多种调度器,适合工业级大规模调参。

from ray import tune, air
tuner = tune.Tuner(train_func,
  param_space={'lr': tune.loguniform(1e-5,1e-1),
  'bs': tune.choice([16,32,64])},
  tune_config=tune.TuneConfig(num_samples=1000))
result = tuner.fit()
精度: ⭐⭐⭐⭐⭐ 分布式
💡
实战经验:怎么选搜索策略?
  • 初学阶段:先手动调参建立直觉,理解每个超参数的影响
  • 小数据集(< 1 万样本):Grid Search + K 折交叉验证,跑得起也准确
  • 中等数据集(1-100 万):用 RandomizedSearchCV 或 Optuna,节省算力
  • 大数据集 / 大模型Optuna + Ray Tune 分布式 + 剪枝,没跑完就知道哪条线没戏
  • 高维空间(> 5 个超参):必须用贝叶斯优化,Grid Search 组合数爆炸
  • 必记的组合:Optuna + PyTorch Lightning + Weights & Biases = 调参三件套

💾 模型保存与推理测试

📦 保存最佳实践

# 方法1:保存state_dict(推荐)
torch.save(model.state_dict(), 'models/final/best_model.pt')

# 方法2:保存完整模型
torch.save(model, 'models/final/best_model.pt')

# 方法3:ONNX格式(跨平台部署)
dummy = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, dummy, 'model.onnx')

# 方法4:TorchScript(移动端)
traced = torch.jit.trace(model, dummy)
traced.save('model_scripted.pt')

# 方法5:保存训练状态(断点续训)
torch.save({
    'epoch': epoch,
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'best_val_acc': best_acc,
}, 'checkpoint.pth')

🔍 推理测试代码

import torch
from PIL import Image
from torchvision import transforms

# 1. 加载模型
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MyModel(num_classes=5)
model.load_state_dict(torch.load('models/best.pt', map_location=device))
model = model.to(device)
model.eval()  # 重要!切换到评估模式

# 2. 数据预处理(必须和训练时一致!)
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485], [0.229])
])

# 3. 推理
def predict(image_path):
    img = Image.open(image_path).convert('RGB')
    tensor = transform(img).unsqueeze(0).to(device)  # 加batch维度
    
    with torch.no_grad():  # 关闭梯度计算
        output = model(tensor)
        probs = torch.softmax(output, dim=1)
        pred_idx = probs.argmax(1).item()
        confidence = probs[0, pred_idx].item()
    
    return {
        'class': class_names[pred_idx],
        'confidence': f'{confidence:.2%}',
        'all_probs': {class_names[i]: f'{p:.2%}' for i, p in enumerate(probs[0])}
    }

result = predict('test_image.jpg')
print(result)
← 返回训练调优 部署上线 →