44-R语言机器学习:分类回归树

《精通机器学习:基于R 第二版》学习笔记

1、商业案例

在前面的内容中,我们通过努力建立了一些模型,现在看看我们能否提高这些模型的预测能力。对于回归问题,还是使用前列腺癌数据集,以LASSO回归0.444的均方误差作为基准,看看能否改善。
对于分类问题,使用乳腺癌数据集和皮玛印第安人糖尿病数据集。在乳腺癌数据集中,我们逻辑斯蒂回归取得了98%的预测正确率。在糖尿病数据集中,SVM模型的正确率是76%,我们希望再提高一些。

2、模型构建与模型评价

2.1 回归树

树方法的精髓就是划分特征,从第一次分裂开始就要考虑如何最大程度改善RSS,然后持续进行二叉分裂,直到树结束。后面的划分并不作用于全体数据集,而仅作用于上次划分时落到这个分支之下的那部分数据。这个自顶向下的过程被称为“递归划分”。这个过程是贪婪的,这个名词在研究机器学习方法时会经常遇到。贪婪的含义是,算法在每次分裂中都追求最大程度减少RSS,而不管以后的划分中表现如何。这样做的结果是,你可能会生成一个带有无效分支的树,尽管偏差很小,但是方差很大。为了避免这个问题,生成完整的树之后,你要对树进行剪枝,得到最优的规模。
这种方法的优点是可以处理高度非线性关系,但它还存在一些潜在的问题,首要的问题就是,一个观测被赋予所属终端节点的平均值,这会损害整体预测效果(高偏差)。相反,如果你一直对数据进行划分,树的层次越来越深,这样可以达到低偏差的效果,但是高方差又成了问题。

> # 前列腺癌数据集
> load("./data_set/prostate.RData")
> dim(prostate)
## [1] 97 10
> # 将gleason评分编码为指标变量
> prostate$gleason <- ifelse(prostate$gleason < 7, 0, 1)
> # 划分训练数据集和测试数据集
> pros.train <- subset(prostate, train == TRUE)[, 1:9]
> pros.test <- subset(prostate, train == FALSE)[, 1:9]
> dim(pros.train)
## [1] 67  9
> dim(pros.test)
## [1] 30  9
> library(pacman)
> p_load(rpart)
> tree.pros <- rpart(lpsa ~ ., data = pros.train)
> print(tree.pros$cptable)
##           CP nsplit rel error    xerror       xstd
## 1 0.35852251      0 1.0000000 1.0157577 0.17603383
## 2 0.12295687      1 0.6414775 0.9660475 0.13092855
## 3 0.11639953      2 0.5185206 0.7843392 0.10082254
## 4 0.05350873      3 0.4021211 0.6784125 0.08999510
## 5 0.01032838      4 0.3486124 0.5978760 0.07845462
## 6 0.01000000      5 0.3382840 0.5912064 0.08078274

标号为CP的第一列是成本复杂性参数,第二列nsplit是树分裂的次数,rel error列表示相对误差,即某次分裂的RSS除以不分裂的RSS(RSS(k)/RSS(0)),xerror和xstd都是基于10折交叉验证的,xerror是平均误差,xstd是交叉验证过程的标准差。
可以看出,5次分裂在整个数据集上产生的误差最小。
查看统计图:

> plotcp(tree.pros)
回归树统计图

这幅图使用误差条表示树的规模和相对误差之间的关系,误差条和树规模是对应的。选择树的规模为5,也就是经过4次分裂可以建立一个新的树对象,这个树的xerror已经最小,无需剪枝。

> p_load(partykit)
> plot(as.party(tree.pros))
partykit树图

由partykit包生成的树图明显优于party包生成的。查看模型在测试集上的表现:

> party.pros.test <- predict(tree.pros, newdata = pros.test)
> rpart.resid <- party.pros.test - pros.test$lpsa
> accu.rpart <- mean(rpart.resid^2)
> print(accu.rpart)
## [1] 0.6136057

