965ade52dc0bea480da4d38c26813374.png

今天我们来学习机器学习中的一个重量级的武器,集成学习算法。基本上集成学习法是一个多分类器整合方法,传统的机器学习模型只会建立一个分类器,集成学习法通过构建多个分类器,对多个分类器的预测结果进行聚合,提高分类精度。集成方法根据训练数据构造一组基分类器,并通过对每个基分类器的预测进行投票来进行分类。

未经许可请勿转载

更多数据分析内容参看这里

一、基本概念

下面我们看一下集成学习算法的过程。

5e044d8091c01d95ccc1205af97a8f58.png

原始的训练数据,通过一些取样方法,会被分成多个不同的数据集,每个数据集生成各自的分类器,最后会把t个分类器的结果进行整合,基本上采用的是投票机制。集成学习法后面隐含的逻辑就是三个臭皮匠胜过诸葛亮。

集成学习法在分类上有两种方式。第一类是在训练数据上进行处理,建立多个模型。还有一大类是从输入字段上进行处理,产生多个模型。

基于训练数据的处理,主要通过不同的取样方式来决定哪些样本会被选作训练集,每一个训练集都会产生一个特定学习算法的分类器。这种方式主要由两种集成学习算法:袋装法Bagging 和提升法Boosting。

基于输入字段的处理,输入字段的一个子集会被挑选出来,构建单独的训练集。这种方式中一个主要方法是随机森林(Random forest)。 随机森林是决策树分类算法的改良,会建立一系列的决策树,通过投票决定结果。传统的决策树通过挑选最好的属性来进行分类,而随机森林会从最好的K个属性里面随机选择1个来进行分类,这时就会产生很多不同的决策树。这种方式在有冗余变量时效果尤佳。

二、集成学习模型构建

1. 袋装法(Bagging)原理

我们先看一下下面的数据集(x是输入字段,y是目标字段)

b2c5ac30f18538dab9c0c87af7ea60dc.png

假定我们构建一个分类器,并且限制为单层的二元树,怎么样才能得到最佳的分类的一层二元树?这边有两个选择(1)将X<=0.35的预测为1,X>0.35的预测为-1.(2)X<=0.75,预测为-1,X>0.75,预测为1。这两种方式,预测的准确率最多均为70%。

下面我们来看一下,使用袋装法怎么提升预测的准确率。我们采用有放回式的抽样,抽取十笔样本。在每一次抽样中,我们都构建一个二元分类树,如下所示。

7c0faa75d8a0a25c84d40fe04d3831de.png

581a31abe7b9bc08a5bf6355087680dc.png

这时候神器的事情发生了,我们再次把原始的0.1到1这是个数据放入上面构建的10个二元树分类器,结果如下:

476ec0c1aec459980844d7bb80b3b1dc.png

结果是,预测的准确率达到了100%,超越了单层分类树70%的限制。从这个例子可以看出,即便每个分类器是一个基本的二元树,把所有的分类器结果整合后,就形成了一个二层的决策树的效果。基本上Bagging的方法可以套用任何分类模型,这边使用了决策树,比较常见的方式。

由于每个样本都有相同的概率被抽取,bagging方式不需要对特定的训练集进行权重处理。也就是说,这种方式不会存在过拟合问题。

2. 提升法(Boosting)原理

提升法也是对训练数据进行处理,和Bagging不同,它会根据实际情况去调整每个训练数据被抽中的概率,开始大家被抽取的概率是一样的,但是一直被分类错误的数据会被加重权重,在后续的基础分类器中这些一直分错的数据会被更多的抽中,目标是希望比较难分类的数据再下一次的分类中能够分类正确。也就是说Boosting会给每一个训练范例赋予一个权重,每一轮的分类器都可能改变权重值。

