第 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 折交叉验证:
- 把样本随机分成 \(K\) 份;
- 每次取其中 1 份作为验证集,其余 \(K-1\) 份作为训练集;
- 在训练集拟合模型,在验证集计算预测误差;
- 对 \(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 交叉验证中的常见错误
使用交叉验证时,容易犯以下错误:
- 数据泄漏。在划分训练集前使用全样本进行标准化、变量筛选或缺失值填补,会把验证集信息泄漏到训练过程。
- 时间顺序忽略。时间序列预测不能随意打乱样本,应使用滚动预测或按时间划分训练测试。
- 只看一个指标。不同指标对应不同目标,报告时应与研究问题一致。
- 忽略不确定性。一次随机划分可能偶然有利于某个模型,可以重复交叉验证或报告误差差异。
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如果不同准则给出不同选择,不应机械服从某一个数字,而要回到研究目标:我们是更关心解释简洁性、预测误差,还是变量的经济含义?