基准MSE为0.444,我们的努力并没有改善预测结果。但是,这种方法并非一无是处。从生成的树图中可以看出对响应变量影响最大的特征(lcavol),并且很容易解释,这往往比正确率重要得多。

2.2 分类树

分类树与回归树的运行原理是一样的,区别在于决定分裂过程的不是RSS,而是误差率。

> # 乳腺癌数据集
> p_load(MASS)
> str(biopsy)
## 'data.frame':    683 obs. of  10 variables:
##  $ thick  : int  5 5 3 6 4 8 1 2 2 4 ...
##  $ u.size : int  1 4 1 8 1 10 1 1 1 2 ...
##  $ u.shape: int  1 4 1 8 1 10 1 2 1 1 ...
##  $ adhsn  : int  1 5 1 1 3 8 1 1 1 1 ...
##  $ s.size : int  2 7 2 3 2 7 2 2 2 2 ...
##  $ nucl   : int  1 10 2 4 1 10 10 1 1 1 ...
##  $ V7     : int  3 3 3 3 3 9 3 3 1 2 ...
##  $ V8     : int  1 2 1 7 1 7 1 1 1 1 ...
##  $ V9     : int  1 1 1 1 1 1 1 1 5 1 ...
##  $ class  : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
##  - attr(*, "na.action")= 'omit' Named int  24 41 140 146 159 165 236 250 276 293 ...
##   ..- attr(*, "names")= chr  "24" "41" "140" "146" ...
> biopsy <- biopsy %>% select(-"ID") %>% rename(thick = V1, u.size = V2, u.shape = V3, 
+     adhsn = V4, s.size = V5, nucl = V6) %>% na.omit()

> set.seed(123)
> ind <- sample(2, nrow(biopsy), replace = T, prob = c(0.7, 0.3))
> biop.train <- biopsy[ind == 1, ]
> biop.test <- biopsy[ind == 2, ]
> 
> # 确保结果变量是因子类型
> class(biopsy$class)
## [1] "factor"

先生成树,找到最优分裂次数:

> set.seed(123)
> tree.biop <- rpart(class ~ ., data = biop.train)
> tree.biop$cptable
##           CP nsplit rel error    xerror       xstd
## 1 0.79651163      0 1.0000000 1.0000000 0.06086254
## 2 0.07558140      1 0.2034884 0.2616279 0.03710371
## 3 0.01162791      2 0.1279070 0.1511628 0.02882093
## 4 0.01000000      3 0.1162791 0.1511628 0.02882093

交叉验证误差仅在两次分裂后就达到了最小值(见第3行)。现在可以对树进行剪枝,再在图中绘制剪枝树,看看它在测试集上的表现:

> cp <- min(tree.biop$cptable[3, ])
> prune.tree.biop <- prune(tree.biop, cp = cp)
> plot(as.party(prune.tree.biop))
剪枝后的生成树

从对树图的检查中可以发现,细胞大小均匀度是第一个分裂点,第二个是nucl。完整树还有一个分支是细胞浓度。在测试集上测试:

> rpart.test <- predict(prune.tree.biop, newdata = biop.test, type = "class")
> table(rpart.test, biop.test$class)
##            
## rpart.test  benign malignant
##   benign       136         3
##   malignant      6        64
> accu.rpart.class <- (136 + 64)/nrow(biop.test)
> print(accu.rpart.class)
## [1] 0.9569378

只有两个分支的基本树模型给出了差不多96%的正确率,比逻辑斯蒂回归还是要差些。

2.3 随机森林

为了显著提高模型的预测能力,我们可以生成多个树,然后将这些树的结果组合起来。随机森林技术在模型构建过程中使用两种奇妙的方法,以实现这个构想。第一个方法称为自助聚集,或称装袋。另一个方法是,对数据进行随机抽样(装袋)的同时,独立树每次分裂时对输入特征也进行随机抽样。通过每次分裂时对特征的随机抽样以及由此形成的一套方法,你可以减轻高度相关的预测特征的影响,这种预测特征在由装袋法生成的独立树中往往起主要作用。这种方法还使你不必思索如何减少装袋导致的高方差。独立树彼此之间的相关性减少之后,对结果的平均化可以使泛化效果更好,对于异常值的影响也更加不敏感,比仅进行装袋的效果要好。

