《精通機器學習:基于R 第二版》學習筆記
1、集成模型簡介
集成學習的定義是:“有策略地建立多個模型(如分類器或?qū)<蚁到y(tǒng))并將其組合在一起,解決特定計算智能問題的過程?!痹陔S機森林和梯度提升模型中,我們將幾百或幾千棵樹的“投票”結(jié)果組合起來進行預測。于是,根據(jù)集成學習的定義,這些模型就是集成學習模型。
在機器學習中,這種方法的優(yōu)點是可以將幾種性能平平甚至很差的學習器的預測結(jié)果結(jié)合起來,從而提高整體準確率。
2、數(shù)據(jù)準備
> # 使用皮瑪印第安糖尿病數(shù)據(jù)集
> library(pacman)
> p_load(MASS, caret, caretEnsemble, caTools)
>
> pima <- rbind(Pima.tr, Pima.te)
> set.seed(123)
>
> # 拆分為訓練集和測試集
> split <- createDataPartition(y = pima$type, p = 0.75, list = F)
> train <- pima[split, ]
> test <- pima[-split, ]
> table(train$type)
##
## No Yes
## 267 133
3、模型評價與模型選擇
訓練集中Yes和No的比例大概為2:1。在很多數(shù)據(jù)集中,我們感興趣的結(jié)果是一種罕見的事件。于是你就會得到這樣一種分類器,它的正確率非常高,但預測我們感興趣的結(jié)果時效果非常差。也就是說,根本預測不出真陽性的結(jié)果。為了平衡響應變量,可以對少數(shù)類進行上采樣,對多數(shù)類進行下采樣,建立“人工合成”的數(shù)據(jù)。
在上采樣過程中,對于交叉驗證使用的每折數(shù)據(jù),少數(shù)類都使用有放回的隨機抽樣,以使其數(shù)量與多數(shù)類的觀測數(shù)量相匹配。
> control <- trainControl(method = "cv",
+ # 5折交叉驗證
+ number = 5,
+ # 保存最后預測概率
+ savePredictions = "final",
+ classProbs = T,
+ # 按照索引再抽樣,使基礎模型使用同樣的數(shù)據(jù)折
+ index = createResample(train$type,5),
+ # 上采樣
+ sampling = "up",
+ summaryFunction = twoClassSummary)
訓練模型,可以使用任意caret包支持的模型。我們訓練3種:分類樹模型:"rpart"、多元自適應回歸樣條模型:"earth"和K最近鄰模型:"knn"。
> set.seed(123)
> # caretList() 函數(shù)不但能夠建立模型,還可以按照caret包的規(guī)則調(diào)整每種模型的超參數(shù)
> # 使用caretModelSpec()函數(shù)可以為每種模型創(chuàng)建各自的調(diào)優(yōu)參數(shù)網(wǎng)格
> models <- caretList(type ~ ., data = train, trControl = control, metric = "ROC",
+ methodList = c("rpart", "earth", "knn"))
> models
## $rpart
## CART
##
## 400 samples
## 7 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 400, 400, 400, 400, 400
## Addtional sampling using up-sampling
##
## Resampling results across tuning parameters:
##
## cp ROC Sens Spec
## 0.02631579 0.7317882 0.7083862 0.7436684
## 0.03383459 0.7549152 0.7268892 0.7400487
## 0.28571429 0.6766871 0.7609114 0.5924628
##
## ROC was used to select the optimal model using the largest value.
## The final value used for the model was cp = 0.03383459.
##
## $earth
## Multivariate Adaptive Regression Spline
##
## 400 samples
## 7 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 400, 400, 400, 400, 400
## Addtional sampling using up-sampling
##
## Resampling results across tuning parameters:
##
## nprune ROC Sens Spec
## 2 0.7729508 0.6911083 0.7151514
## 8 0.7685541 0.7188456 0.6668305
## 14 0.7486742 0.7106624 0.6515910
##
## Tuning parameter 'degree' was held constant at a value of 1
## ROC was used to select the optimal model using the largest value.
## The final values used for the model were nprune = 2 and degree = 1.
##
## $knn
## k-Nearest Neighbors
##
## 400 samples
## 7 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 400, 400, 400, 400, 400
## Addtional sampling using up-sampling
##
## Resampling results across tuning parameters:
##
## k ROC Sens Spec
## 5 0.7120835 0.6554364 0.6975610
## 7 0.7409795 0.6941265 0.6695143
## 9 0.7590350 0.7110115 0.6845189
##
## ROC was used to select the optimal model using the largest value.
## The final value used for the model was k = 9.
##
## attr(,"class")
## [1] "caretList"
基礎模型能夠有效組合的要求是,它們不高度相關。模型預測結(jié)果是否相關沒有嚴格的規(guī)則,我們應該用結(jié)果進行實驗,查看結(jié)果:
> modelCor(resamples(models))
## rpart earth knn
## rpart 1.0000000 0.6360436 0.8140344
## earth 0.6360436 1.0000000 0.3596560
## knn 0.8140344 0.3596560 1.0000000
rpart與knn高度相關。我們先忽略它,繼續(xù)建立第四個分類器——融合模型——并檢查結(jié)果:
> model.preds <- lapply(models, predict, newdata = test, type = "prob") %>%
+ lapply(function(x) x[, "Yes"]) %>% as.data.frame()
使用 caretStack() 函數(shù)將這些模型融合在一起,進行最終預測。
> stack <- caretStack(models, method = "glm", metric = "ROC",
+ trControl = trainControl(method = "boot",
+ number = 5, savePredictions = "final", classProbs = T,
+ summaryFunction = twoClassSummary))
> summary(stack)
##
## Call:
## NULL
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.7594 -0.7203 -0.4605 0.8363 2.3597
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.5601 0.1998 7.809 5.76e-15 ***
## rpart -1.5056 0.3690 -4.081 4.49e-05 ***
## earth -1.5263 0.5211 -2.929 0.0034 **
## knn -1.3850 0.3438 -4.029 5.61e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 954.74 on 744 degrees of freedom
## Residual deviance: 756.85 on 741 degrees of freedom
## AIC: 764.85
##
## Number of Fisher Scoring iterations: 4
即使rpart模型和knn模型高度相關,它們的系數(shù)也依然是顯著的,所以應該可以在分析中保留這兩個模型。下面比較單獨模型的結(jié)果和集成模型的結(jié)果:
> ensemble.prob <- 1 - predict(stack, newdata = test, type = "prob")
> model.preds <- model.preds %>% mutate(ensemble = ensemble.prob)
> colAUC(model.preds, test$type)
## rpart earth knn ensemble
## No vs. Yes 0.8512397 0.8358729 0.8667355 0.8743543
通過 colAUC() 函數(shù)可以看到單獨模型的AUC和集成模型的AUC,通過模型融合建立的集成模型確實可以提高預測能力。
4、多類分類
4.1 數(shù)據(jù)理解與數(shù)據(jù)準備
> p_load(mlr, ggplot2, HDclassif, DMwR, reshape2, corrplot)
> data(wine)
> table(wine$class)
##
## 1 2 3
## 59 71 48
響應變量是數(shù)值型的標簽(1、2和3)。
使用合成少數(shù)類過采樣技術,使數(shù)據(jù)量倍增:
> # 將類別轉(zhuǎn)換為因子變量,否則函數(shù)不起作用
> wine$class <- as.factor(wine$class)
>
> set.seed(123)
>
> # SMOTE() 函數(shù)中,默認的最近鄰數(shù)量為5
> # 如果想創(chuàng)建數(shù)量是現(xiàn)有數(shù)量2倍的少數(shù)類,就設定 'percent.over = 100'
> df <- DMwR::SMOTE(class ~ ., wine, perc.over = 300, perc.under = 300)
> table(df$class)
##
## 1 2 3
## 187 245 192
現(xiàn)在數(shù)據(jù)集的觀測變?yōu)榱?24個。然后對各特征進行可視化:
> # 先標準化
> wine.scale <- scale(wine[, 2:5]) %>% as.data.frame() %>% mutate(class = wine$class)
>
> wine.scale %>% melt(id.var = "class") %>% ggplot(aes(class, value)) +
+ geom_boxplot(outlier.colour = "red") + facet_wrap(~variable, ncol = 2) +
+ theme_bw()

