第 14 章 模型选择、交叉验证与预测评估

在实际建模中,我们往往不只有一个候选模型。收入可以用水平值,也可以取对数;消费函数可以是线性的,也可以加入二次项;违约预测可以使用少数核心变量,也可以使用更多行为变量。模型选择要回答:在多个合理模型之间,如何做出有依据的选择?

本章介绍两类常用计算方法:信息准则和交叉验证。前者从拟合优度与模型复杂度之间权衡,后者直接评估模型在未见数据上的预测表现。对于经济统计本科教学,重点不是记住某个准则的名字,而是理解:更复杂的模型通常训练误差更小,但未必预测更好。

14.1 训练误差、测试误差与过拟合

设训练数据为 \(\mathcal D_{\text{train}}\),我们用它估计模型参数。训练误差是在同一批数据上计算的误差,测试误差是在未参与拟合的新数据上计算的误差。若模型过于复杂,可能把训练数据中的偶然噪声也学进去,从而训练误差很低,测试误差却较高。这种现象称为过拟合。

预测建模中真正关心的通常是未来样本或新地区样本上的表现,因此仅报告训练拟合优度是不够的。

14.2 信息准则

信息准则常用于似然模型的比较。设模型的最大对数似然为 \(\ell(\hat\theta)\),参数个数为 \(k\),样本量为 \(n\)。AIC 定义为

\[ \operatorname{AIC} = -2\ell(\hat\theta)+2k. \]

BIC 定义为

\[ \operatorname{BIC} = -2\ell(\hat\theta)+k\log n. \]

其中第一项衡量拟合误差,第二项惩罚模型复杂度。AIC 和 BIC 越小,表示在相应准则下模型越优。BIC 的复杂度惩罚通常比 AIC 更强,尤其当样本量较大时。

下面用模拟租金数据比较三个线性模型。数据为教学模拟,不对应真实房价或租金来源。

set.seed(2026)
n <- 400
income <- rlnorm(n, log(8), 0.5)
area <- runif(n, 30, 120)
distance <- runif(n, 1, 25)
rent <- 5 + 0.08 * area - 0.10 * distance + 0.35 * log(income) +
  rnorm(n, sd = 1.2)

rent_dat <- data.frame(rent, income, area, distance)

m1 <- lm(rent ~ area + distance, data = rent_dat)
m2 <- lm(rent ~ area + distance + log(income), data = rent_dat)
m3 <- lm(rent ~ area + distance + log(income) + I(distance^2), data = rent_dat)

data.frame(
  model = c("m1", "m2", "m3"),
  AIC = c(AIC(m1), AIC(m2), AIC(m3)),
  BIC = c(BIC(m1), BIC(m2), BIC(m3))
)
#>   model  AIC  BIC
#> 1    m1 1300 1316
#> 2    m2 1294 1314
#> 3    m3 1294 1318

信息准则适合在同一响应变量、同一数据集和相近模型族之间比较。不能把不同数据、不同因变量或不同样本定义下的 AIC/BIC 直接放在一起解释。

14.3 K 折交叉验证

交叉验证通过反复划分训练集和验证集来估计测试误差。最常用的是 K 折交叉验证:

  1. 把样本随机分成 \(K\) 份;
  2. 每次取其中 1 份作为验证集,其余 \(K-1\) 份作为训练集;
  3. 在训练集拟合模型,在验证集计算预测误差;
  4. \(K\) 次验证误差取平均。

若损失函数为平方误差,则 K 折交叉验证误差为

\[ \operatorname{CV} = \frac{1}{n}\sum_{i=1}^n (y_i-\hat y_{-k(i),i})^2, \]

其中 \(\hat y_{-k(i),i}\) 表示第 \(i\) 个观测所在折未参与训练时得到的预测值。

cv_lm <- function(formula, data, K = 5) {
  n <- nrow(data)
  fold <- sample(rep(seq_len(K), length.out = n))
  loss <- numeric(K)

  for (k in seq_len(K)) {
    train <- data[fold != k, ]
    valid <- data[fold == k, ]
    fit <- lm(formula, data = train)
    pred <- predict(fit, newdata = valid)
    loss[k] <- mean((valid[[as.character(formula[[2]])]] - pred)^2)
  }

  mean(loss)
}

set.seed(1)
c(
  m1 = cv_lm(rent ~ area + distance, rent_dat, K = 5),
  m2 = cv_lm(rent ~ area + distance + log(income), rent_dat, K = 5),
  m3 = cv_lm(rent ~ area + distance + log(income) + I(distance^2), rent_dat, K = 5)
)
#>    m1    m2    m3 
#> 1.503 1.509 1.486