2.3.1 随机森林回归

> p_load(randomForest)
> # 前列腺癌数据集
> set.seed(123)
> rf.pros <- randomForest(lpsa ~ ., data = pros.train)
> rf.pros
## 
## Call:
##  randomForest(formula = lpsa ~ ., data = pros.train) 
##                Type of random forest: regression
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##           Mean of squared residuals: 0.6936697
##                     % Var explained: 51.73

随机森林生成了500个不同的树(默认设置),并且在每次树分裂时随机抽出两个变量。结果的MSE为0.69,差不多52%的方差得到了解释。下面看看能否对默认数量的树做一些改善。过多的树会导致过拟合,当然,“多大的数量是‘过多’”依赖于数据规模。

> plot(rf.pros)
随机森林回归

这个图表示MSE与模型中树的数量之间的关系。可以看出,树的数量增加时,一开始MSE会有显著改善,当森林中大约建立了90棵树之后,改善几乎停滞,甚至还有降低。

找出最优树数量:

> which.min(rf.pros$mse)
## [1] 80

试试只有80棵树的随机森林:

> set.seed(123)
> rf.pros.80 <- randomForest(lpsa ~ ., data = pros.train, ntree = 80)
> rf.pros.80
## 
## Call:
##  randomForest(formula = lpsa ~ ., data = pros.train, ntree = 80) 
##                Type of random forest: regression
##                      Number of trees: 80
## No. of variables tried at each split: 2
## 
##           Mean of squared residuals: 0.6566502
##                     % Var explained: 54.31

可以看到,MSE和解释方差都有一点微小的改善。

> varImpPlot(rf.pros.80, scale = T, main = "Variable Importance Plot - PSA Score")
最优树回归

该统计图Y轴是按重要性降序排列的变量列表,X轴是MSE改善百分比。请注意,在分类问题中,X轴应该是基尼指数的改善。
lcavol 是最重要的变量,lweight 次之。如果想查看具体数据,可以使用 importance() 函数。

> randomForest::importance(rf.pros.80)
##         IncNodePurity
## lcavol      25.011557
## lweight     15.822110
## age          7.167320
## lbph         5.471032
## svi          8.497838
## lcp          8.113947
## gleason      4.990213
## pgg45        6.663911

看看模型在测试集上的表现:

> rf.pros.test <- predict(rf.pros.80, newdata = pros.test)
> rf.resid <- rf.pros.test - pros.test$lpsa
> 
> accu.rf <- mean(rf.resid^2)
> print(accu.rf)
## [1] 0.5512549

MSE依然高于使用LASSO得到的0.444。

2.3.2 随机森林分类

随机森林的真正威力在于解决分类问题。

> # 乳腺癌数据集
> set.seed(123)
> rf.biop <- randomForest(class ~ ., data = biop.train)
> rf.biop
## 
## Call:
##  randomForest(formula = class ~ ., data = biop.train) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: 3.38%
## Confusion matrix:
##           benign malignant class.error
## benign       294         8  0.02649007
## malignant      8       164  0.04651163

OOB(袋外数据)误差率为3.38%。

> plot(rf.biop)
随机森林分类

图中显示,误差和标准误差的最小值是在树的数量很少的时候取得的,可以使用which.min() 函数找出具体值。

> which.min(rf.biop$err.rate[, 1])
## [1] 125

125棵树就可以使模型正确率达到最优。看看模型的表现:

