MIT:The Analytics Edge 笔记04-决策树和随机森林

MIT课程 15.071x The Analytics Edge 第四单元的学习记录。


Trees

第四单元的主题是决策树和随机森林。

1.理论

CART(classification and regression trees)

决策树

自变量是决策树上的节点(splits)。但是注意,不是每个自变量都有一个节点;也就是说,有的自变量有多个节点(随着取值的不同,导致因变量的结果也不同),有的自变量没有节点(对因变量影响很小)。
因变量是决策树上的叶子/终端(leaves/nodes)。此图上的因变量的取值是0或者1。
在各个节点,根据各个自变量的取值,最终到达叶子节点,也就得到了因变量的取值。
注意,决策树的左边,节点的判断语句总是为True/Yes,右边节点的判断语句总是为False/No。
tree
最左的分支表示,如果 LowerCou=lbr 且 Responde=CRI 且 Petition=CIT,那么因变量的取值为0。
最右的分支表示,如果 LowerCou!=lbr 且 Responde=STA,那么因变量的取值为1。

决策树的大小

minbucket可以理解为,决策树被节点分割后,每个bucket数据的数量。
minbucket越大,分组越少,split越少。
minbucket越小,分组越多,split越多。

Classification tree 和 Regression tree
  • Classification tree analysis is when the predicted outcome is the class to which the data belongs.(简单的讲,预测值是0和1,比如支持还是反对)
  • Regression tree analysis is when the predicted outcome can be considered a real number.(简单的讲,预测值是可变的,比如房价等等)

体现在代码中的话,
如果指定了type = “class”,那么是 Classification tree。
如果没有指定type = “class”,那么是 Regression tree。

Random Forest

随机森林,被设计出来用于提高CART的精度。
和字面意思类似,如果决策树只有一棵树,那么随机森林会创建多个决策树,然后找到效果最好的那一个。
那么它是如何创建多个决策树的呢,有点复杂。
它并不是多次调用rpart(),简单的调整几个参数而已。
每个决策树所用的数据,都只是原数据的随机subset或者说随机子集。
如果训练集被分成1,2,3,4,5 这五个子集,那么第一次可能选取2,4,5,2,1,第二次可能选取3,5,1,5,2。

参数nodesize

类似于minbucket,每个子集的最小数目。它越小,生成的决策树越大。

参数ntree

生成多少个决策树。一般几百个就够了。

好消息是,参数的选取,相比CART而言,对结果的影响没有那么大。

Cross Validation

minbucket应该选取什么样的值,来大道最好效果呢?
我们采用 k-fold cross validation 的方法。

我们将训练集train分成k份,比如 k=5 的时候,
我们先用1,2,3,4来训练,5用来验证;
再用1,2,3,5来训练,4用来验证;
再用1,2,4,5来训练,3用来验证。。。
所以模型中创建了很多决策树。
我们测试每个分割方法下,参数每一个可能的取值,计算这个取值对应的预测精度,绘制曲线。
曲线的X轴是参数的取值,Y轴是预测精度,这样可以很容易找到参数的最佳取值。

CP

像R平方一样,我们也定义了一个概念 cp(complexity parameter) 用来观测效果。
cp越小,决策树越大(over fitting)。

formula
formula
formula + \lambda*s )
formula})
cp越大,分母越小,tree越小。
cp越小,分母越大,tree越大。

2.建模和评估

CART

# Install rpart library
install.packages("rpart")
library(rpart)
install.packages("rpart.plot")
library(rpart.plot)

# CART model
# method="class" 表示我们创建了一个 classification tree
StevensTree = rpart(Reverse ~ Circuit + Issue + Petitioner + Respondent + LowerCourt + Unconst, data = Train, method="class", minbucket=25)

# plot tree
prp(StevensTree)

# Make predictions
# 记得指定 type = "class"
PredictCART = predict(StevensTree, newdata = Test, type = "class")
table(Test$Reverse, PredictCART)

# ROC curve
library(ROCR)

