跳到内容

第 12 卷 / 第 6 章 / 第 2 课

6.2 残差、异方差与影响点:诊断针对哪一种失效 ​

拟合成功只说明软件返回了结果。残差弧线可能提示遗漏非线性,漏斗形可能提示方差变化,高杠杆点也可能左右拟合线。这些都是结合数据生成过程调查的线索,不能翻译成“把碍眼的行删掉”。

本课目标 ​

  • 连接线性、独立、方差和分布假设与具体结论;
  • 解释 residual-vs-fitted、Q-Q、leverage 与 Cook's distance;
  • 使用 robust/clustered standard errors 的适当边界;
  • 理解共线性影响系数稳定而非必然损害预测。

1. 先区分预测条件与推断条件 ​

常见假设:

  1. 条件均值形式正确,E[ε∣X]=0;
  2. 观测或误差依赖结构被正确处理;
  3. 条件方差是否恒定;
  4. 误差分布与有限样本推断方法相容;
  5. 设计矩阵无完全共线;
  6. 模型训练与应用分布相符。

异方差不必然让 OLS 系数有偏,但普通标准误可能错误,OLS 也不再保证是最有效的线性无偏估计。遗漏混杂变量导致的 E[ε∣X]≠0 则可能直接让系数偏。

2. Residual vs fitted 检查均值和方差结构 ​

以下代码使用 statsmodels 0.15、pandas、NumPy、seaborn 和 Matplotlib,按顺序运行。合成数据刻意含有队伍共享效应、变化的噪声尺度和两个缺失字段。先明确筛选拟合样本,后续诊断才能指向同一批行。

python
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt
import seaborn as sns

rng = np.random.default_rng(12)
training = pd.DataFrame({
    "team_id": np.repeat(np.arange(20), 4),
    "team_size": rng.integers(2, 9, 80),
    "resources_used": rng.uniform(10, 50, 80),
    "mission_type": pd.Categorical(rng.choice(["survey", "repair"], 80)),
})
team_effect = rng.normal(0, 3, 20)
training["duration_minutes"] = (
    30 - 2 * training["team_size"] + .4 * training["resources_used"]
    + team_effect[training["team_id"]]
    + rng.normal(0, 1 + .04 * training["resources_used"], 80)
)
training.loc[2, "resources_used"] = np.nan
training.loc[5, "mission_type"] = np.nan
required = ["duration_minutes", "team_size", "resources_used", "mission_type", "team_id"]
included = training[required].notna().all(axis=1)
fit_rows = training.loc[included].copy()
model = smf.ols(
    "duration_minutes ~ team_size + resources_used + C(mission_type)",
    data=fit_rows, missing="raise",
).fit()
fitted, residuals = model.fittedvalues, model.resid
fig, ax = plt.subplots()
sns.scatterplot(x=fitted, y=residuals, alpha=0.5, ax=ax)
ax.axhline(0, color="black", linestyle="--")
ax.set_xlabel("Fitted duration (minutes)")
ax.set_ylabel("Residual (minutes)")
plt.show()

观察:

  • 弧形:漏掉非线性或交互;
  • 漏斗:方差随均值变化;
  • 分组条带:类别、测量离散化或遗漏群体;
  • 大片结构:时间/空间依赖或模型错设。

平滑线有助看系统模式,但 residual 和 fitted 共用估计参数,图形解释仍需结合数据生成过程。

3. Q-Q 图不是“模型是否有效”的总开关 ​

Q-Q 图比较标准化残差分位数与参考分布。尾部偏离可能来自重尾、异常点、混合群体或方差错设。

python
import statsmodels.api as sm
influence = model.get_influence()
sm.qqplot(influence.resid_studentized_internal, line="45")
plt.show()

误差正态性主要用于某些小样本精确 t/F 推断;OLS 点估计并不要求 Y 或 residual 完全正态。大样本近似也依赖独立性、矩条件和设计,不能只凭样本量大就忽略聚类。

若目标是预测,验证集误差和区间覆盖比“残差看起来正态”更直接。

4. 异方差的处理取决于目标 ​

  • 只修推断:使用 heteroskedasticity-consistent covariance(HC 系列);
  • 已知方差结构:加权最小二乘;
  • 方差随均值变化:转换目标或使用合适 GLM;
  • 预测区间:显式建模条件方差或分位数。
python
robust_model = model.get_robustcov_results(cov_type="HC3")
print(robust_model.summary())

robust SE 不会修复非线性、遗漏变量、错误依赖结构或坏数据。它主要改变协方差估计,不改变原 OLS 拟合值。

HC3 按 (1−hii)−2 放大每个平方残差对协方差的贡献,其中 hii 是杠杆值,用来缓解基础异方差协方差估计的小样本问题。它不会让相关行变独立,杠杆接近 1 时修正本身也可能不稳定。

5. 聚类与时间相关 ​

