R语言第八讲 评估模型之交叉验证法分析案例

题目

      评估Auto数据集上拟合多个线性模型所产生的测试错误率。Auto数据集是存在与ISLR程序包中的一个摩托车相关数据的数据集,读者可自行下载ISLR程序包,并将Auto数据集加载。

相关资料

      交叉验证是在机器学习建立模型和验证模型参数时常用的办法。交叉验证,顾名思义,就是重复的使用数据,把得到的样本数据进行切分,组合为不同的训练集和测试集,用训练集来训练模型,用测试集来评估模型预测的好坏。在此基础上可以得到多组不同的训练集和测试集,某次训练集中的某样本在下次可能成为测试集中的样本,即所谓“交叉”。

那么什么时候才需要交叉验证呢?交叉验证用在数据不是很充足的时候。比如在我日常项目里面,对于普通适中问题,如果数据样本量小于一万条,我们就会采用交叉验证来训练优化选择模型。如果样本大于一万条的话,我们一般随机的把数据分成三份,一份为训练集(Training Set),一份为验证集(Validation Set),最后一份为测试集(Test Set)。用训练集来训练模型,用验证集来评估模型预测的好坏和选择模型及其对应的参数。把最终得到的模型再用于测试集,最终决定使用哪个模型以及对应参数。

回到交叉验证,根据切分的方法不同,交叉验证分为下面三种:

第一种是简单交叉验证,所谓的简单,是和其他交叉验证方法相对而言的。首先,我们随机的将样本数据分为两部分(比如: 70%的训练集,30%的测试集),然后用训练集来训练模型,在测试集上验证模型及参数。接着,我们再把样本打乱,重新选择训练集和测试集,继续训练数据和检验模型。最后我们选择损失函数评估最优的模型和参数。

第二种是S折交叉验证(S-Folder Cross Validation)。和第一种方法不同,S折交叉验证会把样本数据随机的分成S份,每次随机的选择S-1份作为训练集,剩下的1份做测试集。当这一轮完成后,重新随机选择S-1份来训练数据。若干轮(小于S)之后,选择损失函数评估最优的模型和参数。

第三种是留一交叉验证(Leave-one-out Cross Validation),它是第二种情况的特例,此时S等于样本数N,这样对于N个样本,每次选择N-1个样本来训练数据,留一个样本来验证模型预测的好坏。此方法主要用于样本量非常少的情况,比如对于普通适中问题,N小于50时,我一般采用留一交叉验证。

通过反复的交叉验证,用损失函数来度量得到的模型的好坏,最终我们可以得到一个较好的模型。那这三种情况,到底我们应该选择哪一种方法呢?一句话总结,如果我们只是对数据做一个初步的模型建立,不是要做深入分析的话,简单交叉验证就可以了。否则就用S折交叉验证。在样本量少的时候,使用S折交叉验证的特例留一交叉验证。

此外还有一种比较特殊的交叉验证方式,也是用于样本量少的时候。叫做自助法(bootstrapping)。比如我们有m个样本(m较小),每次在这m个样本中随机采集一个样本,放入训练集,采样完后把样本放回。这样重复采集m次,我们得到m个样本组成的训练集。当然,这m个样本中很有可能有重复的样本数据。同时,用没有被采样到的样本做测试集。这样接着进行交叉验证。由于我们的训练集有重复数据,这会改变数据的分布,因而训练结果会有估计偏差,因此,此种方法不是很常用,除非数据量真的很少,比如小于20个。

        随机抽样函数sample介绍:

 

> x=1:10

> sample(x=x)
[1]  3  5  9  6 10  7  2  1  8  4

        第一行代码表示给x向量x赋值,第二行代码表示对x向量进行随机抽样。结果输出为每次抽样抽得的结果,可以看出该抽样为无放回抽样-最多抽n次,n为向量中元素的个数。

       如果想指定在该向量中抽取元素的个数,需要加一个参数size:

> x=1:1000

> sample(x=x,size=20)

[1]  66 891 606 924 871 374 879 573 284 305 914 792 398 497 721 897 324 437

[19] 901  33

       这是在1~1000的正整数中抽样,其中size指定抽样的次数,抽了20次,结果如上所示。

       这些都是无放回抽样。所谓无放回抽样,也就是说某个元素一旦被选择,该总体中就不会再有该元素。如果是有放回抽样,则需添加一个参数repalce=T:

> x=1:10

> sample(x=x,size=5,replace=T)

[1] 4 7 2 4 8

       “replace”就是重复的意思。即可以重复对元素进行抽样,也就是所谓的有放回抽样。我们看上面的结果,元素4在5次随机抽样的过程中被抽取了两次。

       如果我们输入代码的位置与某个函数中参数的位置一一对应的话,我们可以不写该函数的参数,如:

> x=1:10

> sample(x,20,T)

[1] 1 2 2 1 5 5 5 9 9 5 2 9 8 3 4 8 8 8 1 1

      简单运用:对于掷骰子,投硬币(这可能是介绍抽样必介绍的内容),都属于有放回抽样。

       这里要说明,对于sample函数,参数x可以是数值,也可以是字符,实际上参数x代表任意一个向量:

> a=c("A","B")

> sample(x=a,size=10,replace=T)