> set.seed(123)
> rf.biop.125 <- randomForest(class ~ ., data = biop.train, ntree = 125)
> print(rf.biop.125)
## 
## Call:
##  randomForest(formula = class ~ ., data = biop.train, ntree = 125) 
##                Type of random forest: classification
##                      Number of trees: 125
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: 2.95%
## Confusion matrix:
##           benign malignant class.error
## benign       294         8  0.02649007
## malignant      6       166  0.03488372
> rf.biop.125.test <- predict(rf.biop.125, newdata = biop.test, type = "response")
> table(rf.biop.125.test, biop.test$class)
##                 
## rf.biop.125.test benign malignant
##        benign       138         0
##        malignant      4        67
> accu.rf.125 <- (138 + 67)/nrow(biop.test)
> print(accu.rf.125)
## [1] 0.9808612

训练集上的误差率约为3%,在测试集的209个观测中,只有4个观测被误分类,而且没有一个是误诊为“恶性”的。回忆一下,我们使用逻辑斯蒂回归得到的正确率也是约为98%。

> varImpPlot(rf.biop.125)
变量重要性

图中,变量重要性是指每个变量对基尼指数平均减少量的贡献,此处的变量重要性与单个树分裂时有很大区别。回忆一下,单个树是在细胞大小均匀度开始分裂的,然后是 nucl,接着是细胞密度。这揭示了随机森林技术具有非常大的潜力,不但可以提高模型预测能力,还可以改善特征选择的结果。

再看看pima印第安人糖尿病数据:

> pima <- rbind(Pima.tr, Pima.te)
> set.seed(123)
> ind <- sample(2, nrow(pima), replace = T, prob = c(0.7, 0.3))
> pima.train <- pima[ind == 1, ]
> pima.test <- pima[ind == 2, ]
> 
> set.seed(111)
> rf.pima <- randomForest(type ~ ., data = pima.train)
> rf.pima
## 
## Call:
##  randomForest(formula = type ~ ., data = pima.train) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 22.05%
## Confusion matrix:
##      No Yes class.error
## No  230  26   0.1015625
## Yes  58  67   0.4640000
> which.min(rf.pima$err.rate[, 1])
## [1] 342
> set.seed(111)
> rf.pima.342 <- randomForest(type ~ ., data = pima.train, ntree = 342)
> print(rf.pima.342)
## 
## Call:
##  randomForest(formula = type ~ ., data = pima.train, ntree = 342) 
##                Type of random forest: classification
##                      Number of trees: 342
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 20.73%
## Confusion matrix:
##      No Yes class.error
## No  231  25  0.09765625
## Yes  54  71  0.43200000
> rf.pima.test <- predict(rf.pima.342, newdata = pima.test, type = "response")
> table(rf.pima.test, pima.test$type)
##             
## rf.pima.test No Yes
##          No  86  15
##          Yes 13  37
> accu.pima <- (86 + 37)/nrow(pima.test)
> print(accu.pima)
## [1] 0.8145695

正确率为81%,高于SVM模型的76%。

2.4 梯度提升-分类

梯度提升的主要思想是,先建立一个某种形式的初始模型(线性、样条、树或其他),称为基学习器;然后检查残差,在残差的基础上围绕损失函数拟合模型。损失函数测量模型和现实之间的差别,例如,在回归问题中可以用误差的平方,在分类问题中可以用逻辑斯蒂函数。一直继续这个过程,直到满足某个特定的结束条件。

极限梯度提升模型需要调整一些参数,如下:
 nrounds :最大迭代次数(最终模型中树的数量)
 colsample_bytree :建立树时随机抽取的特征数量,用一个比率表示,默认值为1(使用100%的特征)
 min_child_weight :对树进行提升时使用的最小权重,默认为1
 eta :学习率,每棵树在最终解中的贡献,默认为0.3
 gamma :在树中新增一个叶子分区时所需的最小减损。
 subsample :子样本数据占整个观测的比例,默认值为1(100%)
 max_depth :单个树的最大深度

建立网格,对于上面列出的参数,如果没有设定具体值,那么即使有默认值,运行函数时也会收到出错信息。

> # 以下参数可根据多次实验调整
> grid <- expand.grid(nrounds = c(75, 100), colsample_bytree = 1, min_child_weight = 1, 
+     eta = c(0.01, 0.1, 0.3), gamma = c(0.5, 0.25), subsample = 0.5, max_depth = c(2, 3))

