技巧 | 交叉验证

交叉验证(Cross Validation,CV)是一种常见的模型评估方法。简言之,就是将样本分为训练集(tranning set)和测试集(test set),训练集用来估计模型参数,测试集用来评价模型精度。交叉验证有多种类型,本篇主要介绍以下三种:

  • 留一法交叉验证

  • k折交叉验证

  • 重复k折交叉验证

1 留一法交叉验证

假设样本数为,留一法交叉验证(Least-One-Out Cross-Validation,LOOCV),就是每次只留下一个样本作为测试集,剩余个样本作为训练集,重复次。

步骤如下:

Step 1:从个样本中拿出个样本来估计模型参数,这些样本称为训练集,剩余一个样本为测试集;
Step 2:使用上个步骤估计出的模型参数预测测试集样本的因变量取值;
Step 3:重复上述两个步骤,直至所有样本都有且仅有一次为测试集样本;
Step 4:记测试集的因变量预测值构成的向量为pred,对应的因变量真实值为obs
Step 5:使用predobs两个向量计算R2、均方根误差(RMSE)等模型评价指标。

以线性模型为例。

以下模型的R2 = 0.8264:

model <- lm(mpg ~ wt + qsec, data = mtcars)
## R2 = 0.8264

使用留一法交叉验证计算模型的R2。

  • 生成向量pred

n = dim(mtcars)[1]
pred = 1:n

for(i in c(1:n)) {
  
  mod <- lm(mpg ~ wt + qsec, data = mtcars[-i,])
  pred[i] = predict(mod, mtcars[i,])
}
  • 计算R2

前面的推文已经介绍了R2(线性回归(一)——模型表达式和输出结果

R2_CV <- function(pred, obs) {
  
  err <- sum((pred - obs)^2)
  ctr <- sum((mean(obs) - obs)^2)
  R2_CV = 1 - err/ctr
  return(R2_CV)
}

R2_CV(pred, mtcars$mpg)
## [1] 0.7785627

得到的R2 = 0.7785627。下面使用caret工具包中的R2()函数进行验证。

R2()函数提供了两种计算R2的方法,一种是form = "corr"(默认值),即将R2视为预测值和真实值相关系数的平方,另一种是form = "traditional",即上面列出的计算公式。

library(caret)
R2(pred, mtcars$mpg)
## [1] 0.7800446
cor(pred, mtcars$mpg)^2
## [1] 0.7800446

R2(pred, mtcars$mpg, form = "traditional")
## [1] 0.7785627
  • 可以看出,使用form = "traditional"与自定义的R2_CV()函数计算的结果相同。

2 k折交叉验证

k折交叉验证(k-Folder Cross-Validation),是将个样本随机分为等份,将其中份作为训练集,剩余一份作为测试集,并不断重复,直至每份都有且仅有一次作为过测试集。当时,k折交叉验证等价于留一法交叉验证。最常用的是10折交叉验证。

自定义将样本随机分为k等份的函数group_fun()

group_fun <- function(data, seed, k = 10) {
  
  set.seed(seed)
  n = dim(data)[1]
  
  order_rand <- sample(1:n, n, replace = F)
  group_rand <- order_rand %% k + 1
  
  return(group_rand)
}
  • data:包含样本变量的数据框;

  • seed:随机数种子;

  • k:等分的份数,默认值为10。

检测自定义函数的效果:

group_fun(mtcars, 123)
##  [1]  2  6 10  5  4  1  9  3  2  6  1 10  4  1 10  9  9  8  8  3  7  8  5  2  5
## [26]  2  6  4  7  3  3  7

group_fun(mtcars, 1024, k = 5)
##  [1] 2 3 3 4 1 5 5 2 3 4 1 2 5 2 1 3 3 1 5 3 4 3 4 5 2 4 5 2 1 2 1 4
  • 返回的数字表示对应分组的编号。

10折交叉验证:

k = 10
x <- group_fun(mtcars, 1024, k = k)
pred <- NULL
obs <- NULL

for(i in 1:k) {
  
  mod <- lm(mpg ~ wt + qsec, data = mtcars[x != i,])
  pred = c(pred, predict(mod, mtcars[x == i,]))
  obs <- c(obs, mtcars[x == i,]$mpg)
}

R2_CV(pred, obs)
## [1] 0.7871856

留一法交叉验证得到的R2是唯一的,而k折交叉验证由于分组过程存在随机性,因此计算的R2是不固定的。

以下列举了上述代码不同随机数种子对应的计算结果:

seedR2
40.7837388
160.7888558
640.7782393
1280.778737
2560.7904926
10240.7871856
  • 可以看出,虽然不同随机数种子对应的计算结果不同,但总体还是比较稳定的。

3 重复k折交叉验证

重复k折交叉验证(Repeated k-Fold Cross-Validation )是对样本进行多次随机分组,每次分组都进行一次k折交叉验证,再将多次计算的结果取平均值,以消除分组的偶然性对计算结果可能造成的偏差。

set.seed(1024)
rp = 10
seeds = runif(rp, 0, 10000)
r2 <- 1:rp
  • rp:重复次数;

  • seeds:储存随机数种子的向量,长度为rp;

  • r2:储存每次R2的计算结果,长度为rp。

for(j in 1:rp) {
  
  k = 10
  x <- group_fun(mtcars, seeds[j], k = k)
  pred <- NULL
  obs <- NULL
  
  for(i in 1:k) {
    
    mod <- lm(mpg ~ wt + qsec, data = mtcars[x != i,])
    pred = c(pred, predict(mod, mtcars[x == i,]))
    obs <- c(obs, mtcars[x == i,]$mpg)
  }
  
  r2[j] = R2_CV(pred, obs)
}

mean(r2)
# [1] 0.7802496
seedR2
40.7806959
160.7791301
640.7643035
1280.7826845
2560.7762842
10240.7802496

4 总结

  • 留一法只适用于样本数较小的情况;

  • k折交叉验证可用于样本量较大的情况。

  • 0
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值