AI 学习课程 / LESSON 05

第五节:表格数据项目

把前四节连接起来:拿到一张带缺失值和类别特征的表,建立基线,用交叉验证比较方案,查清错误,最后交付模型与实验报告。

实际计算的完整项目分层 3 折验证逐条错误分析

01 / 先问清楚要预测什么

项目:预测站点是否为高需求

设想提前给共享单车站点备车。输入预测时已知的预报温度、是否下雨、站点类型,预测这个场景是否属于高需求。正类 y=1 表示高需求,y=0 表示非高需求。

输入 X

温度(数值)、下雨(0/1)、站点(类别)。

目标 y

场景结束后才知道的真实需求标签,用于训练与评估。

选择指标

本教学项目预先选择 F1,阈值固定 0.5,同时报告其他指标。

漏报高需求(FN)可能备车不足;误报高需求(FP)可能多备车辆。真实项目应估计这些成本,再选指标与阈值。这里用 F1 展示完整评估流程。

使用 180 个固定随机种子生成的独立合成场景,包含随机标签与缺失记录。所有结果只代表这个教学数据,不是实际运营预测。

不输入场景编号,也不输入当天结束后才知道的需求量。真实按日期连续采集的数据,需要考虑按时间划分;本数据没有日期或跨行依赖。

02 / 先认识数据,再训练

原始表还不能直接做矩阵乘法

这里先看 120 个开发样本;另有 60 个保留测试样本。开发集同时用于训练和内部验证,测试集此时不参与探索或调参。

浏览原始开发数据

小屏幕可横向滚动表格,查看全部列。

编号预报温度 °C下雨站点类型真实 y

检查各列的含义、缺失比例、类别分布、异常数值和标签比例。温度缺失需要处理;站点 A/B/C 是名字,不能直接当连续数值参与矩阵运算。

一行 = 一个样本;一列 = 一个特征。 原始输入:120 行 × 3 个字段;y 单独保存。 处理后:数值矩阵 X,形状 (120, d);标签 y,形状 (120,)。

真实项目还要检查重复来源、标签是否可靠、特征在预测时是否真的可获得。本合成数据的编号唯一,不代表真实数据也没有重复。

03 / 验证集轮流做,测试集继续保留

3 折交叉验证究竟是什么?

把 120 个开发样本分成三组。第 1 次拿组 1 验证、其余训练;第 2 次拿组 2 验证;第 3 次拿组 3 验证。每次都重新拟合预处理、重新训练模型。

绿色:这一折训练橙色:这一折验证蓝框:当前查看的一折
第 1 折 F1 = s₁;第 2 折 F1 = s₂;第 3 折 F1 = s₃ 平均 F1 = (s₁+s₂+s₃)/3 本页折间标准差 = √[Σ(sₖ−平均值)²/3]

本页使用分层划分:对每个类别固定洗牌后,轮流放入三组,尽量让类别比例接近。三组可能因为不能整除而大小略有差异。图中的组号表示分组,未必是连续编号。

交叉验证分数用于选方案,也可能因反复尝试而变得乐观。折间标准差是这次分数的离散程度,不是置信区间;最终仍须使用独立测试集。

04 / 把表转换为模型能计算的数字

填充 → 缩放 → 独热编码

温度缺失:从训练部分学习填充值

默认用这一折训练部分的温度中位数填充缺失值。中位数是排序后居中的值;偶数个观测取中间两个的平均。另一种可选教学方案是固定填 0,观察其验证表现。

标准化:减均值、除标准差

z = (填充后温度 − μ训练) / σ训练 μ、σ 来自这一折填充后的训练温度。 同样的填充值、μ、σ 应用于这一折验证样本。

这让温度在训练数据上接近均值 0、标准差 1。本页下雨已是 0/1,不再缩放。若标准差为 0,则改用 1,避免除零。

站点类别:一个类别对应一列

A=公园、B=学校、C=商圈。缺失站点先写成 MISSING,再当作一个类别。编码词表仅由训练部分出现的类别建立。

若训练词表是 [A,B,C,MISSING]: A → [1,0,0,0];B → [0,1,0,0];C → [0,0,1,0] 缺失 → [0,0,0,1] 训练时没见过的类别 → [0,0,0,0](本项目约定)

它不假设“商圈是公园的 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 个样本上重训模型。它与某一折的模型不是同一个拟合结果。

开发集内部:3 次训练 → 比较验证分数 → 确定方案 最终训练:120 个开发样本 → 拟合预处理 + 重训所选模型 最终评估:60 个保留测试样本 → 只 transform + predict + 计算指标

锁定后配置和候选选择不能再改,预测新样本仍可使用。真实工作中若据测试成绩改模型或阈值,就需要新的独立测试数据。本页刷新只是重做教学,不会让已看过的测试重新变成未知。

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)
  1. 写出任务、正类含义、预测时可用的特征和 FP/FN 的实际影响。
  2. 解释为何本节只有开发集与保留测试集,但每一折仍有训练与验证两部分。
  3. 手算某条验证记录的填充、标准化和独热向量,写出处理后形状。
  4. 完成“有站点 / 无站点”的单因素比较,下载实验记录,写明验证证据。
  5. 挑一条 FP 和一条 FN,提出合理的错误原因假设;区分观察与证明。
  6. 根据开发验证锁定方案,测试一次,下载最终 Markdown 报告。

11 / 检查理解

五道自测题

1. 3 折验证前先在完整开发集上计算温度中位数,是否正确?

2. 独热编码 B→[0,1,0,0] 表示什么?

3. 折外预测中的模型有没有用这条记录训练?

4. 最终方案选好后,应该使用哪份数据重训?

5. 缺失温度子组的误判率高,能否立刻证明填充方法导致错误?

下一节:PyTorch 基础。在可靠的训练与评估流程中,学习 Tensor、自动求导、数据加载、保存与推理。