回归分析人口预测:R²、共线性与外推风险拆解

发布时间:2026/11/2 15:54:16
回归分析人口预测:R²、共线性与外推风险拆解 简介围绕回归分析的人口数量预测这份课程实验论文面向统计学、数据分析相关专业学生及需要掌握预测建模流程的读者系统演示从指标选择、样本确定到模型建立与诊断的完整思路。文档内容涵盖一元线性回归中总人口与粮食产量的散点图、理论模型设定与回归诊断多元线性回归中的数据标准化、逐步回归、多重共线性检测与主成分分析并延伸至曲线回归和Logistic模型等非线性预测方法可作为课程实验报告撰写与回归建模复习的参考范例。资源包共1个doc文件约442KB目录与章节结构清晰包含摘要、前言、各章分析及结论、参考文献便于按实验步骤对照阅读。目前已有396人学习适合需要理解人口预测建模流程、补齐回归分析知识点的读者参考。1. 三次曲线赢在哪一份回归分析人口预测实验里的反常识结论35 个年度样本六个候选模型。用回归分析做人口数量预测时最直觉的排序是“拟合优度越高越准”可把 2014 年的结果摆在一起排序就变了一元线性回归 R²0.826误差 5384 万人四元回归 R² 爬到 0.972误差压到 1400 万人三次曲线 R²0.999误差 607 万人而 Logistic 模型干脆把人口上限估到了样本后段实际值之下模型自相矛盾。更有意思的是参数最多的模型赢在了“回代”上而不是真正的预测上。这篇拆解要讲的就是这条从 0.826 到 0.999 的曲线上每一步到底发生了什么以及为什么“误差最小”这个结论需要打个折扣。2. 一元线性回归R²0.826 的单变量模型错在哪一步2.1 选指标就是选因果假设粮食产量真的能预测人口吗实验第一步是选指标用 x1 表示粮食产量、x2 表示 GDP、x3 表示出生率、x4 表示死亡率y 表示年末总人口样本取 1980—2014 年共 35 个年度观测。一元模型挑了粮食产量作为唯一自变量理由是散点图上“粮食产量增加、总人口增加点大致落在一条直线附近”。这个理由在描述层面成立但在机制层面站不住。粮食产量和总人口都是典型的时间序列二者共享同一个向上趋势。两个各自单调上升的序列放在一起相关系数天然会很高这叫趋势共变也叫伪回归的温床。真正的检验很直接对两个序列各做一阶差分把“增量”拿去回归如果系数仍然显著说明关系不是趋势带来的如果一差分就散了那前面的 0.909 只是两条上升曲线的巧合。更根本的问题在因果方向。人口是粮食需求的来源之一粮食产量又受耕地、技术、投入影响二者互为因果用一个去预测另一个回归系数就不再是“效应”只是一个混合了双向反馈的统计关联。人口学里真正驱动总量变化的是出生率、死亡率与年龄结构这些是“率”而非“量”它们和总人口的关系带滞后、带非线性塞进一元线性模型里会被严重扭曲。这里有个容易被忽略的细节出生率、死亡率在样本区间内是下降的而总人口是上升的二者呈负相关。所以当模型把出生率放进多元回归时会得到一个负系数——出生率越低人口越多——这在统计上成立在机制上却是“时间趋势的镜像”不能读成因果。理解这一点后面多元模型里那个 -1460.798 的系数才不会读错。2.2 系数表逐行读截距、斜率、t 值与自由度一元模型的输出很干净R0.909R²0.826调整后 R²0.821残差标准误 4985.997。系数表里截距 53054.404标准误 5500.013t9.646p0.001粮食产量斜率 1.468标准误 0.117标准化 Beta 0.909t12.513p0.001。代入理论模型 yβ0β1xε得到ŷ 53054.404 1.468x斜率 1.468 的读法是粮食产量每多 1 万吨总人口平均多 1.468 万人。这个解释必须在“样本区间内、其他条件不变”的限定下才成立一旦离开样本它什么都不是。更值得看的是 t 值的算法。t 系数 / 标准误自由度是 n-233。R²0.826 意味着总平方和里有 82.6% 被回归平方和解释剩下的 17.4% 落在残差里——换算成标准差就是 4986 万人。这个数字比很多省级行政区的人口还多也就是说模型的“平均误差”本身就已经和一个大型城市群同量级了。显著性检验回答的是“这个斜率是否可能为 0”不是“这个模型预测得准不准”两件事经常被混为一谈。下面这段代码是复现这张系数表的最小实现重点在三个容易被跳过的地方加截距列、看调整后 R²、看残差自相关。import numpy as np import statsmodels.api as sm from statsmodels.stats.stattools import durbin_watson # x: 粮食产量万吨y: 年末总人口万人均按年份升序 X sm.add_constant(np.asarray(x, dtypefloat)) # 不加截距列OLS 会被强制过原点系数全错 y np.asarray(y, dtypefloat) fit sm.OLS(y, X).fit() print(fit.params) # [截距, 斜率]对应 53054.404 与 1.468 print(fit.bse) # 标准误t 值 params / bse print(fit.rsquared_adj) # 比较不同参数个数的模型时看这个不看 R² print(durbin_watson(fit.resid)) # 接近 2 说明残差无一阶自相关明显小于 1.5 要警惕add_constant那一行的作用是把常数项显式塞进设计矩阵statsmodels不会自动补。durbin_watson返回的是残差差分平方和与残差平方和之比时间序列样本上它通常小于 1.5说明相邻年份的残差同向漂移——这意味着标准误被低估t 值被人为放大。这份实验没有报告这一项是一个盲点。2.3 “预测 2014”其实是在回代样本内误差不是泛化误差把 x60702.6 代入方程得到 142165.84 万人而 2014 年实际值是 136782 万人高估 5384 万人。实验报告把这个差值当成模型的预测误差但这里有个方法论上的关键细节2014 年的数据本身就在建模样本里粮食产量 60702.6 正是样本中 x 的最大值总人口 136782 也正是拟合时用到的 y 之一。所以这不是预测是回代。回代误差反映的是模型对已见数据的贴合程度而不是对新数据的泛化能力。参数越多、阶数越高的模型在回代上天然占便宜因为它们有更多自由度去穿过样本点。这就解释了一个看似矛盾的现象一元模型 R² 只有 0.826误差却比四元模型的 1400 万人还要大一个量级——不是一元模型“更差”而是它没有足够的自由度去迁就那个点。正确的做法是时间切分留最后 3 到 5 年完全不参与拟合拿它们做样本外验证再用滚动原点的方式逐年推进。如果想要更严格一点可以只留最后一年把它前面所有年份作为训练集逐年重复。这样得到的误差才是“预测误差”才有资格拿来比较模型。3. 多元线性回归R² 从 0.826 爬到 0.972为什么误差没跟着降3.1 中心化与标准化解决的是数值问题不是共线性问题多元模型的第一步是数据中心化和标准化。中心化是把每个变量减去自己的均值标准化是再除以标准差。实验里给出的理由是“取消量纲不同、数值相差较大所引起的误差”这个说法只对了一半。量纲问题确实存在粮食产量以万吨计量级在 3 万到 6 万GDP 以亿元计量级从 4551 到 63 万出生率和死亡率是千分位的小数量级 6 到 23。这四个变量放进同一个设计矩阵矩阵的条件数会被拉到很大正规方程 XᵀX 求逆时小数舍入误差被放大数值解不稳。但标准化不改变 OLS 的拟合值、R²、t 值和 F 值。它只改变截距和系数的尺度中心化后截距从 124574.667 变成“均值水平上的预测值”标准化后系数变成 Beta 值可以横向比较哪个变量“更重要”。也就是说中心化和标准化是数值稳定性和可解释性的工具它完全不能缓解共线性。共线性是变量之间的线性依赖你给每个变量乘一个常数依赖关系原样保留。把这两件事混在一起是初学者最常见的误解之一。一个实际的好处是条件数。把设计矩阵按列标准化后再算 SVD条件指数才有跨数据集的可比性SPSS 的共线性诊断表默认就是这么算的。所以标准化在“诊断”环节是必要的在“建模”环节是可选的。3.2 逐步回归的“有进有出”F 门槛、进出顺序与选择后推断全模型的结果先摆出来R0.972R²0.944调整后 R²0.936F126.295p0.001整体显著。但系数表一列出来问题就露了GDP 的系数是 -0.005p0.531死亡率的系数是 -4060.272p0.275两个都没通过显著性检验。全模型方程是ŷ 124574.667 1.045x1 - 0.005x2 - 1460.798x3 - 4060.272x4逐步回归的机制是“有进有出”每次挑一个对残差平方和贡献最大的变量进入模型进入之后立刻对已在模型内的所有变量重新做检验谁的 p 值超过剔除门槛就踢出去直到没有变量能进、也没有变量该出为止。SPSS 的默认门槛是进入 p0.05、剔除 p0.10这两个值不对称是有意为之——门槛相同会导致变量在边界上反复进出算法震荡。跑完的结果是三步第一步只进出生率截距 165146.388、系数 -2687.949R² 已经很高第二步进粮食产量出生率系数变成 -1534.270、粮食产量 0.816第三步进死亡率出生率 -1366.202、粮食产量 1.012、死亡率 -5681.557GDP 全程没进来。最终方程ŷ 134678.878 1.012x1 - 1366.202x3 - 5681.557x42014 年回代值 138506.22 万人误差 1724 万人——注意这个数字比全模型的 1400 万人还大。实验报告据此说“不用逐步回归的预测值更接近真实值”这句话是对的但原因不是逐步回归本身不好而是它把 GDP 剔掉之后剩下的三个变量组合出的方向恰好偏离了 2014 年那个点。在共线性严重的样本上剔变量等于换约束拟合值在样本外的落点会整体平移。更深一层的问题是选择后推断。逐步回归跑完之后那张系数表上的 p 值不是最终模型的 p 值而是在“已经做过几十次比较”这个条件下的 p 值。变量选择过程本身消耗了自由度标准误被系统性低估显著性被系统性高估。业界的做法是逐步回归只用来筛变量筛完之后在选定变量上重新拟合一次报告那一次的推断或者干脆用 LASSO 这类带惩罚的方法把选择和估计放进同一个目标函数里。3.3 多重共线性三套探针VIF、特征值、条件指数共线性诊断这一步实验用了三套指标思路很完整。第一套是方差膨胀因子。VIF_j 1/(1-R²_j)其中 R²_j 是把第 j 个变量对其余所有变量做回归得到的决定系数。容忍度是它的倒数。VIF1 表示完全正交VIF10 对应 R²_j0.9是常用的经验阈值。实验表 2-6 里剔除 GDP 之后出生率 VIF2.868、粮食产量 VIF4.146、死亡率 VIF1.808全部低于 10判定共线性已缓解。第二套是特征值。对标准化后的相关矩阵做特征分解特征值接近 0 说明矩阵接近奇异存在近似线性依赖。实验里“有 3 个特征值接近 0”这个信号比 VIF 更敏感因为它看的是整个矩阵而不是单个变量。第三套是条件指数等于最大特征值除以第 i 个特征值的平方根等价于标准化设计矩阵的奇异值之比。经验阈值是大于 10 提示中等共线、大于 30 提示严重共线。实验里有三个维度超过 10其中最大到 33.798。再配合方差比例表——看每个维度上各变量系数方差被分摊的比例比例接近 1 的那几个变量就是共线的主角。import numpy as np def vif_table(X): X 不含截距列对每个变量用其余变量回归VIF 1 / (1 - R²_j) out {} for j in range(X.shape[1]): yj X[:, j] Xj np.delete(X, j, axis1) Xj np.column_stack([np.ones(len(Xj)), Xj]) # 辅助回归也要截距 beta, *_ np.linalg.lstsq(Xj, yj, rcondNone) resid yj - Xj beta sst (yj - yj.mean()) (yj - yj.mean()) r2 1.0 - resid resid / sst out[j] np.inf if r2 1.0 else 1.0 / (1.0 - r2) return out # 条件指数标准化后做 SVD最大奇异值 / 第 i 个奇异值 Z (X - X.mean(0)) / X.std(0, ddof1) s np.linalg.svd(Z, compute_uvFalse) print(s.max() / s) # 10 中等共线30 严重共线vif_table里的辅助回归必须带截距否则 R²_j 的定义会变VIF 也跟着失真。np.linalg.lstsq比手写正规方程稳因为它走的是 SVD 分解而不是显式求逆条件数大的时候差别很明显。条件指数那段先标准化再做 SVD是为了让不同量纲的变量在同一把尺子上比较。需要强调的是共线性不降低预测精度。只要预测点的变量组合落在训练数据的主方向上拟合值依然稳定膨胀的只是单个系数的方差。共线性真正毁掉的是系数解释——你没法说“GDP 每增加一亿元人口减少 0.005 万人”因为这个 -0.005 的置信区间横跨 0。所以共线性要不要处理取决于你是要预测还是要解释。3.4 主成分回归把 94% 的信息压进两个方向代价是什么主成分分析的思路是把 p 个相关变量正交变换成 p 个互不相关的新变量按方差从大到小排列。实验里前两个主成分的方差贡献率分别是 76.004% 和 18.176%累计 94.18%于是取前两个做主成分回归方程是ŷ 53075.711 1.468x1 2.494×10⁻⁵x22014 年回代值 142180.53 万人误差 5398 万人几乎和一元模型一模一样。这个结果很说明问题第二个主成分几乎没带来增量信息。看系数表更清楚GDP 的系数 2.494×10⁻⁵p0.998等于零。机制上的原因在于 PCA 的排序标准是“方差大”不是“与 y 相关”。粮食产量和 GDP 量级大、波动大自然占据第一主成分出生率和死亡率方差小被压到后面的主成分里正好被 94.18% 这个门槛切掉了。可人口预测里最该保留的信息恰恰是出生率和死亡率。降维降掉的是方差小的方向丢掉的可能是解释力最强的方向。这就是主成分回归的固有代价它保证保留的信息量最大但不保证保留的信息有用。而且主成分是原变量的线性组合含义模糊解释成本高。实验报告自己也在结论里承认了这一点——“主成分的解释含义一般带点模糊性不像原始变量那么清楚”并把它列为误差变大的原因之一。这个自我批评是准确的。4. 非线性模型三次曲线赢在哪里Logistic 输在哪里4.1 从散点形状到阶数三次曲线拟合出了什么换成时间做自变量t1 对应 1980 年t35 对应 2014 年。散点图的形状是单调上升且增速逐渐放缓实验据此选了三次多项式y b0 b1t b2t² b3t³拟合结果 R1.000、R²0.999、调整后 R²0.999、残差标准误 365.498F11749.732p0.001。系数是截距 96103.588、一次项 1849.094、二次项 -17.683、三次项 -0.070。回代 2014 年得到 136174.95 万人误差 607 万人是六个模型里最小的。但看系数表最后一行三次项的 t-0.908p0.371没有通过显著性检验。也就是说这条曲线的形状主要由一次项和二次项决定三次项在样本区间内贡献很小。实验报告里写“每个不同次幂的 t 都经过了显著性检验”这一句和表里的数字对不上是这份报告里少数几处需要打问号的地方。真正值得警惕的是外推行为。三次项系数虽然只有 -0.070但 t³ 增长极快。用四舍五入后的系数往外推量级大致是这样年份序号 t对应年份预测总人口万人说明352014约 136175样本末端回代值422021 前后约 137390曲线在此附近取到极大值502029 前后约 135600已开始回落602039 前后约 128270下降加速把导数 b1 2b2t 3b3t² 令为零解出 t≈42也就是 2021 年前后到顶。实验结论里说“到 2025 年左右达到峰值”本质上是这条三次曲线自己造出来的拐点而不是数据里长出来的拐点。换句话说那个“人口将达峰后缓慢下降”的结论完全由那个 p0.371、统计上不显著的三次项决定。这就是高阶多项式外推的典型风险远处由最高阶项主导而最高阶项恰恰是最不可信的那一项。4.2 Logistic 的参数为什么跑飞三个参数、两个自由度Logistic 模型的公式是 P(t) Pm / (1 b·e^{-k(t-t0)})Pm 是人口极限规模b、k 是待定参数。它的机制假设很明确增长率随人口规模上升而下降总量趋于饱和走出一条 S 形曲线。实验用 MATLAB 的 fittype 拟合起始点随便给了 [2000, 127627, 1]结果是 a1.217×10⁵、b1.176×10⁵、k13.21。置信区间是这场拟合里最刺眼的部分a 的区间 (1.144×10⁵, 1.29×10⁵) 还算正常b 的区间是 (-4.741×10⁹, 4.742×10⁹)k 的区间是 (-4.032×10⁴, 4.035×10⁴)横跨正负四个数量级。原因不是数据不好是参数不可辨识。k13.21 意味着指数项 e^{-k(t-1)} 在 t2 时就已经衰减到 e^{-13.2}≈1.8×10⁻⁶之后一路下溢。整条曲线在样本区间内几乎是水平线 P(t)≈a。这时候 b 和 k 只以乘积 b·e^{-k(t-1)} 的形式出现而这个乘积对所有 t≥1 都约等于零。任何“b 增大、k 增大”的组合都给出几乎相同的残差参数沿着一条山脊线完全无法区分。拟合算法在平坦方向上找不到唯一极小值于是一路把 k 推到上界。后果是连锁的。拟合出的上限 a121700 万人而样本后段的实际值已经越过这条线——一个“人口上限”被实际数据突破模型自己否定自己。实验报告把原因归结为“数据过少、抽样误差”方向对但不够深核心是模型结构在这个样本区间上没有识别力。修法有三条。一是给 k 加上界比如限制在 0.2 以下强制曲线在样本区间内保持可见的弯曲二是把 Pm 固定成一组候选值做一维网格搜索只拟合 b 和 k把三维问题降成一维三是换因变量别拟合累计人口改拟合年增长率 r(t)ΔP/PLogistic 的增长率是线性递减的这个信号在小样本上更容易识别。import numpy as np from scipy.optimize import curve_fit # t: 年份序号1980 记为 1p: 年末总人口万人 def logistic(t, a, b, k): return a / (1.0 b * np.exp(-k * (t - 1))) # 关键在 bounds不给上界k 会跑到让指数项完全下溢的位置b 和 k 一起发散 popt, pcov curve_fit( logistic, t, p, p0[130000, 1.0, 0.03], # a 用样本末值起步b、k 给保守初值 bounds([100000, 1e-3, 1e-3], # 下界 [200000, 1e3, 0.20]), # 上界k0.2 时曲线在样本区间内已接近水平 maxfev20000, ) perr np.sqrt(np.diag(pcov)) # 标准误远大于参数本身就是不可辨识的信号 print(popt, perr)bounds里那个 0.20 不是随便取的样本跨度 35 年k0.2 时特征宽度约 5 年再大就看不到 S 形了。p0的选择也影响收敛a 从样本末值起步比从 2000 起步稳得多——实验里那个 [2000, 127627, 1] 的起始点第一维和真实量级差了六十倍。MATLAB 的fittype配合fitOptions的Lower/Upper参数作用是同一回事。4.3 时间做自变量这是趋势模型不是人口模型三次曲线和 Logistic 都把时间 t 当自变量这个选择的含义要说清楚。t 不是任何可干预的变量它是一个代理把出生率、死亡率、年龄结构、经济水平、城镇化等所有随时间变化的因素全部吸收进去。好处是省事35 年的人口曲线足够平滑任何三阶多项式都能贴得很近。坏处是模型没有结构你没法回答“如果生育水平下降 10%2050 年人口是多少”因为模型里根本没有生育这个变量。判据很简单——把 t 换成 t²R² 照样会很高把 t 换成“年份序号的平方根”R² 可能还是很高。这说明模型学到的是“曲线形状”不是“人口过程”。真正的人口结构模型是队列要素法分年龄组的生育率、存活率、迁移率逐年推演从当前的年龄结构出发一年一年往前滚。它需要的是分年龄的分组数据而不是一个总量序列。这份实验手上的数据只有总量所以只能做趋势外推这一点没有更好的选择但必须清楚结论的边界——它给出的是“如果过去三十五年的模式延续下去”不是“人口会怎样”。5. 排查清单这五类问题在回归预测里反复出现5.1 现象所有模型的误差都在几百到几千精度看起来惊人原因数据集的时间范围到 2014 年而“预测 2014”用的就是这批数据。2014 年的粮食产量 60702.6 是样本内 x 的最大值2014 年的总人口 136782 就在拟合用的 y 里。所有误差都是回代误差参数越多的模型天然占便宜三次曲线三个参数加截距拟合 35 个点当然比一元模型贴得紧。解决把最后 3 到 5 年切出来完全不参与拟合做时间序列留出验证进阶一点用滚动原点每次向前推一年重训重测把每一年的一步预测误差收集起来算 RMSE。报告样本外指标不报告回代指标。5.2 现象逐步回归剔掉 GDP 之后剩下变量的系数全变了原因全模型里出生率系数 -1460.798剔除 GDP 后变成 -1366.202粮食产量从 1.045 变成 1.012。共线性下系数解本来就不唯一剔除一个变量等于给方程组换了一组约束解自然平移。再加上逐步回归每一步的 p 值都是条件 p 值不是最终模型的 p 值显著性被高估。解决先看 VIF 和条件指数判断共线性到底严重到什么程度再决定要不要剔变量报告全模型和精简模型两套结果系数只解释方向和量级别当边际效应读有条件的话上岭回归或 LASSO把收缩和选择放进同一个目标函数。5.3 现象VIF 全部小于 10预测区间还是宽得没法用原因VIF 只看设计矩阵 X 内部的线性依赖和预测点在哪完全无关。预测区间的宽度由两部分决定——残差方差以及预测点的杠杆值 (x₀-x̄)ᵀ(XᵀX)⁻¹(x₀-x̄)。样本末端的点杠杆最大因为那里的 x 离均值最远。这份数据里 2014 年的粮食产量是样本最大值杠杆值天然最高。解决给每个待预测点算杠杆值或马氏距离杠杆超过 2p/n 的点单独标注报告预测区间而不只是点预测如果只要一个数至少把区间宽度写进结论里。5.4 现象Logistic 拟合给出 ±10⁹ 的置信区间参数值却很“整齐”原因参数弱可辨识。k13.21 让指数项在 t2 时就下溢到 10⁻⁶ 量级整条曲线退化成水平线b 和 k 只以乘积形式出现沿山脊线完全不可分。拟合算法在平坦方向上停不住一路把 k 推到边界。解决给 k 加物理上界0.2 以下固定 a 做一维网格搜索或者改拟合年增长率而不是累计人口拿到结果先看标准误标准误远大于参数本身就直接判定不可辨识。5.5 现象三次曲线外推十几年后人口开始下降且越降越快原因三次项系数 -0.070 在 t35 时只贡献 -3001到 t60 时贡献 -15120已经超过一次项贡献的一半。最高阶项在远处主导而它在样本内的 t 检验 p0.371本来就没通过。解决外推之前对最高阶项单独做显著性检验不显著就降阶做系数敏感性分析把 -0.070 换成 -0.05 和 -0.09 各跑一遍看拐点位置移动多少外推步长控制在样本跨度的 20% 以内再远就换模型。6. 选复杂度还是选可解释性把误差表读成一张取舍表本文还有配套的精品资源点击获取