離群點處理:對于其中的高異常值(高于第99個百分位),將其設為第75個百分位的值;對于其中的低異常值(低于第1個百分位),將其設為第25個百分位的值。
> out <- function(x) {
+ x[x > quantile(x, 0.99)] <- quantile(x, 0.75)
+ x[x < quantile(x, 0.01)] <- quantile(x, 0.25)
+ return(x)
+ }
創(chuàng)建新數(shù)據(jù)框:
> wine.trunc <- lapply(wine[, -1], out) %>% as.data.frame() %>% cbind(class = wine$class)
>
> # 選1個特征看看
> wine.trunc %>% melt(id.var = "class") %>% filter(variable == "V3") %>%
+ ggplot(aes(class, value)) +
+ geom_boxplot(outlier.colour = "red") + theme_bw()

可以看到,效果非常好。再看看變量之間的相關性:
> wine.trunc[, -14] %>% cor(.) %>% corrplot.mixed(upper = "ellipse")

V6和V7高度相關,一般來說,在基于非線性的學習方法中,這不是一個問題。但我們還是加入一個L2懲罰項(嶺回歸)來解決它。
4.2 模型評價與模型選擇
> set.seed(123)
> split <- createDataPartition(y = df$class, p = 0.7, list = F)
> train.rf <- df[split, ]
> test.rf <- df[-split, ]
>
> # mlr包要求將訓練集數(shù)據(jù)保存在task數(shù)據(jù)結(jié)構中,該數(shù)據(jù)結(jié)構專門為分類任務而設計
> wine.task <- makeClassifTask(id = "wine", data = train.rf, target = "class")
4.2.1 隨機森林
> # 創(chuàng)建一個重抽樣對象來為隨機森林模型調(diào)整樹的數(shù)量,它包括3個子樣本
> rdesc <- makeResampleDesc("Subsample", iters = 3)
>
> # 建立一個樹的網(wǎng)格來調(diào)整樹的數(shù)量,最小值為750,最大值為2000
> # 也可以使用caret包建立多個參數(shù)列表
> param <- makeParamSet(makeDiscreteParam("ntree",
+ values = c(750, 1000, 1250, 1500, 1750, 2000)))
>
> # 創(chuàng)建控制對象,建立數(shù)值網(wǎng)格 這樣即可調(diào)整超參數(shù),從而找出最優(yōu)樹數(shù)量
> ctrl <- makeTuneControlGrid()
>
> # 調(diào)出最優(yōu)樹數(shù)量和相應的樣本外誤差
> tuning <- tuneParams("classif.randomForest", task = wine.task, resampling = rdesc,
+ par.set = param, control = ctrl)
> tuning$x
## $ntree
## [1] 1250
> tuning$y
## mmce.test.mean
## 0
最優(yōu)的樹數(shù)量是1250,相應的平均誤分類率是0,一個完美的分類。
使用超參數(shù)作為makelearner函數(shù)的訓練包裝器
> rf <- setHyperPars(makeLearner("classif.randomForest", predict.type = "prob", par.vals = tuning$x))
>
> # 訓練模型
> fit.rf <- train(rf, wine.task)
>
> # 查看訓練集上的混淆矩陣
> fit.rf$learner.model
##
## Call:
## randomForest(formula = f, data = data, classwt = classwt, cutoff = cutoff, ntree = 1250)
## Type of random forest: classification
## Number of trees: 1250
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: 0.23%
## Confusion matrix:
## 1 2 3 class.error
## 1 130 1 0 0.007633588
## 2 0 172 0 0.000000000
## 3 0 0 135 0.000000000
看看在測試集上的表現(xiàn):
> pred.rf <- predict(fit.rf, newdata = test.rf)
> getConfMatrix(pred.rf)
## predicted
## true 1 2 3 -err.-
## 1 56 0 0 0
## 2 0 73 0 0
## 3 0 0 57 0
## -err.- 0 0 0 0
> performance(pred.rf, measures = list(mmce, acc))
## mmce acc
## 0 1
測試集上沒有一個錯誤。
4.2.2 嶺回歸
> p_load(penalized, RWeka, mda)
> ovr <- makeMulticlassWrapper("classif.penalized", mcw.method = "onevsrest")
>
> # 創(chuàng)建一個包裝器用裝袋法進行10次(默認值)有放回重抽樣,抽取70%的觀測和所有輸入特征
> bag.ovr <- makeBaggingWrapper(ovr, bw.iters = 10,
+ bw.replace = T, bw.size = 0.7, bw.feats = 1)
>
> # 訓練算法
> set.seed(123)
> fit.ovr <- mlr::train(bag.ovr, wine.task)
> pred.ovr <- predict(fit.ovr, newdata = test.rf)
> getConfMatrix(pred.ovr)
## predicted
## true 1 2 3 -err.-
## 1 56 0 0 0
## 2 1 72 0 1
## 3 0 0 57 0
## -err.- 1 0 0 1
錯了1個,但正確率并不重要,應該更注重創(chuàng)建分類器、調(diào)整超參數(shù)和實現(xiàn)重抽樣的策略和方法。
5、MLR集成模型
> # 使用pima數(shù)據(jù)集創(chuàng)建task對象
> pima.task <- makeClassifTask(id = "pima", data = train, target = "type")
>
> # 增加數(shù)據(jù)觀測,先前只有400行
> pima.smote <- smote(pima.task, rate = 2, nn = 3)
> str(getTaskData(pima.smote))
## 'data.frame': 533 obs. of 8 variables:
## $ npreg: num 5 5 0 3 3 2 0 1 12 1 ...
## $ glu : num 86 77 107 83 142 128 137 189 92 86 ...
## $ bp : num 68 82 60 58 80 78 40 60 62 66 ...
## $ skin : num 28 41 25 31 15 37 35 23 7 52 ...
## $ bmi : num 30.2 35.8 26.4 34.3 32.4 43.3 43.1 30.1 27.6 41.3 ...
## $ ped : num 0.364 0.156 0.133 0.336 0.2 ...
## $ age : num 24 35 23 25 63 31 33 59 44 29 ...
## $ type : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 2 2 2 2 1 ...
現(xiàn)在訓練集中有533個觀測。建立3個基礎模型:隨機森林、二次判別分析和帶有L1懲罰項的GLM:
> base <- c("classif.randomForest", "classif.qda", "classif.glmnet")
# 融合模型是一個簡單的GLM模型,它的系數(shù)是通過交叉驗證調(diào)整得來的
> learns <- lapply(base, makeLearner) %>%
+ lapply(setPredictType, "prob")
>
> s1 <- makeStackedLearner(base.learners = learns, super.learner = "classif.logreg",
+ predict.type = "prob", method = "stack.cv")
看看在測試集上的表現(xiàn):
> fit.s1 <- mlr::train(s1, pima.smote)
> pred.fit <- predict(fit.s1, newdata = test)
> getConfMatrix(pred.fit)
## predicted
## true No Yes -err.-
## No 77 11 11
## Yes 14 30 14
## -err.- 14 11 25
> performance(pred.fit, measures = list(mmce, acc, auc))
## mmce acc auc
## 0.1893939 0.8106061 0.8719008
正確率為81%,AUC與用caretEnsemble包建立的集成模型相比差不多。
總結(jié):
1.融合模型與單獨的基礎模型相比,確實在性能上有所提高;
2.兩種平衡類別的抽樣技術:上采樣與合成少數(shù)類過采樣技術。