美文网首页数据科学与R语言机器学习
44-R语言机器学习:分类回归树

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

作者: wonphen | 来源:发表于2020-03-10 22:03 被阅读0次

    《精通机器学习:基于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语言机器学习:分类回归树

        本文链接:https://www.haomeiwen.com/subject/fgmidhtx.html