如果你第一次接触机器学习,线性回归很适合拿来建立一套完整的思考框架。它的公式不复杂,却把监督学习中最核心的环节都摆在了桌面上:我们要先定义输入和目标,再选择模型形式,用损失函数衡量错误,通过算法寻找参数,最后检查模型是否真的可信。
这篇内容不会只停在“会写 ”。我们会沿着一个房价预测问题,把最小二乘、梯度下降、闭式解、特征尺度、残差诊断和评价指标连成一条线。读完后,你应该能自己实现一个线性回归,也能判断什么时候不该相信这条直线。
假设我们拿到一张二手房成交表,其中有建筑面积、房龄、楼层、到地铁站的距离和最终成交价。现在要回答的问题是:新上架一套房时,能否根据这些已知信息估计它的成交价?
这个问题适合先用回归建模,因为目标“成交价”是连续数值。分类任务回答的是“属于哪一类”,回归任务回答的是“数值大约是多少”。预测明天的用电量、配送时长、门店销量和设备温度,也属于同一类问题。

一条可靠的回归线,起点是问题定义,终点是对新数据的检验。
我们通常把第 个样本写成 :
这里最容易被忽略的是“预测发生的时点”。如果我们要在挂牌时预测成交价,就不能把成交后才知道的信息放进特征,例如最终议价幅度或成交周期。把未来信息泄漏进训练数据,离线分数会很好看,上线后却无法复现。
只有一个特征时,第一步应该画散点图。它能快速回答几个问题:
多特征时无法把所有维度放进一张图,但我们仍可以分别查看目标与各特征的关系、特征分布和相关性,再结合业务含义判断。线性回归是一个合理的起点,不是一条无需检查的默认答案。
模型参数是根据训练集选出来的,所以训练误差天然偏乐观。我们至少要留出一部分模型没有见过的数据进行测试;需要反复调方案时,再把数据划成训练集、验证集和测试集,或者在训练集上做交叉验证。
如果数据有时间顺序,不能随意打乱未来和过去。用未来成交记录训练、再去预测过去,会让评估结果失真。此时更合理的做法是按时间切分,让验证过程模拟真实使用方式。
回归系数描述的是数据中的关联。除非数据来自合适的随机实验,或者有足够强的因果识别设计,否则“面积每增加一平方米,价格平均提高多少”不能直接改写成“增加面积会导致价格提高多少”。
只有一个特征时,线性回归模型写成:
是截距, 是斜率。若 表示面积、 的单位是万元,那么 的单位就是“万元/平方米”。在模型适用范围内,它表示面积增加一平方米时,模型预测的平均成交价变化量。
截距是 时的预测。这个数在数学上必须存在,却不一定有业务解释。如果样本面积都在 40 到 160 平方米之间,“面积为 0 的住房价格”显然不在数据支持的范围内。我们可以保留截距来正确拟合数据,但不要强行给它编一个现实故事。
当一套房有 个特征时,模型变为:
把截距对应的输入固定为 ,就能写成向量形式:
在没有交互项的模型里, 的常见解释是:其他特征保持不变时, 增加一个单位,预测值改变 个单位。这个“其他特征保持不变”很关键。若面积和房间数高度相关,现实中可能很难找到只改变面积而房间数完全不变的对照,系数也会变得不稳定。
很多人把线性回归理解成只能画直线,其实更准确的说法是:模型对待估参数是线性的。例如:
它关于 是弯曲的二次曲线,但 都只以一次幂出现,也没有互相相乘,所以仍能用线性回归估计。类似地,、 或两个原始特征的乘积 都可以先构造成新特征。
反过来, 包含参数乘积,就不再是参数线性的模型。
写下公式,相当于作出了一个可检查的近似:给定特征后,目标的条件均值可以由这些特征的线性组合描述。数据点不必全部落在线上,实际观测通常写成:
汇总了没有进入模型的因素、测量噪声和个体差异。我们要拟合的是平均趋势,并不承诺每套房都恰好等于这条线给出的价格。
线性模型可以包含多项式和交互特征,但这不代表加入越多项越好。特征越多,模型越容易贴住训练样本中的偶然波动。是否添加新项,仍要看验证误差、残差结构和业务解释。
模型已经给出预测,下一步要把“预测得准不准”变成一个可计算的目标。第 个样本的残差定义为:
残差为正,说明模型低估;残差为负,说明模型高估。直接把残差相加不是好办法:带截距的普通最小二乘拟合会让训练残差之和接近 0,正负错误会互相抵消,但这并不代表每次预测都准确。
普通最小二乘选择参数,使残差平方和最小:
机器学习实现里常用均方误差:
推导梯度时,我们还常写成:
三者只差一个与参数无关的正常数,因此最优参数完全相同。 只是为了让平方项求导产生的 2 抵消。阅读代码或教材时,要先确认对方报告的是 SSE、MSE,还是带 的 ,否则数值会对不上。
平方有三个直接结果:
第三点既是优势也是风险。误差从 5 变成 10,绝对误差只翻一倍,平方误差却从 25 变成 100。若大误差对应真实而重要的极端情况,这种重视可能符合业务;若它来自录入错误,一个坏点就可能明显拉动整条回归线。

