《精通机器学习:基于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中可以使用随机森林作为特征选择的方法。
尽管这些方法确实非常强大,但在机器学习世界中它们不是万能的。不同的数据集要求分析者根据实际情况判断应该使用什么分析技术。对于分析者来说,技术的选择和调优参数的选择同等重要,有些预测模型是好的,但有些预测模型是伟大的,这种不断调整和细化的过程决定了二者之间的所有区别。
网友评论