交叉验证带有随机性,因为折的划分是随机的。正式报告时可以设置随机种子,或者重复多次交叉验证并报告平均结果。

14.4 预测评估指标

不同任务需要不同评估指标。对连续变量预测,常见指标包括均方误差

\[ \operatorname{MSE} = \frac{1}{n}\sum_{i=1}^n(y_i-\hat y_i)^2, \]

和平均绝对误差

\[ \operatorname{MAE} = \frac{1}{n}\sum_{i=1}^n |y_i-\hat y_i|. \]

MSE 对大误差更敏感,MAE 更容易解释为平均绝对偏差。对二元分类问题,常见指标包括准确率、Brier分数、对数损失和 AUC。若预测概率为 \(\hat p_i\),Brier 分数为

\[ \operatorname{Brier} = \frac{1}{n}\sum_{i=1}^n(y_i-\hat p_i)^2. \]

对数损失为

\[ \operatorname{LogLoss} = -\frac{1}{n}\sum_{i=1}^n \{y_i\log \hat p_i+(1-y_i)\log(1-\hat p_i)\}. \]

下面给出二元违约预测的简单评估。

set.seed(2)
n <- 500
x1 <- rnorm(n)
x2 <- rnorm(n)
p <- plogis(-1 + 1.2 * x1 - 0.8 * x2)
y <- rbinom(n, 1, p)

fit <- glm(y ~ x1 + x2, family = binomial())
phat <- predict(fit, type = "response")
class_hat <- as.integer(phat > 0.5)

eps <- 1e-12
c(accuracy = mean(class_hat == y),
  brier = mean((y - phat)^2),
  logloss = -mean(y * log(pmax(phat, eps)) +
                    (1 - y) * log(pmax(1 - phat, eps))))
#> accuracy    brier  logloss 
#>   0.7460   0.1610   0.4846

分类准确率依赖阈值,且在类别极不平衡时可能误导。经济统计中的违约、失业、患病等事件常常是低概率事件,因此更应关注预测概率的校准和排序能力。

14.5 交叉验证中的常见错误

使用交叉验证时,容易犯以下错误:

  1. 数据泄漏。在划分训练集前使用全样本进行标准化、变量筛选或缺失值填补,会把验证集信息泄漏到训练过程。
  2. 时间顺序忽略。时间序列预测不能随意打乱样本,应使用滚动预测或按时间划分训练测试。
  3. 只看一个指标。不同指标对应不同目标,报告时应与研究问题一致。
  4. 忽略不确定性。一次随机划分可能偶然有利于某个模型,可以重复交叉验证或报告误差差异。

14.6 案例:预测模型比较

下面用模拟租金数据比较三个模型,并同时报告信息准则和交叉验证误差。

set.seed(3)
forms <- list(
  m1 = rent ~ area + distance,
  m2 = rent ~ area + distance + log(income),
  m3 = rent ~ area + distance + log(income) + I(distance^2)
)

result <- data.frame(model = names(forms), AIC = NA, BIC = NA, CV_MSE = NA)
for (j in seq_along(forms)) {
  fit <- lm(forms[[j]], data = rent_dat)
  result$AIC[j] <- AIC(fit)
  result$BIC[j] <- BIC(fit)
  result$CV_MSE[j] <- cv_lm(forms[[j]], rent_dat, K = 5)
}

result
#>   model  AIC  BIC CV_MSE
#> 1    m1 1300 1316  1.497
#> 2    m2 1294 1314  1.475
#> 3    m3 1294 1318  1.492

如果不同准则给出不同选择,不应机械服从某一个数字,而要回到研究目标:我们是更关心解释简洁性、预测误差,还是变量的经济含义?

14.7 本章小结

模型选择需要在拟合和复杂度之间权衡。AIC 和 BIC 使用最大对数似然和参数个数进行比较;交叉验证直接估计模型在未见数据上的预测误差;预测评估指标要与任务类型和研究目标匹配。经济统计中的模型选择不是纯粹技术问题,还必须结合变量解释、数据生成过程和政策或业务场景。

14.8 练习

  1. 在租金模拟数据中加入一个无关变量,比较 AIC、BIC 和交叉验证误差是否支持保留它。
  2. 把 K 折交叉验证中的 \(K\) 改为 2、5、10,观察 CV 误差的变化。
  3. 对二元分类例子,改变分类阈值,观察准确率如何变化。
  4. 解释为什么时间序列预测不能使用普通随机 K 折交叉验证。