第2章 结构化数据学习:从表格中发现规律

本章技术主题: 结构化数据的分类、回归与泛化评价。
本章技术实验: 用模拟环境表格完成一次分类、一次回归和基线比较。
家庭项目: “打造一个AI阳台植物园”第一阶段——表格数据学习。
跨章位置: 保存字段、基线和独立测试,第3章加入视觉,第12章完成多模态集成。
技术主线: 表格记录 → 特征与标签 → 基线 → 模型训练 → 独立测试 → 错误代价判断。

学习目标

完成本章学习后,你将能够:

  1. 从生活记录中区分样本、特征和标签,并发现缺失值、单位不一致和信息泄漏问题。
  2. 说明分类与回归的差别,分别为两类任务建立简单基线。
  3. 运行一段完整的Keras分类代码,读懂数据划分、归一化、训练、损失和测试结果。
  4. 根据训练集、验证集和测试集的表现识别过拟合,并结合错误代价形成使用判断。

项目导入

本章以“打造一个AI阳台植物园”为连续项目,进入第一阶段:表格数据学习。实践任务是整理一份虚构的阳台环境记录,让模型完成两个相互关联但性质不同的任务:根据当天记录判断植物是否“需要关注”,以及预测第二天早晨的土壤湿度。前者输出类别,属于分类;后者输出连续数值,属于回归。

后续章节还会沿用阳台植物场景研究视觉、时间序列、生成和多模态任务;本章只把每条环境记录作为结构化样本,重点学习特征、标签、分类与回归。同一生活场景可以形成不同AI任务,不能把一章的输入、输出和评价直接套到另一章。

结构化数据通常以表格呈现。每一行代表一次观察,每一列有固定含义,例如温度、光照、空气湿度和土壤湿度。表格看起来比图像简单,但模型不会自动知道哪一列是原因、哪一列是结果,也不会自动发现记录中的单位错误和未来信息。把生活记录变成可靠学习数据,是本章真正的实践重点。

本章的“应用中的AI”是依据表格特征输出状态类别或预测数值的模型。“开发与学习AI”帮助学生讨论字段、生成和解释完整程序、分析训练日志以及提出单变量修改建议。学生必须亲自确认标签规则、数据划分和错误代价,不能让语言模型看到表头后自行虚构数据,也不能只根据它对结果的文字评价决定采用模型。

本项目只使用教师提供的虚构记录,不代表任何真实植物的养护标准。模型输出用于学习数据规律,不替代植物品种说明、专业种植知识或现场观察。

本章成果

考点提示

请重点思考:特征和标签由任务而不是列的位置决定;分类输出类别,回归输出连续数值;损失是训练时衡量差异的量;验证集帮助选择方案,测试集用于最后检查;模型必须先超过合理基线,才有继续使用的价值。

第一节 把生活记录整理成特征和标签

一、把观察变成一行数据

假设每天傍晚记录阳台环境,并由人工观察植物是否需要进一步查看。每一行是一日样本,输入列包括平均温度、累计光照、平均空气湿度和傍晚土壤湿度。状态标签由人工按照课堂项目规则填写为“正常”或“需要关注”。为了练习回归,还记录第二天早晨土壤湿度作为另一个标签。

日期编号 平均温度/℃ 累计光照/lx·h 空气湿度/% 傍晚土壤湿度/% 状态标签 次晨土壤湿度/%
D01 22 12000 55 42 正常 39
D02 24 15000 52 38 正常 34
D03 31 26000 35 18 需要关注 13
D04 20 9000 60 45 正常 43
D05 33 28000 32 15 需要关注 9
D06 26 18000 48 35 正常 31
D07 30 24000 38 20 需要关注 15
D08 23 14000 58 40 正常 37

这里的“状态标签”和“次晨土壤湿度”都可以成为模型要学习的目标,但一次建模只选择其中一个。预测状态时,温度、光照、空气湿度和傍晚土壤湿度是特征,状态是标签;预测次晨湿度时,前四列仍是特征,次晨湿度变成标签。日期编号只是识别记录的编号,通常不作为特征。

