第 17 章 一个机器学习项目怎样完成
文章目录
- 第 17 章 一个机器学习项目怎样完成
- 🌈 关于《AI 零基础 36 讲》
- 1. 先说清楚最后要交出什么
- 2. 把特征、标签与编号放到各自的位置
- 3. 先分开训练与测试,再计算数据处理规则
- 4. 用一条 Pipeline 连接预处理与模型
- 5. 评估时至少保留预测与基线
- 6. 保存模型时,把使用规则一起留下
- 7. 跑通一遍,再检查自己能否解释数据用途
如果把“训练模型”理解成调用一次函数,写出的代码往往很短,问题也很隐蔽。数据有没有混进答案?测试样本是不是曾经参与训练?保存的文件下次还能不能用?一个完整项目需要把这些事情连起来。
这一章用一份小型公开花卉数据跑通流程。输入四个测量值,输出花卉类别。算法先用现成的逻辑回归分类器,我们暂时关注数据怎样进入模型、结果怎样核对,分类器内部的细节留到第 19 章。
本章的 Iris 是公开测量数据。固定快照、字段与许可说明见数据说明。单条预测中的数字沿用公开数据首行,属于接口演示,不代表收集到了一条新的独立样本。
🌈 关于《AI 零基础 36 讲》
🧠零基础也能开始学 AI
《AI 零基础 36 讲》是一套面向初学者的 AI 入门教程。课程从认识和使用 AI 开始,逐步学习
Python、数据处理、机器学习与深度学习,再进入大模型应用开发,最后完成个人知识库助手的综合实践。📖本篇是正式讲解文章
通过具体例子、原创图示和操作结果讲清概念。涉及编程的章节提供可运行代码,方便你边读边试。
🎯读完以后,动手检验
建议按专栏顺序学习,并完成对应的课后训练,检查自己能否独立运用本章知识。
博客专栏:《AI 零基础 36 讲》
1. 先说清楚最后要交出什么
“想做一个 AI”太宽泛。这里把任务写成一句可以检查的话,给出萼片长、萼片宽、花瓣长、花瓣宽四个厘米数值,预测它属于 setosa、versicolor、virginica 中的哪一类。
先看期望输出,通常可以分清三种任务。
| 任务 | 输入例子 | 期望输出 | 常见类型 |
|---|---|---|---|
| 估计二手车价格 | 年龄、里程、车型 | 一个金额 | 回归 |
| 判断花卉类别 | 四个长度测量 | 一个类别 | 分类 |
| 寻找相似的学习习惯 | 学习时段与频率 | 根据相似性形成的分组 | 聚类 |
这些生活例子为教学设定。分类通常有预先定义的类别标签可供学习,聚类则根据输入的结构分组,分出的组还需要人来解释。不能仅凭输出是数字就判定回归,本章类别也编码成 0、1、2,但它们代表名称。
项目目标还需要边界。本章只在这一份小数据集上跑通监督分类,没有开发拍照识花功能,也没有证明对其他植物有效。输入要求是四个测量值,模型无法直接接收一张照片。
2. 把特征、标签与编号放到各自的位置
特征是预测时可以提供的输入,标签是训练时用来对照的答案。通常把特征表写成 X,把标签写成 y,每行对应同一个样本。
公开 Iris 数据有 150 条记录,三类各 50 条,四个测量特征。scikit-learn 官方数据接口提供了这份数据。本章已经把固定版本的数据导出为 CSV,读者不用每次联网获取。
fromexamples.project_coreimportFEATURES,load_dataset frame=load_dataset()X=frame[FEATURES]y=frame["target"]print("特征形状",X.shape,"标签形状",y.shape)print(X.iloc[0].tolist(),int(y.iloc[0]))实际输出如下。
特征形状 (150, 4) 标签形状 (150,) [5.1, 3.5, 1.4, 0.2] 0第一行的四个数字依次是萼片长、萼片宽、花瓣长、花瓣宽,单位都是厘米,标签 0 对应 setosa。X 的形状说明有 150 行、每行四个特征,y 为每行提供一个类别。
CSV 还有sample_id,例如iris-000。这个编号用来追踪记录,既不属于测量特征,也不表示类别。原表按类别排列,编号与类别会存在人为关联,如果把它喂给模型,可能学到的是文件排列方式。
target更不能混进 X。预测新样本时,目标正是未知类别;训练时把类别作为输入,相当于提前提供答案。字段选择写在FEATURES中,就是为了明确哪些列可以进入预测。
这里仍沿用第 14 章的数据检查习惯。程序检查字段、缺失值、测量是否有限且为正、类别编码是否合法、编号是否重复。如果你手动改过表格,先解决检查失败,再谈模型结果。
3. 先分开训练与测试,再计算数据处理规则
训练集用来让模型学习特征与标签之间的关系,测试集用来检查训练后的模型在未参与拟合的记录上表现怎样。这两个用途需要从代码中看得出来。
本章将 80% 的记录留给训练,20% 留给测试,也就是 120 条与 30 条。划分时固定随机种子 42,并使用分层抽样,让本次划分的三类比例保持一致。
fromexamples.project_coreimportload_dataset,split_dataset train,test=split_dataset(load_dataset())print("训练与测试行数",len(train),len(test))print("训练各类",train["target"].value_counts().sort_index().tolist())print("测试各类",test["target"].value_counts().sort_index().tolist())print("编号有交集",bool(set(train["sample_id"])&set(test["sample_id"])))实际得到训练各类[40, 40, 40],测试各类[10, 10, 10],编号有交集为False。X、y 和编号整行一起划分,避免把特征与另一个样本的标签配错。划分接口文档说明了随机种子与分层参数的含义。
固定种子方便复现,不能用来挑一个看起来分数最高的划分。如果反复换种子,看着测试成绩选参数,测试数据就开始影响模型选择了。本章预先选定一次划分,没有按测试结果调参。后面需要选模型时,会引入单独的验证数据或交叉验证。
这份老数据集没有同一个对象反复测量的分组信息,也没有需要按时间推断未来的任务。真实项目如果一个人有多条记录,应考虑按人划分;如果预测未来,应考虑按时间划分。随机拆行并不适合所有问题。
4. 用一条 Pipeline 连接预处理与模型
第 16 章看过均值与标准差。本章先用StandardScaler将各测量列按照训练数据的均值与标准差转换,再把转换后的特征交给分类器。这种转换有助于统一不同列的数值尺度。
关键是均值与标准差从哪来。它们只能由训练数据计算,测试数据使用同一份已确定的规则。先对全部 150 条记录标准化、再划分,会让测试记录参与规则计算,评估就不再严格独立。官方常见错误说明专门讨论了这类数据泄漏。
Pipeline 可以理解为按顺序工作的处理链。调用整条链的fit时,前面的标准化器学习训练数据的统计量,后面的分类器学习类别关系。调用predict时,先按已有统计量转换输入,再进行预测。
fromexamples.project_coreimportFEATURES,load_dataset,split_dataset,make_model train,test=split_dataset(load_dataset())model=make_model()model.fit(train[FEATURES],train["target"])predictions=model.predict(test[FEATURES])print("标准化器见过的训练行数",int(model.named_steps["scale"].n_samples_seen_))print("测试预测条数",len(predictions))输出为训练行数 120、预测条数 30。测试标签没有进入fit,它们留到下一步与预测对照。这里的逻辑回归虽然名称里有“回归”,实际是分类模型,用来输出类别。
训练集准确率同样值得记录,它能帮助我们与测试结果比较。但在训练数据上表现好,不能代替新数据的评估。也不要只保存分类器,漏掉前面的标准化规则,否则以后输入的数值会经过不同处理。
代码里还有几个容易混淆的数字。四个特征与三个类别是数据的结构,分类器训练后学到的系数属于参数,C=1.0与max_iter=1000是运行前指定的配置,后者限制求解的最多迭代次数。把迭代上限写成 1000,并不表示一定更新了 1000 次,也不等于训练记录数量。
如果出现未收敛提示,先看是不是没有做约定的预处理、测量值是否异常,再检查迭代配置。不要直接忽略提示并抄下成绩。本章固定使用已核对的配置,实际运行没有出现未收敛警告,验收日志同时保留了标准输出与错误输出。
5. 评估时至少保留预测与基线
本章的最简单基线是始终预测训练集里最多的类别。三类训练数量一样多,当前实现选择其中一类,在平衡的测试集上会答对 10 条,准确率为10/30,约 33.33%。这个对照让我们知道一个完全不利用测量关系的办法能做到什么。
完整程序已经实际训练,结果如下。
| 记录项 | 本次结果 |
|---|---|
| 训练准确率 | 115/120,约 95.83% |
| 测试准确率 | 28/30,约 93.33% |
| 基线测试准确率 | 10/30,约 33.33% |
| 测试错误数 | 2 条 |
| 保存重载前后预测 | 30 条全部一致 |
准确率的计算很直接,预测类别与真实类别一致的数量除以总数量。分母是测试集的 30 条,不能把训练记录也加进来。逐条预测 CSV保留了编号、测量值、真实标签、模型预测、基线预测与是否正确,你可以自己数一遍。
分错记录值得留着,不能为了好看从报告里删掉。本章这两条都发生在 versicolor 与 virginica 之间。要解释具体为何分错,需要进一步查看特征重叠和决策边界,第 19 章会继续做。
30 条测试样本很少,多错一条就会改变约 3.33 个百分点。93.33% 说明这个固定实验比简单基线好,不能承诺以后任意一批花卉也有同样成绩。数据范围、测量方式、样本数量变了,都需要重新评估。
6. 保存模型时,把使用规则一起留下
训练程序输出iris_pipeline.pkl,里面包含整条处理链、字段顺序和类别名称,旁边的元数据记录 Python 与库版本、文件校验值。保存后立即重载,对同一测试集再预测一次,与保存前的结果逐条比较。
本章使用 Python 的 pickle 保存自己生成的本地对象。pickle 文件加载时可能执行代码,只读取自己训练的可信文件;网上随手下载的模型不能直接放进这个入口。库版本不同也不能假设能正常加载,本章会拒绝版本不一致并提示重新训练。官方模型保存说明介绍了这些限制。
frompathlibimportPathfromexamples.model_ioimportload_modelfromexamples.project_coreimportpredict_one model=load_model(Path("results"))print(predict_one(model,[5.1,3.5,1.4,0.2]))实际输出为{'label': 0, 'name': 'setosa'}。这组数沿用公开表首行,目的是核对保存文件与输入接口,不能拿它当作额外的测试成绩。
单条输入也有结构要求。四个数需要组成一行四列,顺序和厘米单位必须与训练时一致。故意直接传入一维列表的错误示例会报维度错误,修复版本使用统一入口整理形状。完整报错与成功输出见错误记录。
还有一种错误未必会报异常。把花瓣长度与萼片长度交换,仍然是四个数字,模型可能照常预测,却理解错了每一列。本章也支持按字段名传入字典,减少手动排顺序的风险,测量单位仍要由使用者核对。
7. 跑通一遍,再检查自己能否解释数据用途
下载并进入项目,安装固定依赖,运行examples/run_project.py,再运行单条预测入口。生成的文件都保存在章节自己的results目录,包含划分清单、训练行、测试预测、评估结果和模型。
运行成功后,任选一个测试编号,回到原始 CSV 找到这条记录,核对四个特征、标签与预测。再查看标准化器见过的行数,确认是 120。最后重载模型,核对预测一致。这三步能把“程序打印成功”变成可以追踪的检查。
也可以从结果文件反过来检查。iris-134的真实类别为 2,预测为 1;iris-077的真实类别为 1,预测为 2。原表、测试预测表和结果摘要应当互相对得上。如果摘要写着只错一条,明细里却出现两条错误,就要先修正记录,不能继续解释模型为什么好。
请合上代码,回答三个问题。哪些记录允许进入拟合?测试标签在哪一步使用?新输入为什么也要经过同一标准化器?能够指出具体变量和文件,再做【零基础学AI】第 18 章课后练习与答案。下一章把类别换成数值,在公开价格数据上训练线性回归,并解释每一类预测误差。