PredictROC = predict(StevensTree, newdata = Test)
# 注意这里没有指定 type = "class"
# 也就是说,学习得到 classification tree 的模型,但是评估使用 regression tree
# 真是天杀的。。。
# 这个PredictROC 有两列
# 第一列是预测y=0的概率
# 第二列是预测y=1的概率
# 如果比较一下 PredictROC 每行的数据,可以发现这两个概率和为1!那是当然!
# 如果拿 PredictROC 和 PredictCART相比
# 如果 PredictROC[n,2]>0.5,那么PredictCART[n]=1。
# 如果 PredictROC[n,2]<0.5,那么PredictCART[n]=0。
# 所以下面我们只使用第二列

pred = prediction(PredictROC[,2], Test$Reverse)
perf = performance(pred, "tpr", "fpr")
plot(perf)

# AUC
as.numeric(performance(pred, "auc")@y.values)

Random Forest

install.packages("randomForest")
library(randomForest)

# Build random forest model
StevensForest = randomForest(Reverse ~ Circuit + Issue + Petitioner + Respondent + LowerCourt + Unconst, data = Train, ntree=200, nodesize=25 )
# Warning message:
# In randomForest.default(m, y, ...) :
#   The response has five or fewer unique values.  Are you sure you want to do regression?

# 如上面的提示消息所示
# randomForest认为因变量的取值很少,不应该用regression
# 但是 random forest 没有 type = "class" 这样的参数
# 所以我们必须确保因变量这一列的取值都是因子
# Convert outcome to factor
Train$Reverse = as.factor(Train$Reverse)
Test$Reverse = as.factor(Test$Reverse)

# Try again
StevensForest = randomForest(Reverse ~ Circuit + Issue + Petitioner + Respondent + LowerCourt + Unconst, data = Train, ntree=200, nodesize=25 )

# Make predictions
PredictForest = predict(StevensForest, newdata = Test)
table(Test$Reverse, PredictForest)

Cross Validation

# Install cross-validation packages
install.packages("caret")
library(caret)
install.packages("e1071")
library(e1071)

# Define cross-validation experiment
numFolds = trainControl( method = "cv", number = 10 )
cpGrid = expand.grid( .cp = seq(0.01,0.5,0.01)) 

# Perform the cross validation
train(Reverse ~ Circuit + Issue + Petitioner + Respondent + LowerCourt + Unconst, data = Train, method = "rpart", trControl = numFolds, tuneGrid = cpGrid )

# Create a new CART model
StevensTreeCV = rpart(Reverse ~ Circuit + Issue + Petitioner + Respondent + LowerCourt + Unconst, data = Train, method="class", cp = 0.18)

# Make predictions
PredictCV = predict(StevensTreeCV, newdata = Test, type = "class")
table(Test$Reverse, PredictCV)

参数cp和loss的使用

# Penalty Matrix
PenaltyMatrix = matrix(c(0,1,2,3,4,2,0,1,2,3,4,2,0,1,2,6,4,2,0,1,8,6,4,2,0), byrow=TRUE, nrow=5)

# CART model
ClaimsTree = rpart(bucket2009 ~ age + alzheimers + arthritis + cancer + copd + depression + diabetes + heart.failure + ihd + kidney + osteoporosis + stroke + bucket2008 + reimbursement2008, data=ClaimsTrain, method="class", cp=0.00005)

prp(ClaimsTree)

# Make predictions
PredictTest = predict(ClaimsTree, newdata = ClaimsTest, type = "class")
# New CART model with loss matrix
ClaimsTree = rpart(bucket2009 ~ age + alzheimers + arthritis + cancer + copd + depression + diabetes + heart.failure + ihd + kidney + osteoporosis + stroke + bucket2008 + reimbursement2008, data=ClaimsTrain, method="class", cp=0.00005, parms=list(loss=PenaltyMatrix))

# Redo predictions and penalty error
PredictTest = predict(ClaimsTree, newdata = ClaimsTest, type = "class")