第 18 章 用线性回归预测数值
文章目录
- 第 18 章 用线性回归预测数值
- 🌈 关于《AI 零基础 36 讲》
- 1. 先把历史价格数据读对
- 2. 建一个很简单的基线
- 3. 单特征回归就是拟合一条直线
- 4. 多特征模型把几项输入放进同一个式子
- 5. 用误差回答模型错了多少
- 6. 平均误差之外,还要看具体错在哪
- 7. 两种常见运行问题,回到字段与数据范围检查
上一章输入四个花卉测量值,输出一个类别。现在换一个任务,给出汽车的几个规格,预测价格。输出从类别变成连续数值,需要用数值误差来评估,也需要弄清模型系数怎样参与计算。
本章使用 UCI 的 Automobile 公开小数据集,资料来自 1985 年的汽车规格等历史来源,共 205 条记录。目标是学习回归流程。它没有当前市场数据,也不能拿来估算今天的购车价格。UCI 数据页与本章的数据说明记录了来源、许可和下载日期。
请先完成第 17 章的训练测试划分,并熟悉第 14 章的缺失值处理。读完本章,你会得到三个模型的同条件比较、一份逐条预测表、三张图和一个可以逐格运行的 Notebook。
🌈 关于《AI 零基础 36 讲》
🧠零基础也能开始学 AI
《AI 零基础 36 讲》是一套面向初学者的 AI 入门教程。课程从认识和使用 AI 开始,逐步学习
Python、数据处理、机器学习与深度学习,再进入大模型应用开发,最后完成个人知识库助手的综合实践。📖本篇是正式讲解文章
通过具体例子、原创图示和操作结果讲清概念。涉及编程的章节提供可运行代码,方便你边读边试。
🎯读完以后,动手检验
建议按专栏顺序学习,并完成对应的课后训练,检查自己能否独立运用本章知识。
博客专栏:《AI 零基础 36 讲》
1. 先把历史价格数据读对
原始文件没有列标题,每行有 26 个字段,缺失值用问号表示。程序按原始说明为字段命名,将问号读取为缺失值,再选出本章使用的三项特征与价格。
| 字段 | 本章含义 | 用途 |
|---|---|---|
| engine_size | 发动机尺寸或排量字段 | 单特征及多特征输入 |
| curb_weight | 整备重量字段 | 多特征输入 |
| horsepower | 马力字段 | 多特征输入 |
| price | 原始价格字段 | 预测目标 y |
| make | 品牌 | 查阅错误记录,本章不作特征 |
| sample_id | 教学编号 | 追踪原始行,本章不作特征 |
获取到的数据页和原始字段说明没有明确列出这些量的物理单位或价格币种。本章因此保留原表量纲,图和指标都写明“原表价格数值”,没有擅自改成人民币、万元或升。现实应用需要先补齐单位信息,才能接入外部测量数据。
价格缺少四条,马力缺少两条,发动机尺寸与整备重量没有缺失。这六条分别进入拒绝表,保留编号和缺失字段。最终留下 199 条,三种模型都使用同一批完整记录。
fromexamples.regression_coreimportprepare_data clean,rejected,summary=prepare_data()print("原始 保留 拒绝",summary["raw_rows"],len(clean),len(rejected))print(rejected[["sample_id","missing_fields"]].to_string(index=False))输出行数为205 199 6。拒绝表中auto-009、auto-044、auto-045、auto-129缺少价格,auto-130、auto-131缺少马力。拒绝记录可以与原始文件逐行对照。
这里没有用均价填补缺失价格。价格是要学习的答案,填上自己猜出的数值,会把猜测当成真实标签。单特征模型本来只需要发动机尺寸,但为了公平对照,本章也限制在这 199 条共同记录上,避免模型间的样本和划分不同。
2. 建一个很简单的基线
先划分数据,再建立基线。固定种子 42,保留 20% 做测试,199 条最终分为 159 条训练、40 条测试。标签与特征整行划分,编号没有交集。
最简单的预测可以忽略所有规格,始终返回训练集的平均价格。本次训练平均价格约为 12386.31,因此基线对每辆测试汽车都给出这个数。
fromexamples.regression_coreimportrun_experiment models,train,test,summary=run_experiment()print("训练/测试",len(train),len(test))print(f"训练平均价格{train.price.mean():.2f}")print("前三条基线预测",[round(v,2)forvintest.baseline_prediction.head(3)])实际输出为159 40,训练均价为 12386.31,前三条基线预测都是 12386.31。这里从训练数据计算均价,测试数据只用于最后核对。基线提供一条起点,复杂模型至少应在合适的指标上解释自己带来了什么改善。
均值基线与平均绝对误差最优的常数预测也要分清。本章按最小平方误差的思路选择训练均值;若目标只关注绝对误差,训练中位数是另一种值得比较的常数基线。先明确指标,才能知道自己在优化什么。
3. 单特征回归就是拟合一条直线
只使用发动机尺寸时,预测规则可以写成一个系数乘输入,再加上截距。
本次训练得到b≈-6149.36,w≈150.55。如果输入 x=130,使用完整精度系数得到预测约为 13422.17。帽子表示预测数值,y 本身则表示真实价格。
importpandasaspdfromexamples.regression_coreimportrun_experiment models,_,_,summary=run_experiment()model=models["single"]value=model.predict(pd.DataFrame({"engine_size":[130]}))[0]print(f"截距{model.intercept_:.2f}系数{model.coef_[0]:.2f}预测{value:.2f}")输出为截距 -6149.36 系数 150.55 预测 13422.17。截距是在 x=0 时这条直线的输出。本章训练数据的发动机尺寸从 61 到 326,零并不在观察范围内,因此负截距不表示存在一辆负价格汽车。
直线经过点群中间,不会穿过每一个点。训练目标是让整体平方误差尽量小,某些点在直线上方,另一些在下方。
第 16 章手动做了梯度下降,本章的库函数默认使用数值线性代数方法求最小二乘解。两者都可以用于寻找更小的误差,不能把调用fit的所有模型都解释成“执行了前一章那段梯度下降循环”。算法组件可以换,训练数据的用途不变。
4. 多特征模型把几项输入放进同一个式子
现在加入整备重量与马力。三项输入分别乘自己的系数,再加截距。
本次得到的系数按发动机尺寸、整备重量、马力顺序分别约为 78.78、4.16、39.16,截距约为 -11685.47。测试记录auto-016的输入是 209、3380、182,使用完整精度系数计算后,预测约为 25975.35。
fromexamples.regression_coreimportrun_experiment,FEATURES models,_,test,_=run_experiment()row=test.loc[test.sample_id=="auto-016",FEATURES]manual=models["multi"].intercept_+row.to_numpy()[0]@ models["multi"].coef_print(f"手算式预测{manual:.2f}")print(f"模型接口预测{models['multi'].predict(row)[0]:.2f}")两种计算都得到 25975.35。@是第 13 章见过的向量乘积,这里把每个输入与对应系数相乘后相加。正文中的系数只显示两位小数,手动使用这些舍入值会与模型输出略有差异,核对时使用程序保存的完整数值。
多特征系数表达的是其他输入保持相同条件下的线性预测关系。发动机尺寸和马力往往相关,加一个字段后原来的系数会变化。因此不能看到 78.78 小于单特征的 150.55,就断定发动机的实际作用变小了,也不能把系数解释成改装一项规格的因果收益。
不同字段的量纲和数值范围也不同,不能按系数绝对值直接排列重要性。整备重量系数较小,每条记录的输入数值却在数千范围;最终贡献要看系数与输入一起计算。
5. 用误差回答模型错了多少
分类可以数答对多少类,价格预测通常不会与真实值完全一样。我们先计算每条记录的残差,本章约定为真实值减预测值。正残差表示低估,负残差表示高估。
用两条虚构价格举例,真实值为 10000、20000,预测为 12000、17000,残差为 -2000、3000。直接平均残差只有 500,两个方向部分抵消了,无法表达典型误差有多大。
MAE 是平均绝对误差,先把每条误差取绝对值再平均。
两条虚构记录得到(2000+3000)/2=2500,单位与价格相同。
RMSE 是均方根误差,先平方误差并平均,再开平方。
同一例子得到约 2549.51。平方会让大误差产生更大影响,开平方后单位又回到目标价格的量纲。
真实运行的三模型结果如下,MAE 与 RMSE 都保留原表价格量纲。
| 模型 | 训练 MAE | 测试 MAE | 训练 RMSE | 测试 RMSE |
|---|---|---|---|---|
| 均值基线 | 5023.32 | 8316.00 | 6868.49 | 11456.26 |
| 单特征回归 | 2532.00 | 4107.63 | 3432.30 | 5515.42 |
| 三特征回归 | 2111.84 | 3615.90 | 3053.21 | 4999.51 |
本次固定比较中,三特征模型的测试误差比前两个模型小。MAE 3615.90 是 40 条测试误差绝对值的平均,不保证每条都只错这么多,也不能说“价格准确率是某个百分比”。
结果表还保留 R²。它将模型的平方误差与总是预测当前评估集均值的参考误差比较。
在这份非恒定的测试标签上,三特征模型为 0.7789,单特征为 0.7309,均值基线为 -0.1608。参考均值是评估集均值,实际可用的基线用的却是训练集均值,两者不同,因此基线的测试 R² 可能为负。R² 没有价格单位,不能读成 77.89% 的价格预测完全正确。
前面两条虚构记录也能手算 R²。真实价格的均值为 15000,围绕均值的平方差之和为 50000000,预测平方误差之和为 13000000,代入得到1-13000000/50000000=0.74。这表示相对这个均值参考,平方误差减少了相应比例,两条预测仍然都存在误差。
6. 平均误差之外,还要看具体错在哪
图中横轴是真实值,纵轴是预测值。点落在虚线上表示预测恰好正确,虚线下面表示低估,上面表示高估。均值基线始终输出同一个数,所以形成一列水平排列的点。
三特征模型的高价测试记录有明显低估,残差图把这种问题显示得更直接。
| 测试编号 | 品牌 | 真实价格 | 三特征预测 | 残差 |
|---|---|---|---|---|
| auto-016 | bmw | 41315 | 25975.35 | 15339.65 |
| auto-127 | porsche | 34028 | 23175.09 | 10852.91 |
| auto-017 | bmw | 36880 | 26495.68 | 10384.32 |
这三条是本次绝对误差最大的记录。只看整体 MAE 会漏掉高价汽车上的大误差。本章模型没有使用品牌、车身形式等类别信息,三个规格也不足以完整表达价格差异,这些是值得继续检查的可能原因。没有补充实验前,不能断言加上某一字段就一定修好。
最大误差也不能全部归因于输入超范围。auto-016的发动机尺寸 209、重量 3380、马力 182,都位于训练表对应字段的最小值和最大值之间,模型仍然低估了约 15339.65。每列都在已见范围内,只能排除一种明显的输入问题,不能保证这三个值的组合被充分学习过,更不能保证遗漏的信息不影响价格。
还有一个能直接核对的现象。auto-126与auto-127的三项输入相同,真实价格分别为 32528 与 34028。模型只能给出相同预测,约 23175.09。相同输入对应不同目标时,无论怎么算这三个输入,都无法准确区分这两条记录,需要其他信息。
数据量很小,同品牌或相似配置可能同时出现在训练与测试中。随机划分适合本章演示,但若目标是预测从未见过的品牌或未来市场,需要另设计划分和数据来源。本章没有反复按测试成绩换特征,三种方案预先确定,比较结果也不作为真实市场模型选择结论。
7. 两种常见运行问题,回到字段与数据范围检查
把三个特征交给只用一个特征训练的模型,会出现字段不匹配错误。错误示例实际失败,修复版按照训练字段取列后,成功输出 40 条预测。不要通过随便删列消除报错,要核对模型到底用哪几列、顺序是什么。
另一个问题是向模型传入远离训练范围的数。直线可以无限延伸,输入极小的发动机尺寸时甚至可能给出负价格。它并不知道真实汽车价格必须为正。外部使用前应检查单位、字段范围和样本是否属于同类数据;本次测试的三特征预测没有负值,仍然不能保证任意输入都合理。
进入本章项目,运行相关程序,再打开 Notebook。
逐条预测、六行指标比较和完整系数都已写入results,你可以修改显示精度,但不要只保留舍入后的数值继续算误差。
最后独立做一个核对。找到auto-085,它的真实价格为 6989,三特征预测约为 11215.88。计算残差约 -4226.88,并说明这是高估还是低估。再从【零基础学AI】第 18 章课后练习与答案中挑一个指标题,先手算,再用程序确认。下一章回到分类问题,观察类别概率与容易分错的样本。