初始,每个训练数据的权重都是1/N, 他们都会以相同的概率被抽取。根据1/N的权重,我们第一次会抽取样本建立一个训练集,并在此基础上构建一个分类器。建完分类器后,会对原始的训练数据去进行分类,并判断分类的正确率。分类正确的降低该训练数据的权重,错误的进行提升。然后开始下一轮的循环,这种方式将注意力放在难以分类的数据上。

337a3ed53b1353a6b64e843294918247.png

最后的结果是整合每一轮中所有的分类器,进行投票处理。这边对每一轮的分类器将会进行二次赋予权重处理,越后面的分类器权重越高。

现在的问题就是:(1)怎么在每一轮预测结束后去调整权重?(2)怎么整合每一各分类器预测的结果? 我们来看下面的例子。

817e560ef38c5178e76b0687bf7dc127.png

第一轮,x<=0.75预测为-1,x>0.75预测为1。0.1到0.3三个数据预测错误,因此提升其权重值(权重值的计算公式比较复杂,此处不再展开)。剩下7个数值预测正确,降低其权重。

第二轮,X<=0.05预测为-1,x>0.05预测为1。0.4到0.7四个数据预测错误,因此提升其权重值 ,降低其余数值的权重。

第三轮,x<=0.3预测为1,x>0.3预测 为-1。

可以看到,一直分类正确的数值,权重在不断下降。假设我们的分类只进行三轮。

1395f4532dde8a5905c679b3bfb3766b.png

下面我们看一三轮预测的结果,每一轮分类器都赋予了权重(权重值的计算同样在此省略)

06619d5656d415539f62c2eb9889baa5.png

接下来我们再次把原始数据,代入到这三个分类器中分类处理

29ddd06fb8d4df6c74c531a1f6e5608c.png

最后我们来计算最终的分类结果,比如第一个数值0.1,Sum1 = -1.738 + 2.7784 + 4.1195 = 5.16, 因此预测其为1。其余数值相同处理。

使用Boosting算法,AdaBoost(R语言中提供相关的包)后,训练集中的每一个数据都被预测准确了。由于Boosting算法侧重于容易分错的数据,因此它很容易陷入过拟合的问题。一般来来说,Boosting算法的分类准确率高于Bagging。

目前比较新的提优集成学习算法是XGBoost, 全称为Extreme Gradient Boosting,其最大的特点在于能够自动利用CPU的多线程进行并行,大幅度提高模型运算效率。

3. 随机森林(Random forest)原理

随机森林主要是为决策树而设计的, 因为决策树只能建一棵,随机森林可以建多棵,如下所示。决策树并不从训练数据出发,而是从字段选择上进行变更,随机从K个最佳字段中选择一个。

71ad613646469abcbe76921b294cb32f.png

目前基于实际的分类效果来看,boosting算法和随机森林的效果是最好的,因此在一些比赛中基本也就应用到了这两种。通常来说,我们会构建50个基础分类器以上。

三、R语言集成学习模型构建

1. 袋装法(Bagging)

要处理的是一个糖尿病的数据集,class是目标字段

b97b323513b4f95114baca0c4057c252.png

# Read Diabetes Dataset

data <- read.csv("E:/cdadata/jicdata/diabetes.csv")[,-1]

set.seed(102)

select <- sample(1:nrow(data),nrow(data)*0.8) #抽取80%的数据作为训练集

#先用c5.0决策树来分类

# Build C5.0 Model

library(C50)

C50.tree <- C5.0(class ~ ., data=data[select,])

# Make Predictions for Training Data

C50.Prediction <- predict(C50.tree, newdata=data[select,], type='class')

results <- table(Prediction=C50.Prediction, Actual=data[select,]$class)

results

Correct_Rate <- sum(diag(results)) / sum(results)

Correct_Rate

e7c6b3da5012038ea0c7adddb419ae75.png

# Make Predictions for Test Data

C50.Prediction <- predict(C50.tree, newdata=data[-select,], type='class')

results <- table(Prediction=C50.Prediction, Actual=data[-select,]$class)