以上命令会建立一个具有24个模型的网格,caret包会运行这些模型,以确定最好的调优参数。此处必须注意,对于我们现在所用的这种规模的数据集,代码运行过程只需几秒钟,但对于一些大数据集,这个运行过程可能需要几小时。所以,在时间非常宝贵的情况下,必须应用自己的判断力,并使用小数据样本来找出合适的调优参数,否则硬盘空间可能不足。

> p_load(xgboost)
> 
> cntrl <- trainControl(
+   method = "cv",
+   number = 5,
+   # TURE可以看到每折交叉验证中的每次训练迭代
+   verboseIter = TRUE,
+   returnData = FALSE,
+   returnResamp = "final"
+ )
> 
> set.seed(12)
> train.xgb <- train(
+   x = pima.train[,1:7],
+   y = pima.train[,8],
+   trControl = cntrl,
+   tuneGrid = grid,
+   method = "xgbTree"
+ )
## + Fold1: eta=0.01, max_depth=2, gamma=0.25, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## - Fold1: eta=0.01, max_depth=2, gamma=0.25, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## + Fold1: eta=0.01, max_depth=2, gamma=0.50, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## - Fold1: eta=0.01, max_depth=2, gamma=0.50, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## + Fold1: eta=0.01, max_depth=3, gamma=0.25, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## - Fold1: eta=0.01, max_depth=3, gamma=0.25, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## ---------------------------------------------------------------------------------------------------------------
## - Fold5: eta=0.30, max_depth=3, gamma=0.25, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## + Fold5: eta=0.30, max_depth=3, gamma=0.50, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## - Fold5: eta=0.30, max_depth=3, gamma=0.50, colsample_bytree=1, min_child_weight=1, subsample=0.5, nrounds=100 
## Aggregating results
## Selecting tuning parameters
## Fitting nrounds = 100, max_depth = 3, eta = 0.01, gamma = 0.25, colsample_bytree = 1, min_child_weight = 1, subsample = 0.5 on full training set
> # 调用对象可以得到最优的参数,以及参数设置的结果
> train.xgb
## eXtreme Gradient Boosting 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 305, 305, 305, 304, 305 
## Resampling results across tuning parameters:
## 
##   eta   max_depth  gamma  nrounds  Accuracy   Kappa    
##   0.01  2          0.25    75      0.7767601  0.4395523
##   0.01  2          0.25   100      0.7793575  0.4469740
##   0.01  2          0.50    75      0.7767943  0.4346601
##   0.01  2          0.50   100      0.7819891  0.4510575
##   0.01  3          0.25    75      0.8003418  0.5061325
##   0.01  3          0.25   100      0.8056049  0.5171773
##   0.01  3          0.50    75      0.7950103  0.5004624
##   0.01  3          0.50   100      0.7950103  0.4969394
##   0.10  2          0.25    75      0.7872522  0.4950665
##   0.10  2          0.25   100      0.7740602  0.4717656
##   0.10  2          0.50    75      0.7818524  0.4778794
##   0.10  2          0.50   100      0.7766234  0.4704944
##   0.10  3          0.25    75      0.7740602  0.4619805
##   0.10  3          0.25   100      0.7740602  0.4659890
##   0.10  3          0.50    75      0.7609706  0.4435785
##   0.10  3          0.50   100      0.7661996  0.4536559
##   0.30  2          0.25    75      0.7478127  0.4211636
##   0.30  2          0.25   100      0.7477785  0.4239837
##   0.30  2          0.50    75      0.7477444  0.4147580
##   0.30  2          0.50   100      0.7347232  0.3851878
##   0.30  3          0.25    75      0.7401572  0.3967503
##   0.30  3          0.25   100      0.7164388  0.3404687
##   0.30  3          0.50    75      0.7294600  0.3654798
##   0.30  3          0.50   100      0.7031442  0.3088972
## 
## Tuning parameter 'colsample_bytree' was held constant at a value of 1
## 
## Tuning parameter 'min_child_weight' was held constant at a value of 1
## 
## Tuning parameter 'subsample' was held constant at a value of 0.5
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were nrounds = 100, max_depth = 3, eta
##  = 0.01, gamma = 0.25, colsample_bytree = 1, min_child_weight = 1
##  and subsample = 0.5.

