🏠 总目录📚 本教程 挑战B · 四种解法对决 ← →
📑 本页目录(点开跳转)

19 / 一次完成一个小任务

同一道题公平比较,
才知道复杂度值不值得。

先固定切分、指标和调参预算,再比较四种解法;不要先认定谁应该赢。

先抓住一个具体结果

今天只完成第一条基线

用同一份训练与验证划分跑通最简单方案,记下分数、耗时和环境。其他方案沿用相同评估约定;最终测试集留到选择完成后。

接下来,按需要选一项

你现在想看什么?

这些入口是选择,不是必须按顺序完成的任务。

可以停在这里

先固定切分、指标和调参预算,再比较四种解法;不要先认定谁应该赢。

需要更多细节时,继续看完整正文 →
继续探索

原有正文、图解和例子都在下方。按需跳转,不必一次读完。

挑战项目 B · 四种解法对决

⏱ 4–6 天 | 难度 ★★★★☆ | 前置:第 2–6、15–16 章


🎯 为什么选这个项目

第 2 章说了「没有免费的午餐」,但那只是一句话。 这个项目让你亲手把它变成一张数据表——从此选型不再靠感觉。

因果链

同一批数据,四种解法正面对决:
① 线性模型(逻辑回归/岭回归)
② 树模型(LightGBM / GBDT)
③ 神经网络(MLP)
④ 预训练模型微调(如果数据是文本/图像)
× 在【三种不同规模的数据量】上各跑一遍
(100 行 / 1000 行 / 全量)
你会得到一张【什么时候该用什么】的决策表
这张表比任何博客的"经验之谈"都可靠,因为是你自己测的

这个项目训练的是选型判断力 —— 工程师最值钱、也最难自学的能力。

🔑 它同时是一个理论的实证检验: 数学原理第 11 章 NFL 说 "算法的专长是守恒的"——一个模型在某类问题上变强,必然在另一类上变弱。 这个项目就是把那句话画成一张交叉的曲线图。


🧭 开工前:一个能省你两天的建议

结果对照

❌ 常见的做法:先跑线性模型,跑完再跑树,跑完再跑NN……
跑到第三天发现实验函数有 bug,前面全白干
✅ 正确的做法:Day 1 只做一件事——
让【4 个模型 × 1 个数据量 × 1 个数据集】跑通,哪怕分数很烂
确认框架没问题,再去填满整个矩阵 ⭐

这就是"先跑通全流程"在实验项目上的版本。


📊 实验设计

数据集(选 2–3 个,覆盖不同类型)

类型 推荐数据集 获取
表格·分类 Adult 收入预测 / Titanic fetch_openml("adult")
表格·回归 California Housing fetch_california_housing()
文本·分类 20 Newsgroups fetch_20newsgroups()
图像·分类 CIFAR-10 子集 torchvision.datasets

💡 至少要有一个表格数据集和一个非表格数据集——结论会完全不同,这正是重点。

实验矩阵

实验矩阵:每次只填一格

比较模型在不同数据量下的表现
模型100 行1,000 行全量
线性模型待测待测待测
树模型待测待测待测
神经网络待测待测待测
预训练微调待测待测待测

每个格子记录:CV 分数 ± 标准差、训练时间、调参耗时。


🧠 ADHD 任务切分

第一阶段:搭实验框架(Day 1)

先把架子搭好,后面全是填空

  • 参考用时 · 90 分钟

    统一的实验函数:输入 (数据集, 模型, 数据量),输出 (CV分数, 标准差, 训练耗时)。所有预处理进 Pipeline(第 5 章)
  • 参考用时 · 45 分钟

    结果记录:每次实验追加一行到 CSV(模型/数据集/样本量/分数/标准差/耗时/超参)
  • 参考用时 · 30 分钟

    ⭐ 先测噪声底线:同一配置跑 5 个种子,记下分数的标准差。这个数字决定后面所有比较是否有意义