results

Correct_Rate <- sum(diag(results)) / sum(results)

Correct_Rate

8a6c3b5ed5e850b2cf75977a7691d3b2.png

# Build Bagging Model

library(adabag)

set.seed(101)

Bagging_Model <- bagging(class ~ ., data=data[select,], mfinal = 100)

# Make Predictions for Training Data

Bagging.Prediction <- predict(Bagging_Model, newdata=data[select,])

results <- data.frame(Bagging.Prediction$class, Bagging.Prediction$prob)

names(results) <- c("Prediction", "Negative", "Positive")

results

14ba6a03c4f098aab90a5633047b417e.png

#bagging也可以看一下哪个字段相对更重要

barplot(Bagging_Model$importance)

e4d7d35ee42916abb44083399027b6b8.png

#看一下预测结果

Bagging.Prediction$confusion

48747dd193f9c2b61a3e8f6f59b5431f.png

#也可以自己生成混淆矩阵

results <- table(Prediction=Bagging.Prediction$class, Actual=data[select,]$class)

results

Correct_Rate <- sum(diag(results)) / sum(results)

Correct_Rate

ea7f84e657f5643169dc7443a4e15195.png

# Make Predictions for Test Data

Bagging.Prediction <- predict(Bagging_Model, newdata=data[-select,])

Bagging.Prediction$confusion

results <- table(Prediction=Bagging.Prediction$class, Actual=data[-select,]$class)

results

Correct_Rate <- sum(diag(results)) / sum(results)

Correct_Rate #正确率略有提升

8c2fca60da659e350d37b48b064708a2.png

2. 提升法(Boosting)—adaboost

处理的数据同上,这边说明的是,无论是bagging还是boosting,adabag包背后使用的都是决策树算法cart。

# Build Boosting Model

library(adabag)

set.seed(101)

Boosting_Model <- boosting(class ~ ., data=data[select,])

# Make Predictions for Training Data

Boosting.Prediction <- predict(Boosting_Model, newdata=data[select,])

results <- data.frame(Boosting.Prediction$class, Boosting.Prediction$prob)

names(results) <- c("Prediction", "Negative", "Positive")

results

barplot(Boosting_Model$importance)

42a998cd1038d142e452aee47046078b.png

Boosting_Model.Error <- errorevol(Boosting_Model, data[select,]) #计算全体的误差演变

plot(Boosting_Model.Error$error,type="l",

main="AdaBoost Error vs Number of Trees") #对误差演变进行画图,从中可以看出虽#然建立了100个分类器,但到第20个开始误差已经降不下来了。

0160837cdcc5fa649f5efde9d146aa23.png

Boosting.Prediction$confusion

results <- table(Prediction=Boosting.Prediction$class, Actual=data[select,]$class)

results

Correct_Rate <- sum(diag(results)) / sum(results)

Correct_Rate

7a1be5bac639ec64b26a4f9d66144c1d.png

# Make Predictions for Test Data

Boosting.Prediction <- predict(Boosting_Model, newdata=data[-select,])

Boosting.Prediction$confusion

results <- table(Prediction=Boosting.Prediction$class, Actual=data[-select,]$class)

results

Correct_Rate <- sum(diag(results)) / sum(results)

Correct_Rate

c287880a01db718fd66a614df4d3f555.png

3. 提升法(Boosting)—XGBoost

# Read Broadband Dataset

data <- read.csv("E:/jicdata/broadband.csv")[,-1]

# Data Preprocessing

data.y <- data$BROADBAND

data.n <- data[,-c(5,ncol(data))]

data.c <- data[,c("CHANNEL")]

data.c <- as.factor(data.c)

library("nnet")

dummy.c = as.data.frame(class.ind(data.c))

names(dummy.c) <- c("Channel-1", "Channel-2", "Channel-3", "Channel-4")

# Generate Training & Test Datasets

set.seed(102)