由此可以得到最优的参数组合来建立模型,nrounds = 100, max_depth = 3, eta = 0.01, gamma = 0.25, colsample_bytree = 1, min_child_weight = 1 and subsample = 0.5。模型在训练数据上的正确率约81%,Kappa值是0.52。

> param <- list(objective = "binary:logistic", booster = "gbtree", eval_metric = "error", 
+     eta = 0.01, max_depth = 3, subsample = 0.5, colsample_bytree = 1, gamma = 0.25)
> x <- as.matrix(pima.train[, 1:7])
> y <- ifelse(pima.train$type == "Yes", 1, 0)
> train.mat <- xgb.DMatrix(data = x, label = y)
> 
> set.seed(12)
> xgb.fit <- xgb.train(params = param, data = train.mat, nrounds = 100)
> impMatrix <- xgb.importance(feature_names = dimnames(x)[[2]], model = xgb.fit)
> impMatrix
##    Feature        Gain       Cover  Frequency
## 1:     glu 0.496869681 0.379403885 0.27897839
## 2:     age 0.167522580 0.183179212 0.17288802
## 3:     bmi 0.166443148 0.203638740 0.22396857
## 4:     ped 0.099611365 0.123512387 0.18664047
## 5:   npreg 0.038740289 0.064379469 0.06483301
## 6:    skin 0.025254579 0.036745555 0.05304519
## 7:      bp 0.005558357 0.009140753 0.01964637

Gain是这个特征对其所在分支的正确率做出的改善
Cover是与这个特征相关的全体观测的相对数量
Frequency是这个特征在所有树中出现的次数百分比

> xgb.plot.importance(impMatrix, main = "Gain by Feature")
梯度提升变量重要性

看看在测试集上的表现:

> p_load(InformationValue)
> pred <- predict(xgb.fit, x)
> 
> # optimalCutoff() 函数可以找出使误差最小化的最优概率阈值
> optimalCutoff(y, pred)
## [1] 0.489459
> pima.testMat <- as.matrix(pima.test[, 1:7])
> xgb.pima.test <- predict(xgb.fit, pima.testMat)
> y.test <- ifelse(pima.test$type == "Yes", 1, 0)
> confusionMatrix(y.test, xgb.pima.test, threshold = 0.39)
##    0  1
## 0 71  8
## 1 28 44
> accu.xgb <- (71 + 44)/nrow(pima.test)
> print(accu.xgb)
## [1] 0.7615894

跟SVM模型的表现差不多。

> plotROC(y.test, xgb.pima.test)
梯度提升模型ROC曲线

3、使用随机森林进行特征选择

常用的特征选择技术有:正则化、最优子集、递归特征消除。另外,Boruta包使用随机森林方法也可以进行特征选择:

