基于R语言的梯度推进算法介绍
通常来说,我们可以从两个方面来提高一个预测模型的准确性:完善特征工程(feature engineering)或是直接使用Boosting算法。通过大量数据科学竞赛的试炼,我们可以发现人们更钟爱于Boosting算法,这是因为和其他方法相比,它在产生类似的结果时往往更加节约时间。
Boosting算法有很多种,比如梯度推进(Gradient Boosting)、XGBoost、AdaBoost、Gentle Boost等等。每一种算法都有自己不同的理论基础,通过对它们进行运用,算法之间细微的差别也能够被我们所察觉。如果你是一个新手,那么太好了,从现在开始,你可以用大约一周的时间来了解和学习这些知识。
在本文中,笔者将会向你介绍梯度推进算法的基本概念及其复杂性,此外,文中还分享了一个关于如何在R语言中对该算法进行实现的例子。
快问快答每当谈及Boosting算法,下列两个概念便会频繁的出现:Bagging和Boosting。那么,这两个概念是什么,它们之间究竟有什么区别呢?让我们快速简要地在这里解释一下:
Bagging:对数据进行随机抽样、建立学习算法并且通过简单平均来得到最终概率结论的一种方法。
Boosting:与Bagging类似,但在样本选择方面显得更为聪明一些——在算法进行过程中,对难以进行分类的观测值赋予了越来越大的权重。
我们知道你可能会在这方面产生疑问:什么叫做越来越大?我怎么知道我应该给一个被错分的观测值额外增加多少的权重呢?请保持冷静,我们将在接下来的章节里为你解答。
从一个简单的例子出发假设你有一个初始的预测模型M需要进行准确度的提高,你知道这个模型目前的准确度为80%(通过任何形式度量),那么接下来你应该怎么做呢?
有一个方法是,我们可以通过一组新的输入变量来构建一个全新的模型,然后对它们进行集成学习。但是,笔者在此要提出一个更简单的建议,如下所示:
Y = M(x) + error
如果我们能够观测到误差项并非白噪声,而是与我们的模型输出(Y)有着相同的相关性,那么我们为什么不通过这个误差项来对模型的准确度进行提升呢?比方说:
error = G(x) + error2
或许,你会发现模型的准确率提高到了一个更高的数字,比如84%。那么下一步让我们对error2进行回归。
error2 = H(x) + error3
然后我们将上述式子组合起来:
Y = M(x) + G(x) + H(x) + error3
这样的结果可能会让模型的准确度更进一步,超过84%。如果我们能像这样为三个学习算法找到一个最佳权重分配,
Y = alpha * M(x) + beta * G(x) + gamma * H(x) + error4
那么,我们可能就构建了一个更好的模型。
上面所述的便是Boosting算法的一个基本原则,当我初次接触到这一理论时,我的脑海中很快地冒出了这两个小问题:
1.我们如何判断回归/分类方程中的误差项是不是白噪声?如果无法判断,我们怎么能用这种算法呢?
2.如果这种算法真的这么强大,我们是不是可以做到接近100%的模型准确度?
接下来,我们将会对这些问题进行解答,但是需要明确的是,Boosting算法的目标对象通常都是一些弱算法,而这些弱算法都不具备只保留白噪声的能力;其次,Boosting有可能导致过度拟合,所以我们必须在合适的点上停止这个算法。
试着想象一个分类问题请看下图:
从最左侧的图开始看,那条垂直的线表示我们运用算法所构建的分类器,可以发现在这幅图中有3/10的观测值的分类情况是错误的。接着,我们给予那三个被误分的“+”型的观测值更高的权重,使得它们在构建分类器时的地位非常重要。这样一来,垂直线就直接移动到了接近图形右边界的位置。反复这样的过程之后,我们在通过合适的权重组合将所有的模型进行合并。
算法的理论基础我们该如何分配观测值的权重呢?
通常来说,我们从一个均匀分布假设出发,我们把它称为D1,在这里,n个观测值分别被分配了1/n的权重。
步骤1:假设一个α(t);
步骤2:得到弱分类器h(t);
步骤3:更新总体分布,
其中,
步骤4:再次运用新的总体分布去得到下一个分类器;
觉得步骤3中的数学很可怕吗?让我们来一起击破这种恐惧。首先,我们简单看一下指数里的参数,α表示一种学习率,y是实际的回应值(+1或-1),而h(x)则是分类器所预测的类别。简单来说,如果分类器预测错了,这个指数的幂就变成了1 *α, 反之则是-1*α。也就是说,如果某观测值在上一次预测中被预测错误,那么它对应的权重可能会增加。那么,接下来该做什么呢?
步骤5:不断重复步骤1-步骤4,直到无法发现任何可以改进的地方;
步骤6:对所有在上面步骤中出现过的分类器或是学习算法进行加权平均,权重如下所示:
案例练习
最近我参加了由Analytics Vidhya组织的在线hackathon活动。为了使变量变换变得容易,在complete_data中我们合并了测试集与训练集中的所有数据。我们将数据导入,并且进行抽样和分类。
library(caret)rm(list=ls())setwd("C:Usersts93856DesktopAV")library(Metrics)complete <- read.csv("complete_data.csv", stringsAsFactors = TRUE)train <- complete[complete$Train == 1,]score <- complete[complete$Train != 1,]set.seed(999)ind <- sample(2, nrow(train), replace=T, prob=c(0.60,0.40))trainData<-train[ind==1,]testData <- train[ind==2,]set.seed(999)ind1 <- sample(2, nrow(testData), replace=T, prob=c(0.50,0.50))trainData_ens1<-testData[ind1==1,]testData_ens1 <- testData[ind1==2,]table(testData_ens1$Disbursed)[2]/nrow(testData_ens1)#Response Rate of 9.052%
接下来,就是构建一个梯度推进模型(Gradient Boosting Model)所要做的:
fitControl <- trainControl(method = "repeatedcv", number = 4, repeats = 4)trainData$outcome1 <- ifelse(trainData$Disbursed == 1, "Yes","No")set.seed(33)gbmFit1 <- train(as.factor(outcome1) ~ ., data = trainData[,-26], method = "gbm", trControl = fitControl,verbose = FALSE)gbm_dev <- predict(gbmFit1, trainData,type= "prob")[,2]gbm_ITV1 <- predict(gbmFit1, trainData_ens1,type= "prob")[,2]gbm_ITV2 <- predict(gbmFit1, testData_ens1,type= "prob")[,2]auc(trainData$Disbursed,gbm_dev)auc(trainData_ens1$Disbursed,gbm_ITV1)auc(testData_ens1$Disbursed,gbm_ITV2)
在上述案例中,运行代码后所看到的所有AUC值将会非常接近0.84。我们随时欢迎你对这段代码进行进一步的完善。在这个领域,梯度推进模型(GBM)是最为广泛运用的方法,在未来的文章里,我们可能会对GXBoost等一些更加快捷的Boosting算法进行介绍。
结束语笔者曾不止一次见识过Boosting算法的迅捷与高效,在Kaggle或是其他平台的竞赛中,它的得分能力从未令人失望,当然了,也许这要取决于你能够把特征工程(feature engineering)做得多好了。
以上是小编为大家分享的关于基于R语言的梯度推进算法介绍的相关内容,更多信息可以关注环球青藤分享更多干货
R语言之决策树和随机森林总结决策树之前先总结一下特征的生成和选择,因为决策树就是一种内嵌型的特征选择过程,它的特征选择和算法是融合在一起的,不需要额外的特征选择。
一、特征生成:
特征生成是指在收集数据之时原始数据就具有的数据特征,这些数据特征由收集的数据决定(其实也就是在产品定型时设定的需要收集的数据特征),当然,在数据预处理时,也可以在此基础上构造一些新的数据特征,这些特征越多越好,表示你考虑问题比较周全,具体那些变量有用或没用,这要交给下一步特征选择来决定。
二、特征选择
特征选择是指在原有数据特征的基础上,去除重要性比较低的特征变量,过滤出有用的特征变量。这里比较困难的是搞清楚什么样的特征比较重要?这需要根据具体的问题具体分析,有些变量的选择可以很直观的看出来,但这种直觉也不一定正确。对于常用特征选择方法主要有:过滤型、包装型、内嵌型。
过滤型:是指你可以根据某个统计量的大小排序来选择特征变量,如相关系数、p值、R值等
包装型:是指在一个特征集合中选取最优的特征子集。具体需要考虑:用什么样的算法来选取?选取的最优的标准是什么?
常用的算法是分步回归包括向前搜索、向后删除、双向搜索
向前搜索:每次选取一个能使模型预测或分类效果最好的特征变量进来,进来后不退出,直到模型改善效果不再明显;
向后删除:是指每次从特征全集中每次删除一个特征变量能使模型预测或分类效果最好,退出后不进来,直到模型改善效果不再明显;
双向搜索:是指每次每次删除一个特征变量或加入一个特征变量能使模型预测或分类效果最好,退出的不进来,进来的不退出,直到模型改善效果不再明显;
这里再提一下特征变量选择的几个标准:p值、R值、AIC(越小效果越好)、BIC(越小效果越好)、熵(越小效果越好)
内嵌型:这里应该主要就是像决策树这样的情况,算法内部完成特征变量的选取。
三、决策树
决策的几个要点:1、如何决策?(也就是如何树如何分叉)------熵和信息增益---这里面包含的就是特征的选择?哪个特征变量包含的信息量大,就排在前面,至于最后树的深度就决定特征变量的个数。
当然不同的算法使用的衡量的标准不同,还有:信息增益比、基尼不纯系数
2、如何剪枝?-----一般是事后剪枝
3、连续性变量如何离散化?-----阈值的选择
熵:是指信息的混合程度(混乱程度),熵【0-1】越大表示该集合中混合的信息越多,也就表明这次的分叉效果不好还是有很多不同类的信息混在一起
信息增益:熵值的减少量,越大越好
决策树模型特点:模型易于解释;存储空间较小,以树的形式存储,决策树是一个弱分类器,不能完全分类,需要把多个弱分类器通过多数投票法组合在一起。
四、R包实现决策树
library(rpart)
library(rpart.plot)
## rpart.control对树进行一些设置
## xval是10折交叉验证
## minsplit是最小分支节点数,这里指大于等于20,那么该节点会继续分划下去,否则停止
## minbucket:叶子节点最小样本数
## maxdepth:树的深度
## cp全称为complexity parameter,指某个点的复杂度,对每一步拆分,模型的拟合优度必须提高的程度
ct <- rpart.control(xval=10, minsplit=20, cp=0.1)
## kyphosis是rpart这个包自带的数据集
## na.action:缺失数据的处理办法,默认为删除因变量缺失的观测而保留自变量缺失的观测。
## method:树的末端数据类型选择相应的变量分割方法:
## 连续性method=“anova”,离散型method=“class”,计数型method=“poisson”,生存分析型method=“exp”
## parms用来设置三个参数:先验概率、损失矩阵、分类纯度的度量方法(gini和information)
## cost是损失矩阵,在剪枝的时候,叶子节点的加权误差与父节点的误差进行比较,考虑损失矩阵的时候,从将“减少-误差”调整为“减少-损失”
data("Kyphosis")
fit <- rpart(Kyphosis~Age + Number + Start,data=kyphosis, method="class",control=ct,parms = list(prior = c(0.65,0.35), split = "information"))
## 作图有2种方法
## 第一种:
par(mfrow=c(1,3))plot(fit)text(fit,use.n=T,all=T,cex=0.9)
## 第二种,这种会更漂亮一些:
rpart.plot(fit, branch=1, branch.type=2, type=1, extra=102,
shadow.col="gray", box.col="green",
border.col="blue", split.col="red",
split.cex=1.2, main="Kyphosis决策树")
## rpart包提供了复杂度损失修剪的修剪方法,printcp会告诉分裂到每一层,cp是多少,平均相对误差是多少
## 交叉验证的估计误差(“xerror”列),以及标准误差(“xstd”列),平均相对误差=xerror±xstd
printcp(fit)
## 通过上面的分析来确定cp的值
##调用CP(complexity parameter)与xerror的相关图,一种方法是寻找最小xerror点所对应
#的CP值,并由此CP值决定树的大小,另一种方法是利用1SE方法,寻找xerror+SE的最小点对应的CP值。
plotcp(fit)
##利用以下方法进行修剪:
## prune(fit, cp= fit$cptable[which.min(fit$cptable[,"xerror"]),"CP"])
fit2 <- prune(fit, cp=0.01)
#利用模型预测
ndata=data.frame(...)
predict(fit,newdata=ndata)
#案例
str(iris)
set.seed(1234)#设置随机数种子--使每次运行时产生的一组随机数相同,便于结果的重现
#抽样:从iris数据集中随机抽70%定义为训练数据集,30%为测试数据集(常用)
#这里是对行抽样,ind是一个只含1和2的向量
ind <- sample(2, nrow(iris), replace=TRUE, prob=c(0.7, 0.3))
trainData <- iris[ind==1,]
testData <- iris[ind==2,]
f<-Species ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width
#训练数据
fit<-rpart(f,trainData)
#预测
re<-predict(fit,testData)
#******************或者用其他包********************
library(party)
#建立决策树模型预测花的种类
myFormula <- Species ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width
iris_ctree <- ctree(myFormula, data=trainData)
# 查看预测的结果
z<-table(predict(iris_ctree), trainData$Species)
#可以根据以上列联表求出预测的正确率---评估模型
#计算准确度
q<-sum(diag(z))/sum(z)
五、机器集成与随机森林法则
前面说过,决策树的一个特点是:弱分类器,分类不完全,需要利用集成投票的方式来增加精确度和稳健性。
机器集成算法:对于数据集训练多个模型,对于分类问题,可以采用投票的方法,选择票数最多的类别作为最终的类别,而对于回归问题,可以采用取均值的方法,取得的均值作为最终的结果。主要的集成算法有bagging和adaboost算法。
随机森林:随机森林就是利用机器集成多个决策树,主要有两个参数,一个是决策树的个数,一个是每棵树的特征变量个数。
随机森林特点:精确度高、稳健性好,但可解释性差。(可以知道各个变量的重要性)
R包实现机器集成算法:
#adabag包均有函数实现bagging和adaboost的分类建模
#利用全部数据建模
library(adabag)
a<-boosting(Species~.,data=iris)
z0<-table(iris[,5],predict(a,iris)$class)
#计算误差率
E0<-(sum(z0)-sum(diag(z0)))/sum(z0)
barplot(a$importance)
b<-errorevol(a,iris)#计算全体的误差演变
plot(b$error,type="l",main="AdaBoost error vs number of trees") #对误差演变进行画图
a<-bagging(Species~.,data=iris)
z0<-table(iris[,5],predict(a,iris)$class)
#计算误差率
E0<-(sum(z0)-sum(diag(z0)))/sum(z0)
barplot(a$importance)
b<-errorevol(a,iris)#计算全体的误差演变
plot(b$error,type="l",main="AdaBoost error vs number of trees") #对误差演变进行画图
#5折交叉验证
set.seed(1044) #设定随机种子
samp=c(sample(1:50,25),sample(51:100,25),sample(101:150,25)) #进行随机抽样
a=boosting(Species~.,data=iris[samp,]) #利用训练集建立adaboost分类模
z0<-table(iris[samp,5],predict(a,iris[samp,])$class)#训练集结果
z1<-table(iris[-samp,5],predict(a,iris[-samp,])$class)#测试集结果
E0<-(sum(z0)-sum(diag(z0)))/sum(z0)
E1<-(sum(z0)-sum(diag(z0)))/sum(z1)
a=bagging(Species~.,data=iris[samp,]) #利用训练集建立adaboost分类模
z0<-table(iris[samp,5],predict(a,iris[samp,])$class)#训练集结果
z1<-table(iris[-samp,5],predict(a,iris[-samp,])$class)#测试集结果
E0<-(sum(z0)-sum(diag(z0)))/sum(z0)
E1<-(sum(z0)-sum(diag(z0)))/sum(z1)
R包实现随机森林:
#随机森林法则
library(randomForest)
library(foreign)
data("iris")
#抽样数据
ind<-sample(2,nrow(iris),replace = TRUE,prob=c(0.7,0.3))
traning<-iris[ind==1,]
testing<-iris[ind==2,]
#训练数据
rf <- randomForest(Species ~ ., data=traning, ntree=100, proximity=TRUE)
#预测
table(predict(rf),traning$Species)
table(predict(rf,testing),testing$Species)
#查看预测的效果
print(rf)
plot(rf)
#查看重要性
importance(rf)
varImpPlot(rf)