同一队伍任务可能共享未观测因素,队内正相关会让独立性假设下的区间过窄。若处理分配或抽样发生在队伍层,应选择尊重该单位的推断方法,例如聚类协方差或适当的层级模型:

python
clustered = model.get_robustcov_results(
    cov_type="cluster", groups=fit_rows["team_id"].to_numpy(),
    use_correction=True, df_correction=True, use_t=True,
)
print(pd.DataFrame({
    "ordinary_se": model.bse,
    "hc3_se": robust_model.bse,
    "cluster_se": clustered.bse,
}))

聚类数很少时,常规渐近近似可能不可靠,需要小样本校正、randomization inference 或其他专门方法。

分组数组取自 fit_rows,不能直接拿未筛选的 training:长度和顺序都必须与实际设计矩阵一致。样本有 20 个组,不能仅凭这个数量认定覆盖可靠。代码明确选择小样本修正与 t 参考分布,statsmodels 协方差选项说明了具体行为。

时间序列误差还可能自相关;Newey–West/HAC 等方法有带宽与平稳性假设,不能把 cov_type 当一键修复。

6. Leverage、残差和 influence 不同 ​

  • leverage:X 空间中是否远离其他点;
  • residual:该点结果与拟合相差多少;
  • influence:删除该点会怎样改变拟合。

Cook's distance 综合残差与 leverage,是调查指标,不是自动删除规则。4/n 是经验筛查线,不是“超过即错误”的定理。

python
diagnostics = pd.DataFrame({
    "leverage": influence.hat_matrix_diag,
    "studentized_residual": influence.resid_studentized_internal,
    "cooks_d": influence.cooks_distance[0],
}, index=fit_rows.index)
print(diagnostics.sort_values("cooks_d", ascending=False).head())

满秩设计下,杠杆值是 H=X(XTX)−1XT 的对角元素,总和等于含截距在内的拟合系数数目。预测变量位置很远的行,如果结果正好贴近拟合面,影响度也可能很小。

高影响点的处理:

  1. 核对采集与解析;
  2. 判断是否属于目标总体;
  3. 比较保留/排除敏感性;
  4. 考虑稳健回归或更合适模型;
  5. 报告规则和结论变化。

删除真实极端案例会让模型只适用于普通区域,应明确新的适用范围。

7. 共线性扩大系数不确定性 ​

当列接近线性组合时,模型难以分离各自贡献。表现为系数对小数据变化敏感、标准误大、符号可能翻转。

VIF:

VIFj=11−Rj2,

其中 Rj2 来自用其他特征解释第 j 列。VIF > 5 或 >10 只是经验阈值,不能替代目的判断。

共线特征仍可能共同提供好预测;问题主要影响单个系数解释和外推稳定性。删除特征、合成指标、重新设计采样或正则化各有不同语义后果。

8. 缺失模式与选择也要诊断 ​

回归库常按 complete cases 静默删行。先比较进入模型与被删除样本:

纳入规则应覆盖公式中用到的所有变量,包括类别预测变量和变换的定义域限制;额外要求 cluster ID 非空,也会改变分析集合。显式筛选后使用 missing="raise",可防止拟合阶段再次静默删行。

python
print({"input_rows": len(training), "fit_rows": len(fit_rows), "nobs": model.nobs})
print(training.groupby(included)["mission_type"].value_counts(dropna=False, normalize=True))
assert fit_rows.index.equals(model.model.data.row_labels)

若删除与结果或群体相关,complete-case 模型目标总体已经改变。需回到缺失机制和 estimand,而不是只看最终 nobs。

9. 诊断后要做敏感性分析 ​

比较:

  • 线性与样条/非线性规格;
  • 普通、robust 与 clustered SE;
  • 含/不含高影响点;
  • 不同缺失处理;
  • 预设协变量组合;
  • 时间或群体切片。

若结论方向随合理选择剧烈变化,报告这种脆弱性,不应只挑最漂亮的规格。

常见误区 ​

  • 残差不正态就不能用 OLS:要看目标和推断方法。
  • robust SE 修复模型错设:它不修均值结构和混杂。
  • Cook's D 超阈值必须删除:它只提示影响值得调查。
  • 共线性意味着预测一定差:它更直接损害系数可分辨性。

练习 ​

  1. 分别构造非线性、异方差和高杠杆数据,比较残差图。
  2. 对同一模型比较普通、HC3 与 cluster SE。
  3. 删除高 Cook's D 点前后比较系数,并写调查结论。
  4. 检查 complete-case 删除是否改变群体构成。

小结 ​

诊断不是给模型打一个“通过/失败”标签,而是定位哪项结论受威胁。均值错设、异方差、相关误差、影响点和共线性影响不同,应采取对应修复并报告敏感性。

下一课把预测流程与正则化放进交叉验证,确保缩放、缺失处理和超参数选择都没有偷看最终测试数据。

Built with VitePress | Software Systems Atlas