一元线性回归
一元线性回归(Simple Linear Regression, SLR)是数学建模中"用一条直线刻画两个变量关系"的基础统计模型。它公式简单、计算量小、可解释性强,是竞赛中处理预测类、关系检验类题目的首选入门工具,也是学习多元回归、时间序列等高级方法的垫脚石。本文从原理、适用场景、评价指标、可视化诊断到可运行代码,完整梳理一元线性回归的竞赛实战用法。
一、算法含义
1.1 通俗理解
假如我们想知道"身高 是否影响体重 ",或者"广告投入 能带来多少销量 ",一种最朴素的想法是:在散点图中找一条"最好"的直线,让它尽量穿过所有点。这条直线就是回归直线:
其中 是截距( 时 的取值), 是斜率( 每增加 1 个单位, 平均变化 个单位)。所谓"最好",指的是让所有点到直线的竖直距离(残差)的平方和最小——这就是最小二乘法(Ordinary Least Squares, OLS)。
一元线性回归回答三个问题:
- 关系方向与强度: 与 是正相关还是负相关? 对 的影响是否显著?
- 关系大小: 每变化一个单位, 平均变化多少( 的估计值)?
- 预测:给定新的 , 大概是多少(点预测 + 区间预测)?
1.2 数学模型
一元线性回归的完整模型是:
- :第 个样本的因变量(响应变量、被解释变量),如销量、温度、房价;
- :第 个样本的自变量(解释变量、特征),如广告费、时间、面积;
- :待估计的未知参数(截距与斜率);
- :随机误差项,代表模型无法解释的"噪声"(测量误差、遗漏因素等);
- :样本量。
模型的核心思想是平方和分解(方差分析的思想): 的总波动(SST)可以拆成"回归能解释的部分"(SSR)和"解释不了的残差部分"(SSE):
1.3 最小二乘估计(OLS)的推导
最小二乘的思想:选择 ,使残差平方和最小。目标函数为:
对 、 分别求偏导并令其为 0,得到正规方程(Normal Equations):
解正规方程,得到最小二乘估计的闭式解:
两个很有用的直观结论:
- 回归直线必过样本中心点 (由 直接得到);
- 可以看成 与 的"标准化协方差的变体":分子是共同波动 ,分母是 自身波动 。
进一步,残差方差 的无偏估计为(除以 是因为估计了两个参数、损失两个自由度):
1.4 高斯-马尔可夫假设
OLS 估计的良好性质(无偏性、有效性)依赖以下 高斯-马尔可夫假设(GM 假设):
- 线性性: 与参数 是线性关系,即 (对 本身可以是曲线,只要对参数线性即可);
- 零均值:误差期望为零,(保证估计无偏);
- 同方差:误差方差恒定,,不随 或 变化(违反即"异方差");
- 不相关:不同样本的误差互不相关,(违反即"自相关",常见于时间序列);
- 正态性(做检验时额外需要):,用于构造 检验、 检验与置信区间。
高斯-马尔可夫定理:在假设 1~4 成立时,OLS 估计量是最佳线性无偏估计(BLUE, Best Linear Unbiased Estimator)——即所有线性无偏估计中方差最小。这是 OLS 在理论上"最优"的根基。
1.5 优缺点
优点:
- 原理简单、结果直观("一个截距 + 一个斜率"即可解释全部关系);
- 有闭式解,计算极快, 很大也没有压力;
- 理论完备:标准误、 检验、 检验、置信区间、预测区间都有现成公式;
- 可解释性强,评审老师一看就懂,竞赛中"能简单就不复杂";
- 是多元回归、方差分析、时间序列等方法的共同基础。
缺点:
- 只能刻画线性关系,遇到曲线关系会严重失真;
- 对离群点敏感:平方损失会把大残差放大,一个异常点就能把直线"拽歪";
- 只有一个自变量,信息量有限(多变量时需用多元线性回归);
- 假设较多(同方差、正态性等),违反时检验结论不可靠;
- 外推有风险:超出样本 范围做预测时,结论可能完全不可信。
二、何时使用(适用场景与条件)
2.1 适用场景
- 两变量关系检验:判断 是否显著影响 、影响方向和大小。如"气温是否显著影响用电量""受教育年限对收入的影响有多大"。
- 预测与插值:给定新的 ,预测 的点估计与区间。如"明年第 13 个月销量预测"(当时间 与销量近似线性时)。
- 竞赛中的预测类题目:数学建模竞赛中大量"预测未来某量"的题目,若散点图显示近似线性趋势,一元线性回归是性价比最高的起点模型,也常作为与高级模型对比的基线(baseline)。
- 数据探索与预处理:快速判断两变量关联强弱(配合相关系数),为是否引入某变量提供依据。
- 作为评价其他模型的参照:竞赛论文中常写"以线性回归为基线,XX 模型将 RMSE 降低了 xx%"。
2.2 使用前提(建模前检查清单)
| 检查项 | 具体要求 | 检查手段 |
|---|---|---|
| 线性趋势 | 与 近似直线关系 | 先画散点图,肉眼看是否"一坨直线" |
| 样本量 | 一般要求 ;至少 (估计两个参数) | 数样本个数 |
| 变量类型 | 、 为连续变量(或近似连续) | 数据本身 |
| 无强离群点 | 少数极端点会带偏直线 | 散点图 + 残差图 |
| 同方差 | 残差波动不随 增大而增大 | 残差-拟合值图(喇叭形即异方差) |
| 正态性 | 残差近似正态(做 /F 检验时需要) | 残差 QQ 图 |
| 独立性 | 各样本误差不相关(时间序列要小心) | 残差随时间/序号的图 |
竞赛提示:以上检查不必全部完美通过才敢用模型,但至少在论文中画出散点图 + 残差图并给出判断,评审会认为你的模型是"经过诊断的",而不是"拍脑袋的"。
2.3 不适用 / 慎用的情形
- 明显的非线性关系:散点呈抛物线()、指数增长()、周期波动等。强行拟合直线会出现"残差有形状"的典型症状。此时应改用:多项式回归、对数/指数变换后再线性回归(如 )、非线性最小二乘等。
- 存在强离群点:个别极端观测会把回归线显著拉偏。处理方式:核实数据是否录入错误、使用稳健回归(Huber、RANSAC)、或在论文中说明剔除理由。
- 异方差:残差随 增大呈喇叭状发散,此时 的估计失真,检验与置信区间不可靠。改用加权最小二乘(WLS)或对 做变换(如取对数)。
- 因变量不是连续变量: 为 0/1 二分类时用 Logistic 回归; 为计数(如"事故次数")时考虑 Poisson 回归。
- 时间序列自相关:用 (时间)做 预测时,相邻误差往往相关,违背假设 4,且长期外推预测极不可靠。
- 需要外推很远:样本 ,却要预测 处的 ,线性趋势是否延续完全未知,属于高风险操作。
2.4 与其他回归方法的对比与选择
| 方法 | 适用情形 | 与一元线性回归的关系 |
|---|---|---|
| 一元线性回归 | 单自变量、近似线性 | 本文主角,最简单 |
| 多元线性回归 | 多个自变量共同影响 | 一元回归的矩阵推广, |
| 多项式回归 | 单变量但呈曲线(如二次) | 把 当新特征,本质仍是线性模型 |
| 对数/指数变换 | 指数增长、幂律关系 | 变换 或 后仍用一元线性回归 |
| 加权最小二乘(WLS) | 异方差:给不同样本不同权重 | OLS 的推广,误差大方差小的样本权重小 |
| 稳健回归(Huber / RANSAC) | 存在离群点 | 把平方损失换成对离群点不敏感的抗损失 |
| Logistic 回归 | 因变量是 0/1 二分类 | 形式相似但输出是概率,非连续变量 |
选择原则(竞赛实战):先画散点图 → 呈直线就用一元线性回归(能简单就不复杂);呈曲线先试变换或多项式;残差诊断发现问题再升级到 WLS / 稳健回归。论文中按"先简单后复杂"的递进逻辑写,最能体现建模思路。
三、算法指标
下面每个指标都给出:中文名、公式、取值范围、方向(越大/越小越好)、如何解读。符号定义见第五节。
3.1 决定系数 R²(拟合优度)
- 取值范围:(含截距的一元回归中);
- 方向:越大越好, 表示完美拟合, 表示直线不比"用均值 硬猜"强;
- 解读: 的总变异中能被 的线性关系解释的比例。例如 表示" 解释了 约 97% 的波动"。注意:一元回归中 (Pearson 相关系数的平方); 高只说明"拟合好",不代表有因果关系,也不代表预测能力强(外推时尤其如此)。
3.2 残差标准误 RSE(Residual Standard Error)
- 取值范围:,单位与 相同;
- 方向:越小越好;
- 解读:模型"平均而言预测错多少",是误差标准差 的无偏估计。它直接进入所有标准误、置信区间、预测区间的公式,是"模型精度"最根本的度量。若 ,说明模型已把能解释的都解释了。
3.3 均方根误差 RMSE(Root Mean Squared Error)
- 取值范围:,单位与 相同;
- 方向:越小越好;
- 解读:预测值与真实值偏差的"典型大小"。与 RSE 的区别仅在分母( vs ),两者数值接近(RMSE 略小)。因含平方项,对大误差(离群点)非常敏感:一个残差为 10 的点对 RMSE 的贡献相当于 100 个残差为 1 的点。竞赛中 RMSE 是不同模型横向对比的通用"货币"。
3.4 平均绝对误差 MAE(Mean Absolute Error)
- 取值范围:,单位与 相同;
- 方向:越小越好;
- 解读:预测偏差的"平均大小",比 RMSE 对离群点更稳健(没有平方放大)。恒有 。实用技巧:若 RMSE 明显大于 MAE(如超过 1.5 倍),提示数据中存在大残差的离群点,值得去残差图里找一找。
3.5 平均绝对百分比误差 MAPE(Mean Absolute Percentage Error)
- 取值范围:,以百分比计;
- 方向:越小越好,一般 MAPE < 10% 认为预测精度优秀,10%~20% 良好;
- 解读:误差占真实值的平均百分比,无量纲,因此可以跨数据集、跨量纲比较。致命缺陷:当 接近 0 时该项会爆炸(除以 0),所以只适用于 远离 0 的数据(如销量、价格),不适用于有正有负或含 0 的序列。
3.6 斜率的 t 检验与 p 值
- 原假设:(" 对 没有线性影响");备择假设 ;
- 取值范围:;;
- 方向: 越大越好(越显著); 越小越好,常用阈值 (显著)、(高度显著);
- 解读: 统计量服从自由度 的 分布, 值是在 成立时观察到当前或更极端 的概率。 就拒绝原假设,结论:" 对 的影响在 0.05 水平上显著"。截距 的检验形式完全相同(),但截距是否显著通常不是关注重点。
3.7 整体 F 检验(回归显著性检验)
- 原假设:(整个回归不显著);
- 取值范围:;;
- 方向: 越大越好(越显著); 越小越好;
- 解读:检验"回归整体是否比'用均值硬猜'显著更好"。MSR/MSE 比值越大,说明解释的部分远大于噪声。一元回归的独有结论:,两个检验完全等价( 值也相同),所以一元回归中通常只报一个; 检验的真正用武之地在多元回归(检验"所有斜率同时为 0")。
3.8 Pearson 相关系数 r(线性相关强度)
- 取值范围:;
- 方向: 越大线性相关越强; 正相关(同增同减), 负相关, 无线性相关;
- 经验分级: 强相关; 中等相关; 弱相关; 极弱或无关(分级仅供参考,还要结合 值);
- 解读:度量线性相关的方向与强度。与回归斜率的关系:( 为样本标准差)。一元回归中 。警惕: 只能反映线性关系——( 对称取值)的 会接近 0,但两者存在完美的非线性关系。
3.9 回归系数的 95% 置信区间
- 取值范围:一个实数区间;
- 方向:区间越窄估计越精确;区间不包含 0 等价于该系数在 0.05 水平上显著(与 t 检验结论一致);
- 解读:重复抽样 100 次,约 95 次的置信区间会覆盖真实参数。例如" 的 95% CI 为 "表示斜率真值大概率落在这个范围内,且因为不包含 0,斜率显著不为 0。若真实参数(模拟题里已知)落在区间内,说明估计与真值一致,是对整套计算的一次"自检"。
3.10 指标汇总表
| 指标 | 中文名 | 公式 | 取值范围 | 方向 | 一句话解读 |
|---|---|---|---|---|---|
| R² | 决定系数 | 越大越好 | 解释了 变异的比例 | ||
| RSE | 残差标准误 | 越小越好 | 误差标准差 的无偏估计 | ||
| RMSE | 均方根误差 | 越小越好 | 典型预测偏差,对离群点敏感 | ||
| MAE | 平均绝对误差 | 越小越好 | 平均偏差,比 RMSE 稳健 | ||
| MAPE | 平均绝对百分比误差 | 越小越好 | 误差占真值百分比, 不能近 0 | ||
| t | 斜率 t 统计量 | 越大越好 | 检验 , 即显著 | ||
| F | F 统计量 | 越大越好 | 整体回归显著性,一元中 | ||
| r | Pearson 相关系数 | 越大越好 | 线性相关方向与强度, | ||
| CI | 系数 95% 置信区间 | 区间 | 越窄越好 | 不含 0 即显著;覆盖真值即自检通过 |
四、可视化图表
线性回归不是"算出系数就完事",画图诊断和算指标同等重要。以下 5 张图覆盖"拟合效果展示 + 三大假设检验(线性、同方差、正态性)+ 预测质量检验",全部代码见第六节,图片自动保存到 figures/ 目录。
4.1 五张图速查表
| 图名(输出文件) | 用途 | 关键解读点 |
|---|---|---|
① 散点 + 回归直线 + 95% 置信带 + 95% 预测带(slr_fit_with_bands.png) | 展示整体拟合效果与两种区间的区别 | 点应沿直线均匀分布;置信带(内圈)比预测带(外圈)窄;两条带在 处最窄、向两端变宽;约 95% 的点落在预测带内 |
② 残差-拟合值散点图(slr_resid_fitted.png) | 检验非线性与异方差 | 好的图形:残差围绕 水平线随机散布、无任何形状;曲线形 = 非线性;喇叭形 = 异方差 |
③ 残差 QQ 图(slr_resid_qq.png) | 检验残差正态性 | 好的图形:点贴近 45° 参考直线;两端翘起 = 重尾;S 形弯曲 = 偏态 |
④ 残差-自变量散点图(slr_resid_x.png) | 检验 与残差是否还有系统性关系 | 与图②互补:好的图形同样是无形状的随机散布;若 增大时残差出现趋势或发散,说明模型遗漏了 的某些信息 |
⑤ 实际值-预测值散点 + 对角线(slr_actual_pred.png) | 检验模型整体预测能力 | 好的图形:点紧贴 对角线;点在对角线下方 = 预测偏高;点整体偏移或发散 = 模型有系统偏差 |
4.2 每张图"好"与"异常"的特征
图① 散点 + 回归直线 + 置信带 + 预测带
-
好图特征:数据点呈"细长条带"沿直线分布;约 95% 的点落在预测带(外圈)内;置信带明显窄于预测带。
-
异常特征:点呈曲线弯曲(线性假设不成立);点呈扇形发散(异方差);少数点远离直线且把直线拽向自己(强离群点/杠杆点)。
-
区间公式(在 处):
- 均值 的置信带:
- 个体 的预测带:
预测带比置信带多一个根号下的 "1",所以更宽——预测"一个个体的取值"永远比估计"平均水平"更难。
图② 残差-拟合值图(最重要的诊断图)
- 好图特征:残差像"一勺随机撒的芝麻",围绕 水平线均匀分布,无趋势、无形状、无聚集。
- 异常特征一(非线性):残差呈 U 形或倒 U 形弯曲 → 真实关系是曲线,应换多项式或变换;
- 异常特征二(异方差):残差呈喇叭形/漏斗形(拟合值大的一侧残差更散)→ 用 WLS 或取对数;
- 异常特征三(离群点):个别点残差远超其他点(如超过 )→ 核查数据或稳健回归。
图③ 残差 QQ 图
- 好图特征:样本分位数点几乎贴在 参考线上,两端略有抖动属正常( 越小越抖)。
- 异常特征:左端下垂右端上翘(S 形)= 重尾分布;整体弯曲偏离直线 = 偏态;极端离群点表现为两端孤立的点。
- 注意: 较大(如 )时,中心极限定理保证 /F 检验对正态性不敏感,QQ 图轻微偏离不必过度紧张。
图④ 残差-自变量图
- 好图特征:同图②,残差随机散布在 0 附近,与 没有系统性关系。
- 异常特征:若出现明显趋势(如 大的一侧残差持续为正),说明模型还"欠拟合", 的某些信息没被用尽(可考虑加入 项);若出现周期波动,可能遗漏了周期性因素。
图⑤ 实际值-预测值图
- 好图特征:点集中在对角线 附近,带宽约为 ,无系统偏移。
- 异常特征:点在对角线一侧成片分布 = 系统性高估/低估;低值区与高值区偏离方向相反 = 非线性未捕捉;整体离对角线远 = 模型解释力弱。
五、符号说明
| 符号 | 含义 | 示例/单位 |
|---|---|---|
| 自变量(解释变量) | 广告投入/万元 | |
| 因变量(响应变量) | 销售额/万元 | |
| 样本量 | ||
| 模型参数个数(含截距) | 一元回归 | |
| 截距(真实值) | 3( 的单位) | |
| 斜率(真实值), 每增 1 单位 的平均变化 | 2( 的单位 / 的单位) | |
| 的最小二乘估计 | 由样本算出 | |
| 随机误差项 | ||
| 误差方差 | 4(本例) | |
| 的无偏估计(= MSE) | ||
| 残差标准差估计(= RSE) | 约 2.0 | |
| 的样本均值 | ||
| 第 个样本的拟合值 | ||
| 第 个样本的残差 | ||
| 的离差平方和 | ||
| 的离差平方和 | ||
| 离差交叉积和 | ||
| SST | 总平方和 | |
| SSR | 回归平方和(模型解释的部分) | |
| SSE | 残差平方和(解释不了的部分) | |
| MSR | 回归均方 | |
| MSE | 残差均方 | |
| R² | 决定系数 | ,无量纲 |
| RSE | 残差标准误 | 与 同单位 |
| RMSE | 均方根误差 | 与 同单位 |
| MAE | 平均绝对误差 | 与 同单位 |
| MAPE | 平均绝对百分比误差 | % |
| Pearson 相关系数 | 无量纲, | |
| 统计量 | 无量纲 | |
| 统计量 | 无量纲 | |
| 值 | 原假设成立时观察到当前结果的概率 | , 显著 |
| 自由度 的 分布双侧 95% 临界值 | 时约 1.98 | |
| SE | 标准误(Standard Error) | |
| CI | 置信区间(Confidence Interval) | 95% CI |
| PI | 预测区间(Prediction Interval) | 95% PI |
| 显著性水平 | 0.05 | |
| 原假设 |
六、可运行程序(完整代码)
环境要求:Python 3.12,依赖 numpy、scipy、scikit-learn、matplotlib、pandas(
pip install numpy scipy scikit-learn matplotlib pandas)。以下所有代码块按顺序拼接保存为slr_demo.py,在本文档所在目录运行即可:控制台打印全部指标,并在figures/子目录生成 5 张诊断图。
# -*- coding: utf-8 -*-
"""
============================================================
一元线性回归完整示例:手写实现 + sklearn 对照
------------------------------------------------------------
数据(合成):x ~ U(0, 20),y = 3 + 2x + ε,ε ~ N(0, 2^2),n = 100
输出:控制台打印全部常用指标 + figures/ 目录下 5 张诊断图
依赖:numpy、scipy、scikit-learn、matplotlib、pandas
============================================================
"""
# ========== 0. 导入库与全局设置 ==========
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
from sklearn.linear_model import LinearRegression
# ---- matplotlib 中文显示设置(防止图内中文乱码)----
plt.rcParams["font.sans-serif"] = ["PingFang SC", "Arial Unicode MS", "SimHei"]
plt.rcParams["axes.unicode_minus"] = False # 让负号"-"正常显示
# ---- 图片输出目录(相对当前工作目录的 figures/ 子目录)----
FIG_DIR = "figures"
os.makedirs(FIG_DIR, exist_ok=True)
# ========== 1. 数据生成 ==========
np.random.seed(42) # 固定随机种子,保证每次运行结果完全一致
n = 100 # 样本量
beta0_true, beta1_true = 3.0, 2.0 # 真实截距与斜率(待估计的真值)
sigma_true = 2.0 # 误差标准差 σ(方差 σ² = 4)
x = np.random.uniform(0, 20, n) # 自变量 x:均匀分布 U(0, 20)
eps = np.random.normal(0.0, sigma_true, n) # 随机误差 ε ~ N(0, 2²)
y = beta0_true + beta1_true * x + eps # 因变量 y = 3 + 2x + ε
print(f"已生成 n = {n} 个样本,x ∈ [{x.min():.2f}, {x.max():.2f}],"
f"y ∈ [{y.min():.2f}, {y.max():.2f}]")
# ========== 2. 手写实现:OLS 参数估计(不调用任何拟合库) ==========
# 2.1 先计算三个基础统计量(含义见文档第五节符号表)
x_bar = np.mean(x) # x 的样本均值
y_bar = np.mean(y) # y 的样本均值
Sxx = np.sum((x - x_bar) ** 2) # x 的离差平方和
Syy = np.sum((y - y_bar) ** 2) # y 的离差平方和
Sxy = np.sum((x - x_bar) * (y - y_bar)) # 离差交叉积和
# 2.2 回归系数的最小二乘估计:β̂1 = Sxy / Sxx,β̂0 = ȳ − β̂1·x̄
beta1_hat = Sxy / Sxx
beta0_hat = y_bar - beta1_hat * x_bar
# 2.3 拟合值与残差
y_hat = beta0_hat + beta1_hat * x # 拟合值 ŷ_i
resid = y - y_hat # 残差 e_i = y_i − ŷ_i
# 2.4 平方和分解:SST = SSR + SSE
SSE = np.sum(resid ** 2) # 残差平方和
SSR = np.sum((y_hat - y_bar) ** 2) # 回归平方和
SST = Syy # 总平方和
# 2.5 残差方差的无偏估计(除以 n−2:估计了 2 个参数,损失 2 个自由度)
sigma2_hat = SSE / (n - 2) # 即 MSE
sigma_hat = np.sqrt(sigma2_hat) # 残差标准差估计 σ̂(= RSE)
# ========== 3. 手写实现:拟合优度与误差指标 ==========
R2 = SSR / SST # 决定系数 R²(一元回归中 R² = r²)
R2_check = 1 - SSE / SST # R² 的等价计算式,用于自检
RSE = np.sqrt(SSE / (n - 2)) # 残差标准误(= σ̂,σ 的无偏估计)
RMSE = np.sqrt(np.mean(resid ** 2)) # 均方根误差(除以 n,与 RSE 略有差别)
MAE = np.mean(np.abs(resid)) # 平均绝对误差
MAPE = np.mean(np.abs(resid / y)) * 100 # 平均绝对百分比误差(%)
# ========== 4. 手写实现:显著性检验与置信区间 ==========
# 4.1 回归系数的标准误(t 检验与置信区间的基础)
se_beta1 = sigma_hat / np.sqrt(Sxx)
se_beta0 = sigma_hat * np.sqrt(1.0 / n + x_bar ** 2 / Sxx)
# 4.2 系数的 t 检验:原假设 H0: β = 0(即"x 对 y 没有线性影响")
t_beta1 = beta1_hat / se_beta1
t_beta0 = beta0_hat / se_beta0
p_beta1 = 2 * (1 - stats.t.cdf(abs(t_beta1), df=n - 2)) # 双侧 p 值
p_beta0 = 2 * (1 - stats.t.cdf(abs(t_beta0), df=n - 2))
# 4.3 回归系数的 95% 置信区间:β̂ ± t_{0.025}(n-2) · SE(β̂)
t_crit = stats.t.ppf(0.975, df=n - 2) # 双侧 95% 临界值,n=100 时约 1.98
ci_beta1 = (beta1_hat - t_crit * se_beta1, beta1_hat + t_crit * se_beta1)
ci_beta0 = (beta0_hat - t_crit * se_beta0, beta0_hat + t_crit * se_beta0)
# 4.4 整体 F 检验:F = MSR / MSE ~ F(1, n-2)(一元回归中 F = t²)
F_stat = (SSR / 1) / (SSE / (n - 2))
p_F = 1 - stats.f.cdf(F_stat, dfn=1, dfd=n - 2)
# 4.5 Pearson 相关系数 r = Sxy / sqrt(Sxx · Syy)
r_pearson = Sxy / np.sqrt(Sxx * Syy) # 等价于 np.corrcoef(x, y)[0, 1]
# 4.6 残差均值:对"零均值假设"的快速自检(理论上应约等于 0)
resid_mean = np.mean(resid)
# ========== 5. sklearn 对照实现 ==========
linreg = LinearRegression()
linreg.fit(x.reshape(-1, 1), y) # sklearn 要求特征为二维数组,形状 (n, 特征数)
beta1_sk = linreg.coef_[0] # 斜率(coef_ 是数组,一元回归只有一个元素)
beta0_sk = linreg.intercept_ # 截距
y_hat_sk = linreg.predict(x.reshape(-1, 1)) # 预测
R2_sk = linreg.score(x.reshape(-1, 1), y) # sklearn 内置的 R² 得分
# ========== 6. 打印全部指标(手写实现 vs sklearn 对照) ==========
# 打印 p 值的小工具:p 值小到浮点数下溢为 0(< 1e-300)时,
# 不显示成误导性的 "0.0000e+00",而是显示 "< 1e-300"
def fmt_p(p_val):
return f"{p_val:.4e}" if p_val > 0 else "< 1e-300"
print("=" * 68)
print("一元线性回归结果(手写实现) 真实参数:β0=3, β1=2, σ=2")
print("=" * 68)
print(f"样本量 n = {n}")
print(f"回归方程:ŷ = {beta0_hat:.4f} + {beta1_hat:.4f}·x")
print(f"系数标准误:SE(β̂0) = {se_beta0:.4f},SE(β̂1) = {se_beta1:.4f}")
print(f"残差均值 = {resid_mean:.3e}(≈ 0,验证零均值假设)")
print(f"残差标准差 σ̂ = {sigma_hat:.4f}(真实值 σ = 2.0)")
print("-" * 68)
print("【拟合优度与误差指标】")
print(f"R² = {R2:.4f} (自检值 {R2_check:.4f},应相等)")
print(f"RSE = {RSE:.4f}")
print(f"RMSE = {RMSE:.4f}")
print(f"MAE = {MAE:.4f}")
print(f"MAPE = {MAPE:.4f} %")
print("-" * 68)
print("【显著性检验】")
print(f"截距 t 检验:t = {t_beta0:.4f},p = {fmt_p(p_beta0)}")
print(f"斜率 t 检验:t = {t_beta1:.4f},p = {fmt_p(p_beta1)}")
print(f"整体 F 检验:F = {F_stat:.4f},p = {fmt_p(p_F)}(一元回归中 F = t²)")
print(f"Pearson 相关系数 r = {r_pearson:.4f}(r² = {r_pearson ** 2:.4f} = R²)")
print("-" * 68)
print("【回归系数 95% 置信区间】")
print(f"β0:[{ci_beta0[0]:.4f}, {ci_beta0[1]:.4f}],覆盖真实值 3?{ci_beta0[0] <= 3 <= ci_beta0[1]}")
print(f"β1:[{ci_beta1[0]:.4f}, {ci_beta1[1]:.4f}],覆盖真实值 2?{ci_beta1[0] <= 2 <= ci_beta1[1]}")
print("=" * 68)
print("手写实现 vs sklearn 对照(应完全一致)")
print("-" * 68)
cmp_df = pd.DataFrame({
"指标": ["β̂0(截距)", "β̂1(斜率)", "R²"],
"手写实现": [f"{beta0_hat:.6f}", f"{beta1_hat:.6f}", f"{R2:.6f}"],
"sklearn": [f"{beta0_sk:.6f}", f"{beta1_sk:.6f}", f"{R2_sk:.6f}"],
})
cmp_df["一致"] = ["是" if c else "否" for c in
[np.allclose(beta0_hat, beta0_sk),
np.allclose(beta1_hat, beta1_sk),
np.allclose(R2, R2_sk)]]
print(cmp_df.to_string(index=False))
print("=" * 68)
# ========== 7. 绘制五张诊断图(保存到 figures/ 目录) ==========
# 画线用的细网格:在 x 范围内取 200 个等距点,曲线更平滑
x_grid = np.linspace(x.min(), x.max(), 200)
y_grid = beta0_hat + beta1_hat * x_grid
# ---- 图 1:散点 + 回归直线 + 95% 置信带 + 95% 预测带 ----
# 均值 E(y|x0) 的标准误 与 个体 y0 预测值的标准误(公式见文档 4.2 节)
se_mean = sigma_hat * np.sqrt(1.0 / n + (x_grid - x_bar) ** 2 / Sxx)
se_pred = sigma_hat * np.sqrt(1.0 + 1.0 / n + (x_grid - x_bar) ** 2 / Sxx)
ci_low, ci_high = y_grid - t_crit * se_mean, y_grid + t_crit * se_mean
pi_low, pi_high = y_grid - t_crit * se_pred, y_grid + t_crit * se_pred
plt.figure(figsize=(9, 6))
plt.scatter(x, y, s=30, alpha=0.7, color="steelblue", label="观测点")
plt.plot(x_grid, y_grid, color="crimson", lw=2, label="拟合直线")
plt.fill_between(x_grid, ci_low, ci_high, color="orange", alpha=0.30,
label="95% 置信带(均值)")
plt.fill_between(x_grid, pi_low, pi_high, color="green", alpha=0.12,
label="95% 预测带(个体)")
plt.text(0.05, 0.92, f"ŷ = {beta0_hat:.2f} + {beta1_hat:.2f}x",
transform=plt.gca().transAxes, fontsize=12)
plt.xlabel("x")
plt.ylabel("y")
plt.title(f"散点图与回归直线(R² = {R2:.3f})")
plt.legend(loc="upper left")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(os.path.join(FIG_DIR, "slr_fit_with_bands.png"), dpi=150)
# ---- 图 2:残差-拟合值散点图(检验非线性 / 异方差) ----
plt.figure(figsize=(9, 6))
plt.scatter(y_hat, resid, s=30, alpha=0.7, color="steelblue")
plt.axhline(0, color="crimson", ls="--", lw=1.5)
plt.xlabel("拟合值 ŷ")
plt.ylabel("残差 e")
plt.title("残差-拟合值图(检验非线性与异方差)")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(os.path.join(FIG_DIR, "slr_resid_fitted.png"), dpi=150)
# ---- 图 3:残差 QQ 图(检验正态性) ----
resid_std = resid / sigma_hat # 标准化残差(近似 N(0,1))
theo_quantile = stats.norm.ppf((np.arange(1, n + 1) - 0.5) / n) # 标准正态理论分位数
sorted_resid = np.sort(resid_std) # 标准化残差的样本分位数(升序)
plt.figure(figsize=(9, 6))
plt.scatter(theo_quantile, sorted_resid, s=30, alpha=0.7, color="steelblue")
lim = np.max(np.abs(np.concatenate([theo_quantile, sorted_resid])))
plt.plot([-lim, lim], [-lim, lim], color="crimson", ls="--", lw=1.5,
label="y = x 参考线")
plt.xlabel("标准正态理论分位数")
plt.ylabel("标准化残差样本分位数")
plt.title("残差 QQ 图(检验正态性)")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(os.path.join(FIG_DIR, "slr_resid_qq.png"), dpi=150)
# ---- 图 4:残差-自变量散点图(检验 x 与残差的系统性关系) ----
plt.figure(figsize=(9, 6))
plt.scatter(x, resid, s=30, alpha=0.7, color="steelblue")
plt.axhline(0, color="crimson", ls="--", lw=1.5)
plt.xlabel("x")
plt.ylabel("残差 e")
plt.title("残差-自变量图(检验遗漏的 x 信息)")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(os.path.join(FIG_DIR, "slr_resid_x.png"), dpi=150)
# ---- 图 5:实际值-预测值散点 + 对角线 ----
plt.figure(figsize=(9, 6))
plt.scatter(y, y_hat, s=30, alpha=0.7, color="steelblue")
vmin = min(y.min(), y_hat.min())
vmax = max(y.max(), y_hat.max())
plt.plot([vmin, vmax], [vmin, vmax], color="crimson", ls="--", lw=1.5,
label="y = ŷ 对角线")
plt.xlabel("实际值 y")
plt.ylabel("预测值 ŷ")
plt.title(f"实际值-预测值图(R² = {R2:.3f})")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(os.path.join(FIG_DIR, "slr_actual_pred.png"), dpi=150)
print(f"5 张诊断图已保存到 {os.path.abspath(FIG_DIR)}/ 目录")
# ========== 8. 显示图形 ==========
# 在带图形界面的环境(本地 IDE / 终端)中会弹出 5 个窗口,关闭后脚本结束;
# 在服务器等无图形界面环境中 matplotlib 会自动跳过显示,仅保留 PNG 图片。
plt.show()
七、结果解读与注意事项
7.1 运行输出解读(以本例合成数据为例)
以第六节代码生成的合成数据(真实参数 ,)为例,运行脚本后控制台输出如下:
已生成 n = 100 个样本,x ∈ [0.11, 19.74],y ∈ [2.97, 44.04]
====================================================================
一元线性回归结果(手写实现) 真实参数:β0=3, β1=2, σ=2
====================================================================
样本量 n = 100
回归方程:ŷ = 3.4302 + 1.9540·x
系数标准误:SE(β̂0) = 0.3406,SE(β̂1) = 0.0306
残差均值 = 2.665e-16(≈ 0,验证零均值假设)
残差标准差 σ̂ = 1.8144(真实值 σ = 2.0)
--------------------------------------------------------------------
【拟合优度与误差指标】
R² = 0.9765 (自检值 0.9765,应相等)
RSE = 1.8144
RMSE = 1.7962
MAE = 1.4021
MAPE = 9.6883 %
--------------------------------------------------------------------
【显著性检验】
截距 t 检验:t = 10.0718,p = < 1e-300
斜率 t 检验:t = 63.7539,p = < 1e-300
整体 F 检验:F = 4064.5604,p = < 1e-300(一元回归中 F = t²)
Pearson 相关系数 r = 0.9882(r² = 0.9765 = R²)
--------------------------------------------------------------------
【回归系数 95% 置信区间】
β0:[2.7543, 4.1060],覆盖真实值 3?True
β1:[1.8932, 2.0148],覆盖真实值 2?True
====================================================================
手写实现 vs sklearn 对照(应完全一致)
--------------------------------------------------------------------
指标 手写实现 sklearn 一致
β̂0(截距) 3.430192 3.430192 是
β̂1(斜率) 1.954023 1.954023 是
R² 0.976457 0.976457 是
====================================================================
5 张诊断图已保存到 figures/ 目录
逐项解读:
- 回归方程 :截距 3.4302 与真值 3 接近(相对误差约 14%),斜率 1.9540 与真值 2 非常接近(相对误差约 2%)。这是"已知真值的模拟数据"独有的自检机会;真实竞赛中我们不知道真值,此时要依靠置信区间和假设检验来判断估计质量。
- :误差标准差估计接近真值 (差距来自样本波动),说明模型已经把 能解释的都解释了,剩下的残差确实"只是噪声"。
- R² = 0.9765: 解释了 约 97.6% 的变异,拟合优度很高。与理论预期吻合:总体水平上 。
- RSE = 1.8144 vs RMSE = 1.7962:两者相差很小(只是分母 与 的差异)。若数据中有强离群点,RMSE 会被抬高并明显大于 MAE;本例 RMSE/MAE ≈ 1.28,无离群点迹象。
- MAPE = 9.69%:平均预测误差约为真实值的 10%,属于"良好"档(本例 全部为正,MAPE 可用)。
- 斜率 t 检验:, 值小到浮点数直接下溢(显示为 "< 1e-300",即 ), 对 的影响高度显著;截距 同样显著。
- F 检验:,且 ,验证了一元回归中"F 检验与斜率 t 检验等价"的结论。
- r = 0.9882:强正相关;,两个指标自洽。
- 95% 置信区间: 不包含 0(与检验显著的结论一致),且覆盖真值 2(自检通过)。区间宽度约 0.12,说明在 、 的条件下斜率估计已经相当精确;若想让区间更窄,需要增大 或减小噪声。
- 手写 vs sklearn:三个量完全一致(
np.allclose判定),证明手写公式与 sklearn 内部实现是同一套 OLS。
7.2 五张诊断图的解读(本例)
- 图①:点沿直线均匀分布,约 95% 的点落在绿色预测带内;橙色置信带在 两端(远离 处)明显变宽——这是"外推精度下降"的直观证据。
- 图②④:残差围绕 0 呈"一坨随机芝麻"状,无 U 形、无喇叭形 → 线性假设与同方差假设成立,模型没有遗漏 的系统性信息。
- 图③:QQ 图点基本贴在参考线上,两端略有抖动( 的正常现象)→ 残差近似正态,检验结论可信。
- 图⑤:点紧贴对角线 → 整体预测无系统偏差,模型可用。
如果其中任何一张图出现 4.2 节描述的"异常特征",请回到第二节的检查清单,考虑变换、WLS 或稳健回归。
7.3 常见坑与应对
- 相关 ≠ 因果: 与 显著线性相关,不代表 是 的原因。可能存在第三个变量(混杂因素)同时影响两者,或纯属巧合(如"冰淇淋销量与溺水人数"都受气温驱动)。竞赛论文中措辞应谨慎:写" 与 存在显著的正相关关系",不要写" 导致 "。
- 外推风险:回归直线只在样本 范围内"有依据"。用 2000-2024 年数据预测 2100 年的值,等于假设线性趋势延续 76 年,通常不可信。预测区间会随外推距离急剧变宽(图①两端即此现象的展示),论文中应明确说明外推假设与风险。
- 异方差的诊断与处理:残差图呈喇叭形即异方差。处理:对 取对数(,适用于方差随均值增大而增大的情形)、使用加权最小二乘(WLS,给波动大的样本小权重)、或改用异方差稳健的标准误(White/HC)。
- 离群点的诊断与处理:识别——散点图中远离直线的点、标准化残差绝对值 的点、RMSE 远大于 MAE 的提示;处理——先核实是否为录入错误;确认真实离群点后,可尝试剔除并在论文中说明理由,或改用稳健回归(Huber、RANSAC)并对比两种结果。切忌"为了让 R² 好看"而偷偷删数据。
- R² 高 ≠ 模型好:著名的"安斯库姆四重奏"(Anscombe's quartet)中四组数据的一元回归结果完全相同(R²、系数、检验都一样),但只有一组真正适合线性回归——另三组分别存在非线性、单离群点、强杠杆点。所以必须先画图、再做诊断、最后下结论。
- p 值显著的陷阱: 值只回答" 是否等于 0",不回答"模型是否好用"。 很大时,微弱的线性关系也会显著;还应同时报告 R² 和 RMSE 的绝对大小。
- 训练/测试数据不分:模型指标应尽量在未参与拟合的测试集上报告(交叉验证或留出法),否则 R² 会系统性虚高。竞赛中常犯的错误是"用全部数据拟合、再用同一批数据夸自己"。
- 变量量纲的影响: 的单位换成米/千米会让 差 1000 倍,解释斜率时务必带上单位(如"每增加 1 万元广告投入,销量平均增加 2.01 万元")。
7.4 竞赛论文写作建议(话术模板)
建模段(先讲为什么用、再讲模型、再报结果):
对 与 绘制散点图,发现二者呈明显线性趋势,故采用一元线性回归对二者关系建模,模型为 ,参数由最小二乘法估计,得回归方程 。模型整体 检验 ,决定系数 ,说明 可解释 约 97.6% 的变异,模型拟合效果良好。
显著性段:
斜率 检验得 ,,表明 对 的影响在 水平上高度显著;斜率 95% 置信区间为 ,不包含 0,进一步印证该结论。斜率估计值为 1.954,即 每增加 1 个单位, 平均增加约 1.95 个单位。
假设检验(诊断)段(评审加分点):
对模型残差进行诊断:残差-拟合值图显示残差随机散布于 0 附近,无 U 形与喇叭形趋势,表明线性假设与同方差假设成立;残差 QQ 图显示分位点贴近参考直线,残差近似服从正态分布。模型假设得到验证,检验与区间结论可信。
预测段:
当 时,点预测为 ,其 95% 预测区间为 。需注意 超出样本范围 ,属于外推,预测结果应在"线性趋势延续"的假设下谨慎使用。
与其他模型对比段:
以一元线性回归为基线(,RMSE ),进一步尝试多项式回归/多元回归,XX 模型将测试集 RMSE 降低至 1.52,较基线提升 15.4%,说明引入非线性项/更多变量能进一步提升预测精度。
八、延伸阅读
- 多元线性回归:把"一个 "推广到"多个 ",模型 ,估计量写成矩阵形式 。一元回归中所有概念(R²、t、F、置信区间、残差诊断)全部平行推广,是学习路径上的直接下一步。注意多元回归中 检验不再等于某个 ,R² 也需用调整 R²()比较不同变量数的模型。
- 加权最小二乘(WLS):当残差诊断发现异方差时,给每个样本赋予权重 (波动大的样本权重小),最小化 。这是 OLS 最直接的"打补丁"式推广。
- 稳健回归(Huber 回归、RANSAC):把平方损失换成对离群点不敏感的抗损失函数(Huber 损失:小残差用平方、大残差用线性),或在估计时自动剔除离群点(RANSAC)。数据脏、离群点多时明显优于 OLS。
- 多项式回归与样条:单变量曲线关系可在 OLS 框架内解决——把 当特征就是多项式回归(注意过拟合,阶数用交叉验证选择);分段多项式样条则更灵活。正则化(Ridge/Lasso)可抑制高阶多项式的过拟合。
- Logistic 回归与广义线性模型(GLM):因变量为 0/1(如"是否违约")时改用 Logistic 回归;计数数据用 Poisson 回归。它们与线性回归共享"线性预测子 "的思想,只是输出层不同。
- 时间序列回归的陷阱:用"时间 "做 时,误差往往自相关(违背 GM 假设 4),应进一步学习自相关检验(Durbin-Watson)、差分、ARIMA 等方法;竞赛中的趋势外推预测题常在这条路上深化。
- 推荐资源:James 等《统计学习导论》(ISLR,入门首选,第 3 章即一元线性回归);茆诗松等《概率论与数理统计》(数理统计部分,中文教材,推导完整);姜启源等《数学模型》(竞赛经典,各章含回归应用实例);scikit-learn 官方文档 LinearRegression 页面(查 API 细节)。