select <- sample(1:nrow(data),nrow(data)*0.8)

train_set.x <- cbind(data.n[select,], dummy.c[select,])

train_set.y <- data.y[select]

test_set.x <- cbind(data.n[-select,], dummy.c[-select,])

test_set.y <- data.y[-select]

# Build xgboosting Model

library(xgboost)

dtrain <- xgb.DMatrix(data = as.matrix(train_set.x), label = as.matrix(train_set.y))

dtest <- xgb.DMatrix(data = as.matrix(test_set.x))

xgb.params = list(

#col的抽样比例,越高表示每棵树使用的col越多,会增加每棵小树的复杂度

colsample_bytree = 0.5,

# row的抽样比例,越高表示每棵树使用的col越多,会增加每棵小树的复杂度

subsample = 0.5,

booster = "gbtree",

# 树的最大深度,越高表示模型可以长得越深,模型复杂度越高

max_depth = 2,

# boosting会增加被分错的数据权重,而此参数是让权重不会增加的那么快,因此越大会让模型愈保守

eta = 0.03,

# 或用'mae'也可以

eval_metric = "rmse",

objective = "reg:linear",

# 越大,模型会越保守,相对的模型复杂度比较低

gamma = 0)

cv.model = xgb.cv(

params = xgb.params,

data = dtrain,

nfold = 5, # 5-fold cv

nrounds=200, # 测试1-100,各个树总数下的模型

# 如果当nrounds < 30 时,就已经有overfitting情况发生,那表示不用继续tune下去了,可以提早停止

early_stopping_rounds = 30,

print_every_n = 20 # 每20个单位才显示一次结果,

)

tmp <- cv.model$evaluation_log

plot(x=1:nrow(tmp), y= tmp$train_rmse_mean, col='red', xlab="nround", ylab="rmse", main="Avg.Performance in CV")

points(x=1:nrow(tmp), y= tmp$test_rmse_mean, col='blue')

legend("topright", pch=1, col = c("red", "blue"),

legend = c("Train", "Validation") )

best.nrounds = cv.model$best_iteration

best.nrounds

xgb.Model <- xgb.train(paras = xgb.params, data = dtrain, nrounds = best.nrounds)

# Make Predictions for Training Data

xgb.Prediction <- predict(xgb.Model, dtrain)

xgb.Prediction <- ifelse(xgb.Prediction > 0.01, 1, 0)

accuracy.xgb <- sum(xgb.Prediction==train_set.y)/length(xgb.Prediction)

accuracy.xgb

table(xgb.Prediction, train_set.y)

# Make Predictions for Test Data

xgb.Prediction <- predict(xgb.Model, dtest)

xgb.Prediction <- ifelse(xgb.Prediction > 0.01, 1, 0)

accuracy.xgb <- sum(xgb.Prediction==test_set.y)/length(xgb.Prediction)

accuracy.xgb

table(xgb.Prediction, test_set.y)

4. 随机森林(Random forest)

# Read Diabetes Dataset

data <- read.csv("E:/jicdata/diabetes.csv")[,-1]

set.seed(102)

select <- sample(1:nrow(data),nrow(data)*0.8)

# Build Random Forest Model

library(randomForest)

set.seed(101)

RF_Model <- randomForest(class ~ ., data=data[select,], ntree=300)

# Make Predictions for Training Data

RF.Prediction <- predict(RF_Model, data[select,])

accuracy.rf <- sum(RF.Prediction==data[select,]$class)/length(RF.Prediction)

accuracy.rf

table(RF.Prediction, data[select,]$class)

# Make Predictions for Test Data

RF.Prediction <- predict(RF_Model, data[-select,])

accuracy.rf <- sum(RF.Prediction==data[-select,]$class)/length(RF.Prediction)

accuracy.rf

table(RF.Prediction, data[-select,]$class)

87ed20eb314c961672ef9002214d4678.png

022e79e5f413e74330ed805191efef0d.png
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