> data(Sonar, package = "mlbench")
> str(Sonar)
## 'data.frame':    208 obs. of  61 variables:
##  $ V1   : num  0.02 0.0453 0.0262 0.01 0.0762 0.0286 0.0317 0.0519 0.0223 0.0164 ...
##  $ V2   : num  0.0371 0.0523 0.0582 0.0171 0.0666 0.0453 0.0956 0.0548 0.0375 0.0173 ...
##  $ V3   : num  0.0428 0.0843 0.1099 0.0623 0.0481 ...
##  $ V4   : num  0.0207 0.0689 0.1083 0.0205 0.0394 ...
##  $ V5   : num  0.0954 0.1183 0.0974 0.0205 0.059 ...
##  $ V6   : num  0.0986 0.2583 0.228 0.0368 0.0649 ...
##  $ V7   : num  0.154 0.216 0.243 0.11 0.121 ...
##  $ V8   : num  0.16 0.348 0.377 0.128 0.247 ...
##  $ V9   : num  0.3109 0.3337 0.5598 0.0598 0.3564 ...
##  $ V10  : num  0.211 0.287 0.619 0.126 0.446 ...
##  $ V11  : num  0.1609 0.4918 0.6333 0.0881 0.4152 ...
##  $ V12  : num  0.158 0.655 0.706 0.199 0.395 ...
##  $ V13  : num  0.2238 0.6919 0.5544 0.0184 0.4256 ...
##  $ V14  : num  0.0645 0.7797 0.532 0.2261 0.4135 ...
##  $ V15  : num  0.066 0.746 0.648 0.173 0.453 ...
##  $ V16  : num  0.227 0.944 0.693 0.213 0.533 ...
##  $ V17  : num  0.31 1 0.6759 0.0693 0.7306 ...
##  $ V18  : num  0.3 0.887 0.755 0.228 0.619 ...
##  $ V19  : num  0.508 0.802 0.893 0.406 0.203 ...
##  $ V20  : num  0.48 0.782 0.862 0.397 0.464 ...
##  $ V21  : num  0.578 0.521 0.797 0.274 0.415 ...
##  $ V22  : num  0.507 0.405 0.674 0.369 0.429 ...
##  $ V23  : num  0.433 0.396 0.429 0.556 0.573 ...
##  $ V24  : num  0.555 0.391 0.365 0.485 0.54 ...
##  $ V25  : num  0.671 0.325 0.533 0.314 0.316 ...
##  $ V26  : num  0.641 0.32 0.241 0.533 0.229 ...
##  $ V27  : num  0.71 0.327 0.507 0.526 0.7 ...
##  $ V28  : num  0.808 0.277 0.853 0.252 1 ...
##  $ V29  : num  0.679 0.442 0.604 0.209 0.726 ...
##  $ V30  : num  0.386 0.203 0.851 0.356 0.472 ...
##  $ V31  : num  0.131 0.379 0.851 0.626 0.51 ...
##  $ V32  : num  0.26 0.295 0.504 0.734 0.546 ...
##  $ V33  : num  0.512 0.198 0.186 0.612 0.288 ...
##  $ V34  : num  0.7547 0.2341 0.2709 0.3497 0.0981 ...
##  $ V35  : num  0.854 0.131 0.423 0.395 0.195 ...
##  $ V36  : num  0.851 0.418 0.304 0.301 0.418 ...
##  $ V37  : num  0.669 0.384 0.612 0.541 0.46 ...
##  $ V38  : num  0.61 0.106 0.676 0.881 0.322 ...
##  $ V39  : num  0.494 0.184 0.537 0.986 0.283 ...
##  $ V40  : num  0.274 0.197 0.472 0.917 0.243 ...
##  $ V41  : num  0.051 0.167 0.465 0.612 0.198 ...
##  $ V42  : num  0.2834 0.0583 0.2587 0.5006 0.2444 ...
##  $ V43  : num  0.282 0.14 0.213 0.321 0.185 ...
##  $ V44  : num  0.4256 0.1628 0.2222 0.3202 0.0841 ...
##  $ V45  : num  0.2641 0.0621 0.2111 0.4295 0.0692 ...
##  $ V46  : num  0.1386 0.0203 0.0176 0.3654 0.0528 ...
##  $ V47  : num  0.1051 0.053 0.1348 0.2655 0.0357 ...
##  $ V48  : num  0.1343 0.0742 0.0744 0.1576 0.0085 ...
##  $ V49  : num  0.0383 0.0409 0.013 0.0681 0.023 0.0264 0.0507 0.0285 0.0777 0.0092 ...
##  $ V50  : num  0.0324 0.0061 0.0106 0.0294 0.0046 0.0081 0.0159 0.0178 0.0439 0.0198 ...
##  $ V51  : num  0.0232 0.0125 0.0033 0.0241 0.0156 0.0104 0.0195 0.0052 0.0061 0.0118 ...
##  $ V52  : num  0.0027 0.0084 0.0232 0.0121 0.0031 0.0045 0.0201 0.0081 0.0145 0.009 ...
##  $ V53  : num  0.0065 0.0089 0.0166 0.0036 0.0054 0.0014 0.0248 0.012 0.0128 0.0223 ...
##  $ V54  : num  0.0159 0.0048 0.0095 0.015 0.0105 0.0038 0.0131 0.0045 0.0145 0.0179 ...
##  $ V55  : num  0.0072 0.0094 0.018 0.0085 0.011 0.0013 0.007 0.0121 0.0058 0.0084 ...
##  $ V56  : num  0.0167 0.0191 0.0244 0.0073 0.0015 0.0089 0.0138 0.0097 0.0049 0.0068 ...
##  $ V57  : num  0.018 0.014 0.0316 0.005 0.0072 0.0057 0.0092 0.0085 0.0065 0.0032 ...
##  $ V58  : num  0.0084 0.0049 0.0164 0.0044 0.0048 0.0027 0.0143 0.0047 0.0093 0.0035 ...
##  $ V59  : num  0.009 0.0052 0.0095 0.004 0.0107 0.0051 0.0036 0.0048 0.0059 0.0056 ...
##  $ V60  : num  0.0032 0.0044 0.0078 0.0117 0.0094 0.0062 0.0103 0.0053 0.0022 0.004 ...
##  $ Class: Factor w/ 2 levels "M","R": 2 2 2 2 2 2 2 2 2 2 ...
> table(Sonar$Class)
## 
##   M   R 
## 111  97