不能把“第二天是否需要关注”提前放进当天输入,也不能加入根据状态标签直接填写的“建议浇水”列。这些列在真正预测时尚不存在,或已经包含答案。训练时使用这种未来信息或答案线索称为数据泄漏,它会让测试数字看起来很好,实际应用却无法复现。

二、先建立字段说明

在交给模型之前,为每一列写清名称、含义、单位、允许范围和缺失处理方法。例如温度统一使用摄氏度,湿度统一为0到100之间的百分数,光照使用同一种统计口径。若一行把华氏温度当成摄氏温度,数值仍能进入程序,却会破坏规律。

缺失值不能随意填成0。土壤湿度为0可能表示极干,也可能只是没有记录,两者含义完全不同。入门项目可以先把关键特征缺失的行标记为“不可用于训练”,保留在质量记录中;数据较多时再研究合理的缺失值填补方法。

标签同样需要规则。课堂中的“需要关注”只表示建议再次观察,不等于“必须浇水”,更不等于植物患病。两名学生若对同一行给出不同标签,应先查看书面规则并讨论原因,而不是让模型替人决定正确标签。

三、先看图,再训练模型

表格适合查看精确数值,图形更容易显露关系。可以把傍晚土壤湿度画在横轴、平均温度画在纵轴,用两种颜色表示状态标签。若“需要关注”的点大多出现在高温、低土壤湿度区域,说明这两个特征与标签存在可观察关系。若两类点完全混在一起,则要检查特征是否不足、标签是否不一致,或者任务本身是否无法由这些数据完成。

图上的关系不等于因果关系。即使高温记录更常被标为需要关注,也不能仅凭这张小表断言高温造成了某种植物状态。模型学习的是数据中可用于预测的统计关系,不会自动完成科学因果研究。

开发与学习AI可以根据“字段说明和几行虚构样例”检查单位、范围和可能泄漏,但应要求它逐列说明理由。学生随后回到原表核对,不能让它用想象补齐缺失记录。规划中的完整Notebook应保留原始表、清洗后的表和散点图,使每一步变化都可追溯。

第二节 用分类、回归与基线比较学习结果

一、先问一个最简单的方法能做到什么

在训练神经网络前,应建立基线。基线是一个简单、可重复的参考方法。分类任务可以始终猜训练集中数量最多的类别;如果训练数据中60%是“正常”,这个基线的正确率就是60%。回归任务可以始终预测训练标签的平均值,再计算它与真实值的平均绝对误差。

如果复杂模型没有稳定超过基线,就没有证据说明复杂性带来了价值。基线也能发现评价方式的问题:当100条记录中只有5条需要关注时,始终猜“正常”也能达到95%正确率,却漏掉全部需要关注样本。因此还要分别查看两类错误。

分类模型输出类别或类别分数,常用分类损失衡量预测分布与正确标签的差异。回归模型输出连续数值,可以使用平均绝对误差或均方误差。损失越小,表示模型输出与训练目标在该度量下越接近;它不是对模型“聪明程度”的总评分。

二、运行完整的最小分类程序

下面的程序使用24条虚构数据训练二分类模型。四个特征依次为温度、光照、空气湿度和土壤湿度,标签0表示正常,1表示需要关注。程序固定随机顺序,将60%数据用于训练、20%用于验证、20%用于最终测试,并把模型与多数类基线比较。

import numpy as np
import tensorflow as tf
from tensorflow import keras

keras.utils.set_random_seed(7)

