简介:《基于回归分析的人口数量预测》是一份面向统计学、经济分析及数据建模初学者的课程实验论文文档,围绕人口数量与经济指标的关联预测展开,适合用作回归分析课程作业参考或建模入门范例。资源包内共1个doc文件,大小约442KB,正文按一元线性回归、多元线性回归与非线性模型组织,涉及指标选择、样本确定、散点图绘制、理论模型设定、回归诊断、数据中心化标准化、逐步回归、多重共线性检测、主成分分析,以及曲线拟合和Logistic模型等内容。已有397人学习下载,说明其在同类课程资料中具有一定参考热度。读者可从中获得较完整的实验论文结构、回归建模流程与结果分析思路,便于对照理解从变量关系描述到人口预测的完整推演,也可借鉴其章节安排和统计方法组合,用于自己的课程论文或小型数据分析实践。
1. 反直觉的开局:R² 冲到 0.999,预测误差反而更小
用回归分析做人口数量预测,最容易被一个数字误导:拟合优度。同一份 1980—2014 年的样本里,粮食产量对总人口的一元回归 R² 只有 0.826,四变量多元回归升到 0.944,三次曲线直接达到 0.999。可 2014 年的预测值分别是 142165 万、138182 万、136175 万,真实值 136782 万;拟合最好的那个,误差才 607 万,最差的那个差了 5383 万。这不是巧合,而是回归分析在时间序列上的一种稳定表现:R² 衡量的是样本内解释了多少方差,预测检验的是样本外还剩多少结构。这条分界线不搞清楚,做多少个模型都是在原地打转。
2. 一元线性回归:0.909 的相关系数是怎么变成预测误差的
2.1 最小二乘到底在最小化什么
一元线性回归的理论模型写作 y = β₀ + β₁x + ε,其中 x 是粮食产量(万吨),y 是总人口(万人),样本覆盖 35 个年度观测。参数估计用的是普通最小二乘,目标是让残差平方和 Q(β₀, β₁) = Σ(yᵢ − β₀ − β₁xᵢ)² 最小。对 β₀ 和 β₁ 分别求偏导并令其为零,会得到两条正规方程,解出来就是教科书上那个矩阵形式 β = (XᵀX)⁻¹Xᵀy。这个式子的几何含义是:把观测向量 y 正交投影到由设计矩阵 X 的列向量张成的空间上,投影点就是拟合值 ŷ,残差向量与这个空间垂直——最小二乘本质上是一次投影,不是一次“找线”。
import numpy as np # x: 粮食产量(万吨),y: 总人口(万人),n = 35 X = np.column_stack([np.ones_like(x), x]) # 第一列全 1,对应截距项 beta, *_ = np.linalg.lstsq(X, y, rcond=None) # 解正规方程 X'Xβ = X'y resid = y - X @ beta # 残差向量,与 X 的列空间正交 n, p = X.shape sigma2 = resid @ resid / (n - p) # 残差方差,自由度 n - p = 33 XtX_inv = np.linalg.inv(X.T @ X) se = np.sqrt(np.diag(XtX_inv) * sigma2) # 每个系数的标准误 sst = (y - y.mean()) @ (y - y.mean()) r2 = 1 - (resid @ resid) / sst # R² = 1 - SSE/SST print(beta, se, r2) # [53054.404, 1.468] [5500.013, 0.117] 0.826这段代码里有两处容易被忽略。第一处是自由度 n − p:35 个样本估了 2 个参数,残差方差除以 33 而不是 35,样本量小的时候这个差别会直接体现在标准误上。第二处是lstsq而不是显式求逆,正规方程在自变量量级悬殊时条件数很差,显式inv会把数值误差放大。把这两个数带回去:β₁ = 1.468,标准误 0.117,t 值 12.513,p 值远小于 0.001;β₀ = 53054.404,标准误 5500.013。模型摘要里 R = 0.909、R² = 0.826、调整后 R² = 0.821、残差标准误 4985.997,这些数字互相自洽——残差标准误约等于总人口标准差的 40%,说明模型只吃掉了六成左右的波动。
2.2 系数 1.468 的量纲与截距 53054 的失效区间
回归系数不是无量纲的抽象数,它带着“每单位自变量对应多少单位因变量”的量纲。1.468 的准确读法是:在 1980—2014 这个样本区间内,粮食产量每多 1 万吨,总人口平均多 1.468 万人。这个解释只对样本区间有效,越界就是外推。截距 53054.404 更极端——它表示粮食产量为 0 时总人口是 5.3 亿,这在物理上根本不存在,它的作用只是把回归直线抬到正确高度,不承担任何实际含义。很多人写报告时喜欢给截距也安一个解释,这是常见的误用。
系数能不能用,看的是系数与标准误的比值。1.468 / 0.117 = 12.5,这个 t 值对应的 p 值小于 0.001,意味着“粮食产量对总人口没有线性影响”这个原假设被拒绝。但显著不等于正确,它只说明在假定模型形式正确的前提下,这个斜率不太可能是 0。真正该警惕的是:模型的残差标准误接近 5000 万,而 2014 年的人口真实值是 136782 万,用 ŷ = 53054.404 + 1.468 × 60702.6 算出来的预测值是 142165.84 万,误差 5383.84 万,相对误差约 3.9%。这个误差大约是一个残差标准误,从统计上看“正常”,从业务上看已经偏了一个中等省份的体量。
更有意思的是误差的方向。预测值系统性地高于真实值,残差均值为负。这说明模型不是随机抖动,而是整体抬高了——它把样本末期的增长惯性外推了出去,而真实的人口增长在那个阶段已经开始减速。线性模型没有“减速”这个能力,它的斜率是常数,只能一路向上。
2.3 R² = 0.826 的另一种解释:两个上升序列的伪回归
粮食产量从 1980 年的 32055 万吨涨到 2014 年的 60702 万吨,总人口从 98705 万涨到 136782 万,两条曲线在整个样本区间里几乎单调上升。两个都随时间上升的序列做回归,R² 天然会很高,这跟它们之间有没有因果关系关系不大。统计上把这类现象叫伪回归:回归捕捉到的不是 x 对 y 的驱动,而是两者共享的时间趋势。
判断伪回归最直接的办法是看残差还剩下什么。如果模型真的解释了关系,残差应该表现为围绕 0 的随机波动,没有明显趋势、没有明显自相关。可以算一下残差的一阶自相关系数和 Durbin-Watson 统计量:
r = resid rho = (r[1:] @ r[:-1]) / (r @ r) # 一阶自相关系数,越接近 1 趋势残留越重 dw = 2 * (1 - rho) # DW 统计量,理想值约 2,接近 0 说明强正自相关 print(rho, dw)如果 rho 明显大于 0、DW 明显小于 2,就说明残差里还留着时间结构,模型漏掉了变量。这时候系数的标准误被低估,t 值虚高,显著性检验失去意义——你以为自己找到了强关系,其实只是把趋势拟合了一遍。修法有三条:把时间 t 作为自变量显式放进模型(这正是后面三次曲线在做的事)、对序列做差分、或者改用能处理趋势的模型结构。这三条路的取舍不一样,显式加趋势项保留水平信息但容易过拟合,差分去掉趋势但丢失长期水平,选哪条取决于你要的是解释还是预测。
顺带说一句,t 检验、方差分析在线性回归框架下都是它的特例,卡方检验也能用 logistic 回归替代,所以一元线性回归这一层的机制搞清楚,后面的多元、非线性都是在同一个投影框架上做扩展。
3. 多元回归与逐步回归:加变量和删变量分别在优化什么
3.1 中心化与标准化的真实作用
把粮食产量、GDP、出生率、死亡率四个变量一起放进模型之前,先做了中心化和标准化。常见的误解是“标准化能消除共线性”,这是错的。标准化的作用有两个:一是让不同量纲的系数可比,标准化后的系数就是 Beta 系数,可以直接比较哪个变量影响更大;二是改善设计矩阵的数值条件,GDP 的量级是十万级、出生率是个位数,直接放进 XᵀX 会让条件数变得很难看,求逆时的数值误差被放大。
Z = (X[:, 1:] - X[:, 1:].mean(0)) / X[:, 1:].std(0, ddof=1) # 按列标准化 Z = np.column_stack([np.ones(len(Z)), Z]) # 补回截距列 beta_z, *_ = np.linalg.lstsq(Z, y, rcond=None) # beta_z[1:] 就是标准化系数 Beta;R²、t 值、F 值与未标准化时完全一致关键结论:标准化不改变 R²、不改变 t 值、不改变 F 值,它只改变系数的表达方式。共线性是变量之间的相关信息重叠,跟量纲没有关系,除以一个标准差不会让两个高度相关的变量变得不相关。把标准化当成共线性药方,是这套流程里最常见的误解之一。
3.2 四变量模型的 t 检验读法
四变量模型的输出是:R = 0.972、R² = 0.944、调整后 R² = 0.936、残差标准误 2967.57。方差分析表里回归平方和 4448849243.98、自由度 4、均方 1112212310.99、F = 126.295、p < 0.001,残差平方和 264193847.62、自由度 30。整体模型显著,R² 比一元模型高了 0.118,残差标准误从 4986 降到 2968,看起来是一次明确的改进。
逐个系数看就完全是另一回事了。常数项 124574.667(标准误 21501.729,t = 5.794);粮食产量 1.045(标准误 0.152,t = 6.893,p < 0.001);GDP 系数 −0.005(标准误 0.008,t = −0.635,p = 0.531);出生率 −1460.798(标准误 262.207,t = −5.571,p < 0.001);死亡率 −4060.272(标准误 3649.700,t = −1.112,p = 0.275)。
GDP 这一行值得单独说。系数是 −0.005,标准误是 0.008,标准误比系数本身还大,95% 置信区间从负数跨到正数,把 0 包在里面。这不是“GDP 影响很小”,而是“这份数据说不出 GDP 有什么影响”。原因不难理解:GDP 从 1980 年的 4551 亿涨到 2014 年的 643974 亿,跨了 140 倍,系数被压到千分位以下是量级压缩的必然结果;同时 GDP 和粮食产量、出生率都高度相关,它的独立信息本来就不多。死亡率同理,标准误 3649.7 是系数绝对值的九成,同样不显著。
再看出生率的系数符号:−1460.798。字面读法是“出生率每上升 1 个千分点,总人口下降 1460.798 万”,这跟常识完全相反。真实原因是出生率在这 35 年里持续下降、总人口持续上升,回归捕捉到的是两条反向趋势的统计对应,不是因果。这类符号反常是趋势混淆的典型信号,看到它就该停下来检查残差和变量选择,而不是去解释这个负号。
按这张系数表拼出的方程是 ŷ = 124574.667 + 1.045x₁ − 0.005x₂ − 1460.798x₃ − 4060.272x₄,代入 2014 年数据得到 138182.24 万,与真实值相差 1400.25 万。比一元模型的 5383 万好了不少,但模型里有两个变量是统计上站不住的,带着它们做预测等于让噪声参与计算。
3.3 逐步回归的三步与选择偏差
逐步回归的思路是“有进有出”:每次引入一个变量,引入后对已在模型里的所有变量重新做显著性检验,不显著的剔除,直到没有变量能进也没有变量该出。这套流程在这份数据上走了三步:第一步只保留出生率,常数项 165146.388、系数 −2687.949;第二步加入粮食产量,出生率系数变成 −1534.270、粮食产量系数 0.816;第三步加入死亡率,三个系数分别是粮食产量 1.012、出生率 −1366.202、死亡率 −5681.557,常数项 134678.878。
最终方程是 ŷ = 134678.878 + 1.012x₁ − 1366.202x₃ − 5681.557x₄,2014 年预测值 138506.22 万,误差 172.22 万,是这套流程里除三次曲线外表现最好的结果。注意每一步系数都在变:出生率的系数从 −2687.949 变到 −1534.270 再变到 −1366.202,说明它和后来进入的变量存在信息重叠,单独看第一步的系数会严重高估它的作用。这是逐步回归最有价值的副产品——系数在加入变量过程中的漂移幅度,本身就是共线性的一个温度计。
但逐步回归有一个绕不过去的问题:选择偏差。变量是按 p 值挑的,挑完之后再拿同一份数据去算 p 值和置信区间,这些指标已经不具备名义上的统计意义了。经过筛选的模型,系数被系统性地放大,标准误被系统性地低估,置信区间偏窄,看起来比实际更可靠。这是数据窥探的一种形式,样本量越小越明显。所以逐步回归的结果适合当作“变量重要性排序”来参考,不适合直接引用它的 p 值写结论。
还有一个反直觉的现象值得注意:用了逐步回归的预测误差 172 万,反而比全模型 1400 万更好,尽管全模型的 R² 更高。原因在于剔除 GDP 之后,模型少了一个方差极大的系数,样本外预测的方差跟着降下来。这说明预测精度和拟合优度不是一回事:拟合优度奖励的是样本内解释力,预测精度惩罚的是系数方差,两者在最优点上并不重合。这条规律在后面的主成分分析里还会再出现一次。
4. 多重共线性:特征值、条件指数、VIF 三种判据的底层
4.1 VIF = 1/(1 − R²ⱼ) 是怎么推出来的
方差膨胀因子的定义看着像个经验公式,其实有严格推导。把第 j 个变量对其余所有变量做回归,得到一个 R²ⱼ,那么第 j 个系数估计的方差是 Var(β̂ⱼ) = σ² / (SSTⱼ × (1 − R²ⱼ))。对比一下变量之间完全正交时的情况 Var = σ² / SSTⱼ,多出来的那个因子 1/(1 − R²ⱼ) 就是方差被“膨胀”的倍数。R²ⱼ 越接近 1,说明第 j 个变量能被其他变量几乎完全解释,它在模型里几乎不提供新信息,系数的方差就被放大到无法接受。
def vif(X): out = [] for j in range(X.shape[1]): yj = X[:, j] Xo = np.delete(X, j, axis=1) # 其余变量 Xo = np.column_stack([np.ones(len(Xo)), Xo]) b, *_ = np.linalg.lstsq(Xo, yj, rcond=None) r2j = 1 - ((yj - Xo @ b) @ (yj - Xo @ b)) / ((yj - yj.mean()) @ (yj - yj.mean())) out.append(1 / (1 - r2j)) # VIF = 1/(1-R²ⱼ) return out经验阈值是 VIF > 10 视为严重共线,> 5 需要留意。这份数据剔除 GDP 之后,出生率和粮食产量的 VIF 都是 2.502,死亡率 1.808;三个变量同时在场时,粮食产量升到 4.146,出生率 2.868,死亡率 1.808。全部低于 10,说明剩下的变量之间没有严重共线,不用再剔。VIF 的倒数是容许度,含义正好相反:容许度越小,说明这个变量被其他变量“容许”的空间越小,情况越糟。
4.2 特征值与条件指数:给设计矩阵做一次病情报告
VIF 是逐个变量看的,特征值和条件指数是从整体看设计矩阵的形态。对标准化后的相关矩阵做特征分解,得到一组特征值 λ₁ ≥ λ₂ ≥ … ≥ λₚ,条件指数定义为 κⱼ = √(λmax / λⱼ)。特征值接近 0,意味着设计矩阵在某个方向上近乎塌缩;条件指数大于 10 提示存在共线性,大于 30 就相当严重了。
R = np.corrcoef(X, rowvar=False) # 相关矩阵 lam = np.sort(np.linalg.eigvalsh(R))[::-1] # 从大到小排 cond_idx = np.sqrt(lam[0] / lam) # 每个维度的条件指数 # lam 中若出现 0.001 量级、cond_idx 中出现 30 以上,说明该维度近乎降秩这份数据跑出来的结果是:特征值有 3 个接近 0,条件指数有 3 个超过 10,最大的达到 86 量级,相关矩阵里死亡率的对应值接近 1。三条判据同时报警,结论一致——存在多重共线性。这里的机制值得说透:最小二乘的解是 (XᵀX)⁻¹Xᵀy,XᵀX 的最小特征值接近 0 时,它的逆矩阵在那个方向上的元素会变得极大,系数的估计方差被无限放大,样本稍有扰动系数就剧烈摆动。你看到的“系数符号反常”“标准误巨大”“加一个样本系数就变脸”,根源都在这里。
三项判据的关系也值得理清:VIF 是变量级的局部诊断,回答“哪个变量多余”;特征值和条件指数是矩阵级的全局诊断,回答“整个设计有多病”。两者不总是一致,一个变量单独看 VIF 不高,几个变量组合起来仍可能让某个维度塌缩,所以两套指标要一起看。
4.3 主成分回归:能治共线性,但代价是什么
主成分分析的思路是把相关的原始变量做正交变换,转成一组互不相关的新变量,再按方差贡献率取前几个。这份数据的第一主成分贡献 76.004%,第二主成分贡献 18.176%,累计 94.179%,按“累计贡献率够高”的惯例取前两个就够。这样处理之后,共线性确实被消掉了——新变量之间相关系数为 0,问题从定义上就不存在。
代价有三层。第一层是可解释性:主成分是原始变量的线性组合,系数回到原始空间后含义变得模糊,你没法再说“出生率每上升一个千分点人口变化多少”,因为出生率的信息被摊到了多个主成分上。第二层是信息损失:被丢掉的那 5.8% 方差里,恰好可能含有对预测有用的结构,降维是按方差大小砍的,不是按预测相关性砍的。第三层是结果本身——主成分回归算出的 2014 年预测值是 142180.53 万,误差 5398.5 万,比一元线性回归的 5383.8 万还略差一点。
这个结果看起来意外,其实很合理。主成分分析解决的是共线性带来的方差问题,它不解决模型形态带来的偏差问题。人口增长本身是非线性的,你用线性结构去拟合,就算把共线性处理得干干净净,偏差依然在那里。降维治的是“估计不稳”,治不了“模型不对”,这两类问题必须分开处理。把主成分当成万能药,是这套流程里另一个高频误解。
5. 常见问题排查:回归预测翻车的五个现场
5.1 数据与指标层面的三个坑
坑一:拿相关性当因果方向。现象是模型里出现反常识的符号,比如出生率系数为负、死亡率系数为负。原因是自变量和因变量共享同一条时间趋势,回归拟合的是统计对应,不是驱动机制。粮食产量高是因为人口多、劳动力足,人口多也可能因为农业产出支撑得起,方向是双向的,普通最小二乘区分不了。解决思路是先画时序图看趋势形态,再决定要不要加时间项或做差分,符号反常时优先怀疑趋势混淆,别急着解释系数。
坑二:指标选得多,信息就全。现象是四个变量一起进模型,R² 涨到 0.944,但两个变量不显著。原因是 GDP 这类变量和粮食产量高度相关,进来之后提供的新信息有限,反而把系数方差推高。解决思路是先算相关矩阵和 VIF,把高度相关的变量挑出来,要么剔一个,要么合成一个,别指望模型自己消化。变量数量不是越多越好,每多一个变量就多一份方差,样本量固定时这是硬约束。
坑三:样本区间跨了结构性变化还硬拟合。现象是同一个模型在前后两段数据上系数差别很大,预测老往一个方向偏。原因是这 35 年里人口的自然增长率经历了明显的阶段性变化,早期高增长、后期低增长,一条直线概括不了两段不同的机制。解决思路是分段建模或者引入能表达增速变化的模型形态,至少要在残差图上确认没有系统性结构残留。
5.2 建模与诊断层面的两个坑
坑四:用 R² 选模型。现象是三次曲线的 R² 达到 0.999,看起来完美,直接拿去预测。原因是 R² 随变量和参数增加单调不减,多项式次数越高越容易逼近样本点,这是过拟合的典型表现。看那张系数表就知道问题:三次项系数 −0.070,标准误 0.077,t = −0.908,p = 0.371,这一项根本没通过显著性检验,模型实际上退化成了二次形态,R² 接近 1 是因为人口和时间都是单调序列,模型只是在描点。解决思路是用调整后 R²、AIC 或者样本外误差来选模型,别只看 R²。
坑五:逐步回归之后照抄 p 值和置信区间。现象是最终模型的每个变量都“显著”,写报告时当成强证据。原因是变量是按 p 值筛出来的,筛完再用同一份数据算 p 值,选择偏差会让显著性被系统性高估,系数被放大、标准误被压低。解决思路是把逐步回归的结果当作变量排序参考,真要报告显著性,就在选定变量后用独立样本或者交叉验证重估一次,或者干脆用正则化方法替代逐步筛选。
6. 设计取舍:模型形态的选择比调参更致命
三次曲线为什么赢了,这个问题值得单独想清楚。它的方程是 ŷ = 96103.588 + 1849.094t − 17.683t² − 0.070t³,R = 1.000,残差标准误只有 365.498,2014 年预测值 136174.95 万,误差 607.05 万。但三次项 p = 0.371,不显著,意味着这个模型实际在做二次的事。它赢不是因为三次项有用,而是因为它允许增速随 t 变化——二次项本身就是减速结构,这恰好对上了人口增长后期放缓的事实。一元线性模型做不到这一点,它的斜率是常数,只能一路向上冲,于是预测系统性偏高。
Logistic 模型的失败是另一类。拟合出来的上限 a = 1.217e5,也就是 12.17 亿,可样本后期的人口早就超过了这个数,模型的上限被数据顶穿了。更关键的是参数不可识别:b 的 95% 置信区间是 −4.741e9 到 4.742e9,k 的区间是 −4.032e4 到 4.035e4,两个区间都跨过 0,说明这组参数根本没被数据确定下来。机制上的原因是数据还没进入饱和段——Logistic 曲线要靠拐点之后的数据才能定出上限和斜率,样本全在上升期,参数之间可以互相替代着拟合出差不多的曲线,解不唯一。这不是算法的问题,是数据信息量不够。
验证一个回归模型,我一般按三步走。第一步留出最后 5 年做样本外回测,用前面的数据拟合、后面的数据打分,算 MAPE 而不是看 R²。第二步做滚动起点预测,每次往前推一年重新拟合,看误差是否稳定,误差随时间放大的模型不能用于外推。第三步看残差图,残差呈现趋势或喇叭形,说明模型形态不对或者方差不是常数,这两类问题靠调参解决不了。
从那以后我每次做预测类回归,都强制走一遍“样本外回测 + 残差诊断”,哪怕 R² 是 0.99 也不例外。拟合优度只说明模型记住了过去,样本外误差才说明它理解了结构。这份材料把一元、多元、逐步、主成分、曲线、Logistic 六条路都走了一遍,每条路的失败点都留了痕迹,比只看一个成功模型有用得多。希望帮到你。
本文还有配套的精品资源,点击获取