交叉验证法(CV)和自助法(bootstrap)
下面通过一个示例来学习其原理:
set.seed(12)
#划分观测集
library(ISLR)
#从1:392中随机抽取196个数,代表得到196个训练观测
train <- sample(392,size=196)
#然后用lm()函数中的subset选项,用训练数据集拟合一个线性回归模型
lm.fit <- lm(mpg~horsepower,data=Auto,subset=train)
#用验证数据集来计算均方误差
lm.pred <- predict(lm.fit,Auto)
mean((Auto$mpg-lm.pred)[-train]^2)#删去训练数据集中的观测值
20.86694
#用poly()函数来估计用二次,三次多项式回归所产生的测试误差
lm.fit2 <- lm(mpg~poly(horsepower,2),data=Auto,subset=train)#随机抽取数据集中的192个数来做拟合
#验证均方误差
mean((Auto$mpg-predict(lm.fit2,Auto))[-train]^2)
[1] 15.64132
lm.fit3 <- lm(mpg~poly(horsepower,3),data=Auto,subset=train)
mean((Auto$mpg-predict(lm.fit3,Auto))[-train]^2)
[1] 15.76978
#如果选择了不同的训练集,那在验证集上就会有不同的测试误差
set.seed(123)
train <- sample(392,196)
lm.fit <- lm(mpg~horsepower,data=Auto,subset=train)
mean((Auto$mpg-predict(lm.fit))[-train]^2)
[1] 93.00332
lm.fit2 <- lm(mpg~poly(horsepower,2),data=Auto,subset=train)
mean((Auto$mpg-predict(lm.fit2))[-train]^2)
[1] 101.0372
lm.fit3 <- lm(mpg~poly(horsepower,3),data=Auto,subset=train)
mean((Auto$mpg-predict(lm.fit3))[-train]^2)
[1] 101.5761
由两次分割数据集的方法,以及对应的不同的拟合模型的验证错误率可知,用二次函数来拟合模型有较低的错误率。
验证集方法就是将一个观测集合随机划分为一个训练集和一个验证集,然后在训练集上拟合统计模型,在验证集上评价模型的表现。
上例,在训练集上拟合多个模型(线性、二次、三次),然后再验证集上计算验证集错误率,其中用均方误差作为衡量验证集误差的度量指标。
在不同的分割验证集的方法下,训练模型就会得到不同的误差。可以据最小验证错误率,来选择最合适的模型。
验证集方法的优点:
适用于没有足够大的测试数据集来估计测试错误率;
原理简单,易执行。缺点:
测试错误率的验证法估计的波动较大,因为取决于有效的观测是否在训练集中;
在验证法中只有一部分观测被用于拟合模型,模型与真实关系偏离较大,表现也不会太好,验证集错误率可能会高估在整个数据集上拟合模型所得到的测试错误率。
针对这两个问题,看看改进的验证集方法。
注:广义线性模型函数glm()拟合模型时,如果没有设定family参数,那么它就跟lm()函数一样执行的是线性回归。看以下示例:
glm.fit <- glm(mpg~horsepower,data=Auto)
#提取系数估计值
coef(glm.fit)
(Intercept) horsepower
39.9358610 -0.1578447
lm.fit <- lm(mpg~horsepower,data=Auto)
coef(lm.fit)
(Intercept) horsepower
39.9358610 -0.1578447
以上两种方法得到的是相同的回归模型。
#留一交叉验证法
本次试验中,用glm()函数来做线性回归,然后用cv.glm()函数来计算其LOOCV估计。
cv.glm()函数可以实现留一交叉验证法,也可以实现K折交叉验证法,返回多个组成部分的列表(查看帮助文档)。
library(boot)
glm.fit <- glm(mpg~horsepower,data=Auto)
cv.err <- cv.glm(Auto,glm.fit)
cv.err$delta
[1] 24.23151 24.23114
参数delta向量中的两个值为交叉验证的结果。
#用递增的多项式重复多项式拟合
cv.err <- rep(0,5)
for(i in 1:5){
glm.fit <- glm(mpg~poly(horsepower,3),data=Auto)
cv.err[i] <- cv.glm(Auto,glm.fit)$delta[1]
}
cv.err #可以得到5个测试错误率
原理:保留一个观测值作为验证,其他的观测作为训练集
当观测集很多时,留一交叉验证法的计算量比较大,耗时。
这种方法将观测集随机地分为K个大小基本一致的组(折),第一折作为验证集,剩下的K-1折拟合模型,重复K次,得到K个均方误差,那么K折CV估计为 CV(k)=1K∑ki=1MSEi 。
##K折交叉验证法
set.seed(1)
cv.err.10 <- rep(0,10)
for(i in 1:10){
glm.fit <- glm(mpg~poly(horsepower,i),data=Auto)
cv.err.10[i] <- cv.glm(Auto,glm.fit,K=10)$delta[1]
}
cv.err.10
当K=n时,LOOCV法是K折CV方法的特例。
K不等于n明显的优势:计算方便。
从偏差和方差均衡角度权衡:
LOOCV法能提供近似无偏的测试误差估计,因为每个训练集几乎包含了所有的观测。从减小偏差的角度,LOOCV法比K折CV法要好。而LOOCV法的方差波动较大,因为每个模型几乎是在相同的训练集上训练,因此得到的均方误差有高度的相关性,相关性高的量的均值具有更高的波动性。
自助法的优点是:可以用于衡量一种统计学习方法的估计和预测系数的波动性。
R中执行自助法:
(1)创建一个计算感兴趣的统计量的函数;
(2)用boot库中的boot()函数,可以通过反复地从数据集中有放回的抽取观测来执行自助法。
#自助法
#创建一个统计量函数
alpha.fn <- function(data,index){
X = data$X[index] #选定一定的观测数据集来计算统计量
Y = data$Y[index]
return((var(Y)-cor(X,Y))/(var(X)+var(Y)-2*cor(X,Y)))
}
library(ISLR)
#选择全部100个观测数集来计算
alpha.fn(Portfolio,1:100)
[1] 0.5638699
#下面用sample()函数随机地从1到100中有放回的选取100个观测
set.seed(11)
alpha.fn(Portfolio,sample(100,100,replace = T))
可以多次执行此命令,把相应的alpha估计记录下来,然后计算其标准差,来实现自助法分析。
下面用boot函数可以让这个方法自助运行。
library(boot)
#产生1000个alpha自助法的估计
boot(Portfolio,alpha.fn,R=1000)
返回原始数据统计量的估计值,及其标准差。
例如:估计线性回归模型的精度
#首先创建一个函数,输入数据集和观测序号的集合,返回回归系数的估计值。
boot.fn <- function(data,index){ return(coef(lm(mpg~horsepower,data=data,subset=index)))
}
boot.fn(Auto,1:392)
#得到回归系数的估计值
(Intercept) horsepower I(horsepower^2)
56.900099702 -0.466189630 0.001230536
#通过随机有放回地从观测里抽样,来产生对截距和斜率的自助法估计
set.seed(1)
boot.fn(Auto,sample(392,392,replace=T))
(Intercept) horsepower
40.4161602 -0.1619227
#通过boot函数来计算1000个截距和斜率的自助法估计的标准误差
boot(Auto,boot.fn,1000)
ORDINARY NONPARAMETRIC BOOTSTRAP
Call:
boot(data = Auto, statistic = boot.fn, R = 1000)
Bootstrap Statistics :
original bias std. error
t1* 39.9358610 0.0353322717 0.851997160
t2* -0.1578447 -0.0003835623 0.007409389
估计二次模型的精度
boot.fn <- function(data,index){
coef(lm(mpg~horsepower+I(horsepower^2),data=data,subset=index))
}
set.seed(11)
boot(Auto,boot.fn,R=1000)
ORDINARY NONPARAMETRIC BOOTSTRAP
Call:
boot(data = Auto, statistic = boot.fn, R = 1000)
Bootstrap Statistics :
original bias std. error
t1* 56.900099702 6.804846e-02 2.1668203830
t2* -0.466189630 -1.080139e-03 0.0344652101
t3* 0.001230536 4.322021e-06 0.0001244104
#查看summary()方法
summary(lm(mpg~poly(horsepower,2),data=Auto))$coef
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.44592 0.2209163 106.13030 2.752212e-289
poly(horsepower, 2)1 -120.13774 4.3739206 -27.46683 4.169400e-93
poly(horsepower, 2)2 44.08953 4.3739206 10.08009 2.196340e-21
我们希望从总体中反复地得到独立的数据集来训练模型,但一般情况下,很难从总体中反复抽取得到数据集。
自助法通过反复地有放回地从原始数据集中抽取观测得到数据集,这种方法就容易实现。