01 / 先问清楚要预测什么
项目:预测站点是否为高需求
设想提前给共享单车站点备车。输入预测时已知的预报温度、是否下雨、站点类型,预测这个场景是否属于高需求。正类 y=1 表示高需求,y=0 表示非高需求。
输入 X
温度(数值)、下雨(0/1)、站点(类别)。
目标 y
场景结束后才知道的真实需求标签,用于训练与评估。
选择指标
本教学项目预先选择 F1,阈值固定 0.5,同时报告其他指标。
漏报高需求(FN)可能备车不足;误报高需求(FP)可能多备车辆。真实项目应估计这些成本,再选指标与阈值。这里用 F1 展示完整评估流程。
不输入场景编号,也不输入当天结束后才知道的需求量。真实按日期连续采集的数据,需要考虑按时间划分;本数据没有日期或跨行依赖。
02 / 先认识数据,再训练
原始表还不能直接做矩阵乘法
这里先看 120 个开发样本;另有 60 个保留测试样本。开发集同时用于训练和内部验证,测试集此时不参与探索或调参。
浏览原始开发数据
小屏幕可横向滚动表格,查看全部列。
| 编号 | 预报温度 °C | 下雨 | 站点类型 | 真实 y |
|---|
检查各列的含义、缺失比例、类别分布、异常数值和标签比例。温度缺失需要处理;站点 A/B/C 是名字,不能直接当连续数值参与矩阵运算。
真实项目还要检查重复来源、标签是否可靠、特征在预测时是否真的可获得。本合成数据的编号唯一,不代表真实数据也没有重复。
03 / 验证集轮流做,测试集继续保留
3 折交叉验证究竟是什么?
把 120 个开发样本分成三组。第 1 次拿组 1 验证、其余训练;第 2 次拿组 2 验证;第 3 次拿组 3 验证。每次都重新拟合预处理、重新训练模型。
本页使用分层划分:对每个类别固定洗牌后,轮流放入三组,尽量让类别比例接近。三组可能因为不能整除而大小略有差异。图中的组号表示分组,未必是连续编号。
04 / 把表转换为模型能计算的数字
填充 → 缩放 → 独热编码
温度缺失:从训练部分学习填充值
默认用这一折训练部分的温度中位数填充缺失值。中位数是排序后居中的值;偶数个观测取中间两个的平均。另一种可选教学方案是固定填 0,观察其验证表现。
标准化:减均值、除标准差
这让温度在训练数据上接近均值 0、标准差 1。本页下雨已是 0/1,不再缩放。若标准差为 0,则改用 1,避免除零。
站点类别:一个类别对应一列
A=公园、B=学校、C=商圈。缺失站点先写成 MISSING,再当作一个类别。编码词表仅由训练部分出现的类别建立。
它不假设“商圈是公园的 3 倍”。未知类别全零并不是表达它已被正确理解,只是让输入维度保持固定;实际项目还需考虑未知类别的业务处理。
图解:一条验证记录怎样变成向量?
使用下方最近一次实验的配置,以及当前折的训练参数。可选温度缺失的记录,看看填充如何发生。
05 / 从一个有意义的参照开始
多数类 → 逻辑回归 → 决策树
多数类基线总是预测这一折训练集占多数的类别,不看输入。它回答:如果什么规律都没学,能达到什么水平?逻辑回归和三种深度的树必须在同样的三折上与它比较。
设置并运行一个实验
| 模型 | 第 1 折 F1 | 第 2 折 F1 | 第 3 折 F1 | 平均 F1 | 标准差 |
|---|
页面初始已运行一次默认配置,并自动选中平均 F1 最高的候选。你可以手动选择其他候选查看错误;如果最终不选平均 F1 最高者,应在报告中说明理由。
固定的训练设置与公平比较
逻辑回归使用 600 次全批量梯度下降、学习率 0.15、梯度中的 L2 系数 0.01,偏置不惩罚。树最大深度分别为 1、3、6,叶子最少 4 个样本。所有候选使用同一预处理配置、同一三折、阈值 0.5。
关闭标准化可能使逻辑回归在这个固定学习率下训练不稳定。比较的不只是模型名字,还包括完整特征和训练设置。多数类平票取 1,树以叶子正类比例为分数,再按 p≥0.5 判为 1。
深度、是否使用站点、填充方法属于你选择的超参数或流程配置。模型权重、树分裂和填充值则由训练数据学到。只看训练准确率选深树,会忽略过拟合。
06 / 分数告诉你多少错,样本告诉你怎样错
使用折外预测分析错误
折外预测(OOF):每条开发记录只用“没有拿它训练的那一折模型”来预测。三折拼起来,每个样本有且只有一个这样的预测。本页用它查看所选候选的错误。
| 真实高需求 1 | 真实非高需求 0 | |
|---|---|---|
| 预测高需求 1 | ||
| 预测非高需求 0 |
合并 OOF 后计算的 F1,不一定等于三个 F1 的平均。选择表使用“逐折平均 F1”,这里使用“合并样本 F1”,二者含义不同。树的分数是叶子类别比例,未必是校准后的概率。
| 记录 | 温度 | 下雨 | 站点 | 真实 y | 分数 | 预测 | 结果 |
|---|
按子组检查,但别急着下因果结论
| 子组 | 样本数 | 误判数 | 误判率 |
|---|
若缺失温度的样本错误多,可以提出“填充丢失信息”这个假设,再用新实验验证。也可能是样本少、标签噪声或其他因素。子组误差差异本身不能证明原因。不要拿错误列表反复调到完全正确。
07 / 每次改动都留下证据
一次只改一个主要因素
例如保持填充与标准化不变,仅去掉站点特征,重新跑相同的三折。比较的是“有站点”与“无站点”的整条流水线;不要同时改学习率、填充和树深度后随意归因。
| 实验 | 配置 | 该次平均 F1 最佳候选 | 平均 F1 | 标准差 |
|---|
预览当前 Markdown 报告
实验记录保存在本页内存中,刷新会清空。下载文件可固定结果。测试完成前,报告会明确标记测试集尚未评估。
08 / 方案决定后,才参加最后的考试
锁定配置,在完整开发集上重训
三折比较结束后,选择一个候选。把它的预处理重新在 完整 120 个开发样本上拟合,并在这 120 个样本上重训模型。它与某一折的模型不是同一个拟合结果。
配套 Python 默认实验报告包含默认方案的测试成绩;如果你选择了其他配置,请下载本页当前报告。
锁定后配置和候选选择不能再改,预测新样本仍可使用。真实工作中若据测试成绩改模型或阈值,就需要新的独立测试数据。本页刷新只是重做教学,不会让已看过的测试重新变成未知。
09 / 用同一套处理规则预测新记录
模型交付的是一条完整流水线
保存模型时,也要保存填充值、均值、标准差和类别列顺序。新样本使用完整开发集拟合的这些参数,不为新样本重新计算均值。
锁定前,这是所选候选在完整开发集上拟合后的试算;不用于计算验证成绩。新记录没有真实标签,不能从一次预测得出准确率。超出训练范围的温度、未知站点,都需额外判断适用性。
10 / 代码、报告与复盘
把这个项目变成可复现的交付
配套 NumPy 项目代码包含数据生成、分层三折、训练内预处理、基线与树、OOF 错误表、最终重训和 Markdown 报告。在终端进入项目根目录后运行:
uv run python lessons/lesson-05-project.py结果写入 lessons/lesson-05-output/:开发数据、逐折指标、折外预测、最终测试、实验报告与完整模型 JSON。运行脚本会完成默认方案并显示测试成绩;建议先在网页确定方案,再运行以复现。HTML 不需要先执行 Python。
复现其他配置,可加 --impute zero、--no-scale 或 --no-station。脚本会重新验证、选模和评估,并覆盖输出目录中的当前记录;网页仍保留你手动选择的候选。
训练流水线的核心伪代码
for train_indices, validation_indices in folds:
preprocessor.fit(raw_train) # 只在这一折训练部分拟合
X_train = preprocessor.transform(raw_train)
X_val = preprocessor.transform(raw_validation)
model.fit(X_train, y_train)
validation_scores.append(evaluate(model, X_val, y_val))
# 选好方案后,完整开发集重训;测试只做转换和评估。
preprocessor.fit(raw_development)
model.fit(preprocessor.transform(raw_development), y_development)
final_score = evaluate(model, preprocessor.transform(raw_test), y_test)- 写出任务、正类含义、预测时可用的特征和 FP/FN 的实际影响。
- 解释为何本节只有开发集与保留测试集,但每一折仍有训练与验证两部分。
- 手算某条验证记录的填充、标准化和独热向量,写出处理后形状。
- 完成“有站点 / 无站点”的单因素比较,下载实验记录,写明验证证据。
- 挑一条 FP 和一条 FN,提出合理的错误原因假设;区分观察与证明。
- 根据开发验证锁定方案,测试一次,下载最终 Markdown 报告。
11 / 检查理解