[1] "B" "A" "A" "A" "B" "A" "A" "B" "A" "A"

      上述代码可以理解为掷硬币,抛了10次,其中正面(A)与反面(B)出现的次数是可以重复的。

     上述抽样过程,每一个元素被抽取的概率相等,称为随机抽样。

     有时候我们的抽取元素的概率未必相等(如常见的二项分布概率问题),此时我们需要添加一个参数prob,即:“probability”(概率)的缩写。假设一名医生给患者做某手术成功的概率是80%,那么现在他给20例病人做手术,可能有哪几次是成功的呢?代码如下:

> x=c("S","F")

> sample(x,size=20,replace=T,prob=c(0.8,0.2))

[1] "F" "S" "S" "S" "S" "S" "S" "S" "S" "S" "S" "S" "F" "S" "S" "F" "S" "S"

[19] "F" "S"

     其中“S”代表成功,“F”代表失败。

> x=c(1,3,5,7)

> sample(x,size=20,replace=T,prob=c(0.1,0.2,0.3,0.9))

[1] 3 5 7 3 7 3 7 5 3 7 7 7 1 5 7 5 7 7 3 7

      这些代码告诉我们,对每一个元素都可以给定一个概率,且每个概率是独立的,即在参数prob中,不一定所有元素的概率加起来等于1,它只代表某元素被抽取的概率而已。

     对于sample函数,其参数x可以是R中任意一个对象(如上述对字符的抽样)。其中还有一个功能相同的函数sample.int,“int”即“intger”的缩写,也就是“整数”。它的参数n必须是正整数:

> x=-10.5:7.5

> sample(x=x,size=3)

> sample.int(n=x,size=3)

[1] -5.5 -7.5  0.5

Error in sample.int(x, size = 3) : invalid first argument

        第一行代码生成了-10.5到7.5的等差数列,结果输出的第一行是sample的结果;第二行是sample.int的结果,提示错误:“第一个自变量无效”,因为它不是正整数。其余的用法与ample是一样的。

 

实验

      在开始之前,用 set. seed ()函数来为 R 的随机数生成器设定一个种子 (seed) .这样就就可以得到与下面展示的完全相同的结果。通常来说,使用一种如同交叉验证法这样包含随机性的分析方法时,可以设定一个随机种子,这样下次就可以得到完全相同的结果。

       首先用 sample()函数把观测集分为两半,从原始的 392 个观测中随机地选取一个有 196 个观测的子集,作为训练集。

> library(ISLR)
> set.seed(1)
#产生从1-392中抽样出196个数。
> train=sample(392,196)
[1] 324 167 129 299 270 187 307  85 277 362 330 263 329  79 213  37 105
[18] 217 366 165 290 383  89 289 340 326 382  42 111  20  44 343  70 121
[35]  40 172  25 248 198  39 298 280 160  14 130  45  22 206 230 193 104
[52] 367 255 341 342 103 331  13 296 375 176 279 110  84  29 141 252 221
[69] 108 304  33 347 149 287 102 145 118 323 107  64 224 337  51 325 372
[86] 138 390 389 282 143 285 170  48 204 295  24 181 214 225 163  43   1
[103] 328  78 284 116 233  61  86 374  49 242 246 247 239 219 135 364 363
[120] 310  53 348  65 376 124  77 218  98 194  19  31 174 237  75  16 358
[137]   9  50  92 122 152 386 207 244 229 350 355 391 223 373 309 140 126
[154] 349 344 319 258  15 271 388 195 201 318  17 212 127 133  41 384 392
[171] 159 117  72  36 315 294 157 378 313 306 272 106 185  88 281 228 238
[188] 368  80  30  93 234 220 240 369 164

         用 lm() 函数中的 subset 选项,只用训练集中的观测来拟合一个线性回归模型。

> lm.fit=lm(mpg~horsepower,data=Auto,subset=train)

       现在用 predict ()函数来估计全部 392 个观测的响应变量,再用 mean() 函数来计算验证集中 196 个观测的均方误差。注意一下,下面的 -train 指标意味着只选取不在训练集中的观测。(说明:‘-’就是subset)

> attach(Auto)
> mean((mpg-predict(lm.fit,Auto))[-train]^2)
[1] 23.26601

     从以上数据可以看出用线性回归拟合模型所产生的测试的均方误差估计为 23.26601。下面用 poly ()函数来估计用二次和三次多项式回归所产生的测试误差。

> lm.fit2=lm(mpg~poly(horsepower,2),data=Auto,subset=train)#poly函数估计二次
> mean((mpg-predict(lm.fit2,Auto))[-train]^2)
[1] 18.71646
> lm.fit3=lm(mpg~poly(horsepower,3),data=Auto,subset=train)#和三次
> mean((mpg-predict(lm.fit3,Auto))[-train]^2)
[1] 18.79401

       这两个错误率分别为 18.71646 和 18.79401。如果选择了一个不同的训练集的话,那就会在验证集上 得到一个不同的误差。

> set.seed(2)
> train=sample(392,196)
> lm.fit=lm(mpg~horsepower,subset=train)
> mean((mpg-predict(lm.fit,Auto))[-train]^2)
[1] 25.72651
> lm.fit2=lm(mpg~poly(horsepower,2),data=Auto,subset=train)
> mean((mpg-predict(lm.fit2,Auto))[-train]^2)
[1] 20.43036
> lm.fit3=lm(mpg~poly(horsepower,3),data=Auto,subset=train)
> mean((mpg-predict(lm.fit3,Auto))[-train]^2)
[1] 20.38533

        案例分析完毕,小伙伴们学到了没有(*^_^*)
 

你可能感兴趣的:(R语言,统计学习)