x = np.array([
    [22, 12000, 55, 42], [24, 15000, 52, 38],
    [20, 9000, 60, 45], [26, 18000, 48, 35],
    [23, 14000, 58, 40], [21, 11000, 62, 47],
    [25, 16000, 50, 36], [19, 8000, 65, 50],
    [27, 17000, 47, 34], [22, 13000, 57, 43],
    [24, 10000, 59, 41], [25, 19000, 46, 33],
    [31, 26000, 35, 18], [33, 28000, 32, 15],
    [30, 24000, 38, 20], [29, 22000, 40, 22],
    [32, 30000, 30, 12], [28, 25000, 42, 19],
    [34, 31000, 28, 10], [29, 27000, 36, 17],
    [31, 23000, 34, 21], [30, 29000, 33, 16],
    [28, 20000, 41, 23], [32, 25000, 37, 14],
], dtype="float32")
y = np.array([0] * 12 + [1] * 12, dtype="float32")

rng = np.random.default_rng(7)
order = rng.permutation(len(x))
x, y = x[order], y[order]

train_end = int(len(x) * 0.60)
val_end = int(len(x) * 0.80)
x_train, y_train = x[:train_end], y[:train_end]
x_val, y_val = x[train_end:val_end], y[train_end:val_end]
x_test, y_test = x[val_end:], y[val_end:]

normalizer = keras.layers.Normalization()
normalizer.adapt(x_train)

model = keras.Sequential([
    keras.Input(shape=(4,)),
    normalizer,
    keras.layers.Dense(8, activation="relu"),
    keras.layers.Dense(1, activation="sigmoid"),
])
model.compile(
    optimizer="adam",
    loss="binary_crossentropy",
    metrics=["accuracy"],
)

stop = keras.callbacks.EarlyStopping(
    monitor="val_loss", patience=20, restore_best_weights=True
)
model.fit(
    x_train,
    y_train,
    validation_data=(x_val, y_val),
    epochs=200,
    verbose=0,
    callbacks=[stop],
)

test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
majority_label = int(np.mean(y_train) >= 0.5)
baseline_accuracy = float(np.mean(y_test == majority_label))
probabilities = model.predict(x_test, verbose=0).reshape(-1)
predictions = (probabilities >= 0.5).astype(int)

print("测试损失:", round(float(test_loss), 4))
print("模型测试正确率:", round(float(test_accuracy), 4))
print("多数类基线正确率:", round(baseline_accuracy, 4))
for truth, probability, prediction in zip(y_test, probabilities, predictions):
    print("真实标签:", int(truth), "关注分数:", round(float(probability), 3),
          "预测标签:", int(prediction))

归一化层根据训练集计算各特征的尺度,使数值很大的光照不至于仅因单位较大而压过其他特征。它只在训练集上学习统计量,验证和测试数据不能参与这个过程。隐藏层学习特征组合,最后的S形函数输出0到1之间的关注分数。

这段代码是可运行的最小实现,不要求学生默写。学生应能指出输入数组、标签数组、三份数据、归一化层、模型输出和基线计算分别在哪里。规划中的完整Notebook应把同一程序拆成数据查看、可视化、建模、训练曲线和测试五段,并保留每段运行结果。

三、把同一任务改成回归

若标签改为次晨土壤湿度,任务就从分类变为回归。输入特征可以保持不变,模型最后一层改为一个不带分类激活函数的数值输出,训练损失改为平均绝对误差或均方误差。基线则始终输出训练集次晨湿度的平均值。

比较时不能用“分类正确率”评价回归,而要报告预测值平均偏离真实值多少个百分点。一个平均误差为3个百分点的模型是否可用,还取决于记录误差、植物差异和使用后果。本章不把回归代码叠加到正文程序中;下一阶段的配套Notebook应提供与分类并列的完整回归版本,供学生逐项比较输出层、损失和评价指标。

第三节 检查数据划分、过拟合与错误代价

一、三份数据承担不同责任

训练集用于调整参数,验证集用于比较模型结构、训练轮数和阈值,测试集只在方案确定后作最终检查。如果反复查看测试结果并据此修改模型,测试集也会逐渐变成训练过程的一部分,最终数字仍会过于乐观。