第二阶段:跑满矩阵(Day 2–3)

  • 参考用时 · 90 分钟

    线性模型:记得标准化,调正则强度
  • 参考用时 · 90 分钟

    树模型:调 max_depth / learning_rate / n_estimators(第 4 章)
  • 参考用时 · 120 分钟

    神经网络:用第 11 章的诊断流程(先过拟合 10 样本),调层数/宽度/dropout/lr
  • 参考用时 · 90 分钟

    预训练微调(文本/图像数据集上)
  • 参考用时 · 45 分钟

    ⭐ 公平性检查:每个模型的调参预算是否相当?(给树模型调了 50 组、给 NN 只调了 5 组,那结论无效)

第三阶段:深挖差异(Day 4)

项目的精华

  • 参考用时 · 60 分钟

    画「数据量 vs 分数」曲线,四条线放一张图。交叉点在哪?
  • 参考用时 · 60 分钟

    分析为什么:
    • 小数据上谁赢?为什么?(提示:归纳偏置,第 14 章)
    • 表格 vs 文本,排名有变化吗?为什么?
  • 参考用时 · 45 分钟

    成本维度:把「训练时间」和「调参耗时」也画出来 —— 有时第二名才是正确选择
  • 参考用时 · 45 分钟

    融合实验:把最好的 2–3 个模型加权平均,能超过单个最好的吗?(第 4 章 Stacking)

第四阶段:产出决策表(Day 5–6)

  • 参考用时 · 90 分钟

    写出你自己的选型决策树(不是抄的)
  • 参考用时 · 60 分钟

    README:实验矩阵表、四条曲线图、决策树、「和我预期不同的三个发现」

🔨 实验框架骨架

import time, numpy as np, pandas as pd
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.pipeline import Pipeline

RESULTS = []

def run_experiment(name, pipeline, X, y, n_samples=None, seeds=(0,1,2)):
    """统一实验入口 —— 所有对比都必须走这个函数,保证公平"""
    if n_samples:
        idx = np.random.RandomState(0).choice(len(X), n_samples, replace=False)
        X, y = X[idx], y[idx]
    scores, t0 = [], time.time()
    for s in seeds:
        cv = StratifiedKFold(5, shuffle=True, random_state=s)
        scores.extend(cross_val_score(pipeline, X, y, cv=cv, scoring="roc_auc"))
    rec = dict(model=name, n=len(X), mean=np.mean(scores),
               std=np.std(scores), secs=time.time()-t0)
    RESULTS.append(rec)
    print(f"{name:<20} n={len(X):<7} {rec['mean']:.4f} ± {rec['std']:.4f}"
          f"  ({rec['secs']:.1f}s)")
    return rec

# 用完导出
# pd.DataFrame(RESULTS).to_csv("results.csv", index=False)

🕳️ 专属坑

坑 说明
调参预算不公平 ⭐ 最容易毁掉整个结论。给每个模型相同的调参次数或时间
忘了测噪声底线 没有基准方差,你无法判断 0.005 的差距是不是真的
小数据上只跑一次 100 行数据的方差极大,必须多种子多折
预处理没进 Pipeline 第 5 章那个 76.5% 的教训
只比分数不比成本 训练慢 10 倍换 0.2% 提升,工程上往往不划算
数据集选得太单一 全是表格数据的话,你只会得出"树模型最强"这个片面结论
小数据用了不同的子集 100 行的那一格如果每个模型抽的是不同 100 行,比较无效。固定抽样种子 ⭐
文本/图像给树模型的输入不合理 把 TF-IDF 直接喂 LightGBM 可以,但别拿原始像素喂它再说"树模型不行"

⚖️ 「调参预算公平」具体怎么保证

这是本项目最容易被质疑、也最值得写进 README 的一点。三种做法:

做法 说明 推荐度
等次数 每个模型都随机搜 30 组 ⭐ 最简单,够用
等时间 每个模型都给 10 分钟调参预算 更公平,但结果依赖机器
都用默认参数 谁都不调 也是一个有效的对比维度 ⭐
from sklearn.model_selection import RandomizedSearchCV
BUDGET = 30                                    # ⭐ 所有模型共用这一个数字
search = RandomizedSearchCV(pipe, param_dist, n_iter=BUDGET,
                            cv=cv, scoring="roc_auc", random_state=0)