散点图里的普通回归残差沿目标轴计算,平方后再求和。
有三套房,面积分别是 50、70、90 平方米,真实价格分别是 160、210、290 万元。候选模型是:
它给出的预测是 165、215、265 万元,残差 分别为 。于是:
RMSE 回到了“万元”这个原始单位,所以可以说,这个模型在这三个样本上的典型误差量级约为 15 万元。注意,只有三个训练样本时,这个数字不能代表模型对新房源的真实误差。
在二维散点图上,普通最小二乘最小化的是每个观测值到预测值的竖直差平方和,也就是 。它通常不是数据点到直线的最短垂直距离。后者对应的是另一类“正交距离回归”,适用的测量误差假设也不同。
在线性代数的观察空间里,最小二乘又确实有一个正交投影解释:预测向量 是目标向量 在 的列空间上的投影,残差向量与这个列空间正交。这两个说法处在不同空间,并不矛盾。
损失函数告诉我们要去哪里,梯度下降负责一步步走过去。对于带平方损失的线性回归, 是凸二次函数:形状像一个碗,可能有平坦方向,但不会有一堆真假难辨的局部最低点。
梯度是各参数偏导数组成的向量,指出损失上升最快的方向。要让损失下降,就沿负梯度更新:
是学习率,决定每一步迈多大。

同一个起点,学习率过小会走得很慢,过大则可能跨过谷底来回震荡。
对简单线性回归 ,使用 ,两个梯度为:
写成矩阵后更紧凑:
梯度的量级与预测误差、特征数值共同有关。面积特征如果取值在几百,房龄只在几十,两个方向的梯度尺度可能相差很大,这也是后面要做特征缩放的原因。
设标准化后的单特征样本为 ,目标为 。从 开始,所有预测都是 0,误差 为 。
截距梯度是误差均值,所以 。
“同时更新”指两个新参数都根据同一组旧参数计算。不要先改 ,再拿已经更新的 去算 的梯度;那已经变成另一种更新规则。
合理的全批量梯度下降,损失通常持续下降并逐渐变平。常见异常包括:
全批量梯度下降每次用全部样本,方向稳定但单步成本高;随机梯度下降每次只看一个样本,更新快但噪声大;小批量梯度下降在两者之间,也是大规模训练中更常见的选择。
在线性回归的平方损失下,“训练不收敛”通常不是掉进了局部最低点,而是学习率、数值尺度、数据质量或实现有问题。先检查这些因素,比反复随机初始化更有效。
梯度下降是迭代地逼近最优参数,最小二乘还可以通过解线性方程直接得到答案。把所有样本组成设计矩阵 ,目标组成向量 ,损失写成:
令梯度为 0:
整理得到正规方程:
若 可逆,形式上的解是:

最优残差与设计矩阵的每一列正交,因此 。
只有一个特征时,斜率和截距还能写成更熟悉的形式:
第二个式子说明,带截距的最小二乘回归线一定经过样本中心 。它也解释了斜率的来源:分子衡量 与 同向变化的程度,分母衡量 自身的变化量。
公式里出现逆矩阵,不代表程序应写 np.linalg.inv(X.T @ X)。显式构造 会把条件数问题放大,手动求逆也比直接解最小二乘系统更不稳定。NumPy 可以这样写:
import numpy as np
X_raw = np.array([[50], [70], [90], [110]], dtype=float)
y = np.array([160, 210, 290, 330], dtype=float)
X = np.column_stack([np.ones(len(X_raw)), X_raw]) # 第一列补 1,用来估计截距
theta, residuals, rank, singular_values =
lstsq 通常通过数值线性代数分解来求解;当特征存在完全线性依赖时,它仍可以给出一个最小范数解。更一般地,闭式表达可写为 ,其中 是 Moore–Penrose 伪逆。
无论采用哪条路线,只要目标和数据相同、优化充分,得到的都是同一个最小二乘问题的解。若结果差得很远,应检查截距列、缩放、停止条件、正则化设置和数据预处理是否一致。
设模型同时使用面积和房龄。面积可能在 30 到 300 之间,房龄在 0 到 40 之间。如果再加入“到市中心距离(米)”,数值可能达到几万。对梯度下降来说,不同方向的梯度量级会差很多,损失等高线会被拉成长椭圆,算法只能左右折返着靠近最低点。

缩放没有改变样本信息,却能让损失地形更接近圆形,梯度下降更容易选择步长。
常见的标准化是对每一列特征减去训练集均值,再除以训练集标准差:
标准化后,特征以 0 为中心,尺度大致相近。对无正则化的普通最小二乘来说,只要保留截距且变换可逆,缩放主要改变系数的表示方式和数值条件,映射回原始单位后的预测可以保持一致;对梯度下降来说,它往往会明显加快收敛。带正则化时,缩放还会影响各系数受到惩罚的相对强度,因此更不能忽略。
如果先用全部数据计算均值和标准差,再切分训练集与测试集,测试集的信息已经进入预处理。正确流程是:
先划分训练数据和测试数据,时间序列则按真实时间顺序切分。
只在训练数据上计算每个特征的均值、标准差或其他缩放统计量。
用同一组训练统计量变换训练集、验证集、测试集和未来线上数据。
把缩放器和回归器放进同一个 Pipeline,避免交叉验证时发生泄漏。
标准化使用均值和标准差,本身会受到异常值影响。一个极大的数既可能把均值拉偏,也可能把标准差放大,使大多数正常样本被压在很窄的区间内。Min-Max 缩放同样不会消除异常值,只会让样本最小值和最大值决定整个区间。
发现极端值后,应该先追问它为什么出现:单位是否填错、采集是否失败、它是否代表真实但稀少的业务情形。确认数据有效后,再决定保留、变换、分组建模,还是使用对异常值更稳健的缩放与回归方法。不要把“看起来远”当成自动删除的理由。
一个模型可以有不错的 ,却在某一类房源上持续低估。单个汇总分数会把方向和局部结构压缩掉,残差图则把这些信息重新摊开。
最常见的图是“残差对拟合值”:横轴放 ,纵轴放 。理想情况下,点在 0 附近随机散布,形成宽度大致稳定的水平带,没有清晰曲线,也没有孤零零的极端点。