阳台记录还要注意时间关系。随机打乱适合本章理解基本划分,但若目标是预测未来,正式项目应按时间划分:较早记录用于训练,中间记录用于验证,最近记录用于测试。这样更接近“用过去预测未来”,也更容易发现季节变化造成的数据漂移。

相邻多日来自同一段稳定环境,内容可能高度相似。若把几乎重复的记录分散到训练和测试两边,模型实际上看过非常接近的情况。划分前应先判断哪些记录来自同一连续过程,必要时按时间段或植物个体成组划分。

二、从训练曲线发现过拟合

模型在训练集上的损失持续下降,而验证集损失先下降后上升,通常说明模型越来越适合训练细节,却没有提升对未见数据的能力,这称为过拟合。小数据配复杂模型、训练轮数过多、标签含噪声,都可能加重过拟合。

正文程序使用提前停止:验证损失长期没有改善就停止训练,并恢复验证表现最好的参数。提前停止能够限制问题,但不能弥补错误标签、数据泄漏和代表性不足。更根本的改进包括增加有意义的不同情形、简化模型、重新检查字段和改进划分。

开发与学习AI可以读取学生抄录的训练与验证损失,提出“减少网络规模”“检查泄漏”“增加边界样本”等假设。学生每次只选择一个假设实施,用同一测试集外的验证证据比较。不能把一条训练曲线直接交给AI后接受一句“模型已经很好”。

三、正确率之外还有错误代价

将“需要关注”错判为“正常”称为漏报,将“正常”错判为“需要关注”称为误报。漏报可能使需要复查的情况被忽略;误报会增加人工检查。两种错误的代价不同,阈值选择也会因此不同。

本项目不允许模型自动执行养护动作。它只能提出候选状态,由人结合植物种类、现场观察和记录质量确认。出现特征缺失、数值超出训练范围、分类分数接近或模型服务不可用时,程序应保留原始记录并转为人工检查。

最终判断至少比较四项证据:模型是否超过基线;验证和测试表现是否接近;关键类别的漏报是否可接受;输入条件是否与训练数据一致。若只有24条虚构数据,合理结论应是“完成了机制验证”,而不是宣称获得了普遍适用的植物养护模型。

本章小结

结构化数据学习从定义一行记录开始。特征是模型可获得的输入,标签是希望模型学习的目标。分类预测类别,回归预测连续数值,二者需要不同的输出、损失和评价指标。训练集调整参数,验证集选择方案,测试集进行最后检查。复杂模型只有稳定超过基线才显示出价值;训练表现明显好于验证表现可能意味着过拟合。最终是否使用模型,还要结合数据泄漏、错误代价、输入范围和人工责任判断。

关键术语

目标测试

  1. 在预测“是否需要关注”的任务中,哪一项最适合作为标签? A. 平均温度 B. 状态类别 C. 日期编号 D. 光照单位
  2. 下列哪一项属于数据泄漏? A. 使用当天温度预测次日状态 B. 统一湿度单位 C. 把次日人工结论提前作为当天输入 D. 删除无法解释的重复记录
  3. 分类任务的简单基线可以是什么? A. 始终猜训练集中最多的类别 B. 增加隐藏层 C. 延长训练时间 D. 删除测试集
  4. 哪一种现象最符合过拟合? A. 训练和验证损失都下降 B. 训练损失下降而验证损失持续上升 C. 模型与基线相同 D. 所有特征单位一致
  5. 判断并改错:“验证集用于调整参数,测试集可以每天查看并反复选择模型。”
  6. 判断并改错:“回归输出连续数值,因此可以直接用分类正确率评价。”
  7. 从本章阳台数据中各举一个特征、分类标签和回归标签,并说明三者作用。
  8. 为什么要在训练神经网络前建立基线?请分别给分类和回归写出一种基线。
  9. 某模型训练正确率为98%,验证正确率为70%,测试正确率为68%。你认为可能出现了什么问题?写出两项检查或改进措施。
  10. 在“需要关注”识别中,漏报比误报代价更高。请说明这一判断会怎样影响评价指标、阈值和人工确认规则。