💡 "都用默认参数"这一列很有信息量: 它回答了「开箱即用哪个最强」——而在真实工作里, 你常常没有时间给每个模型调 30 组参数。


🏁 通关标准

  1. 完整的实验矩阵(4 模型 × 3 数据量 × 至少 2 个数据集),每格带 ± 标准差
  2. 测过噪声底线,并用它判断哪些差距是真实的
  3. 调参预算公平,且在 README 里说明了怎么保证的
  4. 画出「数据量 vs 分数」四条曲线,能指出交叉点并解释原因
  5. 产出你自己的选型决策树
  6. 记录了至少 3 个「和预期不同」的发现

🏆 加分

  • 加一个维度:特征数量(10 / 50 / 全部),看高维时排名怎么变
  • 测「加噪声标签」的鲁棒性:哪个模型最抗噪?
  • 把结论和 Kaggle 第 21 章任务决策路径对照,哪里一致、哪里不一致?不一致的地方去想为什么

💡 预期你会发现的(做完再看,别剧透自己)

👀 点开有剧透
  • 表格数据上,树模型在几乎所有数据量下都赢,且几乎不用调参
  • 神经网络在小数据上明显更差(归纳偏置弱,第 14 章)
  • 文本/图像上,预训练微调碾压一切,且数据越少差距越大
  • 线性模型在极小数据上反而可能最好(第 1 章那个逻辑回归)
  • 训练时间差距可能有 100 倍,而分数差距只有 1%
  • 融合能提升,但提升量常常小于噪声

✅ 检查点

  1. 这个项目和哪条理论直接对应?它把那条理论变成了什么?
  2. Day 1 该做什么?为什么不是"先把线性模型跑完"?
  3. 为什么必须先测噪声底线?没测会怎样?
  4. 「调参预算公平」有哪三种做法?为什么"都用默认参数"这一列有价值?
  5. 小数据那一格最容易犯什么错?
  6. 为什么必须至少有一个非表格数据集?
  7. 「成本也是维度」是什么意思?举一个例子。
  8. 四条曲线的交叉点说明了什么?
👀 答案
  1. 对应 NFL「算法的专长是守恒的」——一个模型在某类问题上变强必然在另一类上变弱。这个项目把那句话画成了一张交叉的曲线图。
  2. 让 4 个模型 × 1 个数据量 × 1 个数据集先跑通,哪怕分数很烂。因为如果先跑完一整列才发现实验函数有 bug,前面全白干。
  3. 因为没有基准方差你无法判断 0.005 的差距是不是真的。同一配置跑 5 个种子记下标准差,之后所有比较都以它为尺子。
  4. 等次数(都搜 30 组,最简单够用)、等时间(更公平但依赖机器)、都用默认参数。第三种有价值是因为它回答了「开箱即用哪个最强」——真实工作里你常常没时间给每个模型调 30 组。
  5. 每个模型抽的是不同的 100 行——这样比较无效。必须固定抽样种子。而且 100 行的方差极大,必须多种子多折。
  6. 因为全是表格数据的话,你只会得出"树模型最强"这个片面结论。排名会随数据类型改变,这正是 NFL 的可见证据。
  7. 训练时间和调参耗时也要记进表。例子:训练慢 10 倍换 0.2% 提升,工程上往往不划算——有时第二名才是正确选择。
  8. 说明不同数据量下的最优模型不同——小数据上归纳偏置强的模型(线性/树)赢,大数据上偏置弱的(NN)赢。交叉点就是你的选型分界线。

🛑 可以停在这里

⚡ 走神救援

先记住这几件事

  • 目标是一张自己的选型表:比较四种解法、三种数据量及至少两类数据集。
  • 第一天先让 4×1×1 跑通,不急着跑完整矩阵。
  • 统一数据划分、预处理和调参预算;重复实验,判断差异是否超过波动。
  • 比较分数时也记录训练与调参成本,解释曲线和排名随实验条件发生的变化。
  • 完成本阶段后可以停下;下次从阶段导航回到未完成的任务。

下一个挑战 👉 20-挑战项目C-过拟合实验室.md

打卡记录保存在你的浏览器里,首页能看到总进度