残差中的规律不是装饰,它通常意味着模型还漏掉了某种结构。
传统线性回归常把条件概括为线性、独立、正态和等方差:
这些条件不是一张“违反任意一条就不能算参数”的通行证。最小二乘参数在没有正态性的情况下仍然可以计算。正态性主要关系到小样本下经典置信区间和显著性检验是否准确;在大样本和纯预测任务中,我们通常更关心未见数据上的误差、分布稳定性和不确定性估计方式。
更基础的要求是条件均值设定合理,即 。如果模型漏掉了一个既影响价格又与面积相关的重要变量,斜率可能系统偏误。等方差被破坏时,普通最小二乘系数在一些条件下仍可保持无偏,但常规标准误会出问题,预测区间也会错误地假设各区域有相同宽度。
一个点可以残差很大却没有明显拉动斜率,也可以特征极端但恰好沿着趋势,不一定有害。最需要调查的是同时高杠杆、又偏离趋势的点。实践中可比较包含和移除该点后的拟合,并结合杠杆值、学生化残差和 Cook 距离判断,但最终处理仍要回到数据来源。
不要因为残差图上有一个不顺眼的点就删除它。先核对原始记录、单位和采集过程,再说明保留或排除的业务理由,并报告这个决定对结果的影响。为了让分数变好而删点,会把真实风险一起藏掉。
训练时最小化 MSE,不代表汇报时只能看 MSE。不同指标回答的问题不同,最好同时报告一个与业务单位一致的误差指标和一个相对基线指标。
平均绝对误差为:
它与目标同单位,可以理解成平均偏差幅度,对极端误差的放大程度小于平方损失。
MSE 对大误差更敏感,但单位是目标单位的平方,不方便直接解释。RMSE 是 MSE 的平方根:
它回到目标原单位,同时仍然重罚大误差。若错估豪宅的代价远高于普通房源,RMSE 可能更贴近风险;若我们关心一笔典型预测相差多少,MAE 往往更直观。指标选择应反映错误成本,而不是哪个数字看起来更小。
决定系数定义为:
分母对应“对所有样本都预测真实目标均值”的平方误差。因此:
高不代表因果关系成立,也不保证残差条件满足,更不保证误差小到业务可接受。同一个 下,万元级房价和亿元级销售额的绝对误差完全不同。跨数据集比较 时,也要确认目标分布和切分方式一致。
一个稳妥的最小报告通常包括:
如果目标分布偏斜,只有整体平均值会掩盖少数高价样本的风险。分组评估能告诉我们模型究竟在哪些人群或区间上失效。
下面用一组小型房源数据走完训练、预测、评价和残差检查。示例规模很小,只用于说明接口;真实项目还要处理缺失值、类别特征、时间切分和数据版本。
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
data = pd.DataFrame({
"面积": [45, 55, 65,
这里使用 Pipeline,缩放器只会在传入 fit 的训练数据上计算统计量。交叉验证时,每一折也会分别拟合自己的缩放器,避免把验证折的信息提前带进训练折。
标准化后,LinearRegression().coef_ 对应的是“特征增加一个训练标准差”时的预测变化,不能直接解释成原始单位的一单位变化。如果需要业务解释,可以根据缩放器的 scale_ 还原:
scaler = model.named_steps["scale"]
reg = model.named_steps["regression"]
coef_original = reg.coef_ / scaler.scale_
intercept_original = (
reg.intercept_ - np.sum(reg.coef_ * scaler.mean_ / scaler.scale_)
)
for name, coef in zip(X.columns, coef_original):
print(f"{name} 每增加 1 个原始单位,预测变化 {
系数可解释不等于系数可信。若面积、房龄和地铁距离高度相关,某一列稍有变化就可能让各系数大幅波动,而整体预测变化不大。此时应查看特征相关性、条件数或方差膨胀因子,并考虑删减冗余信息、重新收集数据或使用正则化。
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(11, 4))
axes[0].scatter(y_pred, residuals)
axes[0].axhline(0, color="black", linewidth=1)
axes[0].set_xlabel("预测价格")
axes[0].set_ylabel(
第一张图检查残差结构,第二张图检查预测是否沿理想的 45 度线分布。样本很少时图形容易被偶然波动影响,不要把每个小弯曲都解释成模型问题;样本很大时则要结合抽样显示、分箱统计和分组指标。
真正会用线性回归,不是记住更多公式,而是在得到一条线后知道该追问什么。下面这些错误很常见,也都能通过明确的检查动作避免。
目标是什么,预测发生在什么时点,特征在那个时点是否真的可用?
数据如何切分,评估过程是否模拟了未来使用场景,是否存在时间或实体泄漏?
为什么选择线性形式和当前损失,是否画过散点图并检查了必要的变换或交互?
求解器是否稳定,梯度下降是否收敛,缩放参数是否只来自训练数据?
如果这些问题都有清楚答案,线性回归就不再只是“画一条最贴近散点的线”,而是一套可复查的建模过程。即使最后换成更复杂的模型,这套从问题定义、优化到诊断的顺序仍然适用。
斜率梯度要再乘输入,所以 。
取学习率 ,同时更新后得到 ,。
用新参数重新预测、计算损失和下一轮梯度,直到损失下降很少、梯度足够小或达到最大迭代次数。
测试集相对基线改善了多少,MAE、RMSE、 和分组误差分别说明什么?
残差是否还包含曲线、漏斗、时间结构或影响点,处理决定能否由数据来源和业务规则解释?