R表示sonar对象是岩石,M表示sonar对象是矿藏。

> p_load(Boruta)
> set.seed(111)
> feature.sec <- Boruta(Class ~ ., data = Sonar, doTrace = 1)
> feature.sec$timeTaken
## Time difference of 26.94073 secs

这个算法需要大量的计算能力,在我的笔记本电脑上需要大约27秒。

> table(feature.sec$finalDecision)
## 
## Tentative Confirmed  Rejected 
##        15        29        16

从表格中可以得出最终重要决策的计数。可以看出,我们完全能够去除大约一半特征。下面找出这些特征的名称:

> # 默认Tentative=FALSE
> fnames <- getSelectedAttributes(feature.sec)
> fnames
##  [1] "V4"  "V5"  "V9"  "V10" "V11" "V12" "V13" "V15" "V16" "V17" "V18" "V20"
## [13] "V21" "V22" "V23" "V27" "V28" "V31" "V35" "V36" "V37" "V44" "V45" "V46"
## [25] "V47" "V48" "V49" "V51" "V52"

使用这些特征创建数据集子集:

> Sonar.features <- Sonar[, fnames]
> dim(Sonar.features)
## [1] 208  29

这样,数据框Sonar.features包含了boruta算法选择的所有“确认”特征。可以使用它进行更深入、更有意义的数据探索了。

小结:
要想提高模型的预测能力,建议使用随机森林和梯度提升树。通过随机森林方法可以建立几十棵甚至几百棵树,这些树的结果会聚集成一个综合的预测。随机森林中的每棵树都是使用数据抽样建立的,抽样的方法称为自助法,预测变量也同样进行抽样。对于梯度提升方法,先创建一棵初始的、相对小规模的树,之后,基于残差或误分类会生成一系列树。这种技术的预期结果是建立一系列树,后面的树可以对前面的树的缺点加以改善,最终降低偏差和方差。
另外,在R中可以使用随机森林作为特征选择的方法。
尽管这些方法确实非常强大,但在机器学习世界中它们不是万能的。不同的数据集要求分析者根据实际情况判断应该使用什么分析技术。对于分析者来说,技术的选择和调优参数的选择同等重要,有些预测模型是好的,但有些预测模型是伟大的,这种不断调整和细化的过程决定了二者之间的所有区别。

你可能感兴趣的:(44-R语言机器学习:分类回归树)