机器学习算法篇:决策树和随机森林算法
基本概念
决策树(Decision tree),通俗来讲就是对数据的特征进行一系列的 if 判断从而分类数据。在数据的特征属性中,指定一些条件,从根节点一层一层决策到子节点,最终所有数据都在叶子节点上。
应用:决策树可以做分类,也可以做回归。
注意,决策树对于条件的层级(先后顺序)严格,不能乱。
如何选择特征?
如何评价特征强弱?需要一种评价标准,通过计算不同特征分类后的情况,寻找到最好的那个特征作为根节点。为了评判特征,不得不先了解一个概念,熵(entropy)。
熵
H
(
x
)
=
−
∑
p
i
∗
l
o
g
p
i
H(x)=- \sum p_i * log p_i
H(x)=−∑pi∗logpi ,
i
=
1
,
2
,
3
,
4
,
5.....
n
i=1,2,3,4,5.....n
i=1,2,3,4,5.....n
确定根节点特征的方法:
- 首先,计算初始数据的基础熵值,对每个类别的熵值求和。
- 然后,对通过特征分类后的左右子树的数据进行熵值求和。
- 对比熵值下降的多少来比较特征好坏,哪个特征分类后熵值下降快(信息增益大)就当作根节点。
信息增益计算公式: I g a i n = H b a s e ( x ) − H c u r r e n t ( x ) I_{gain}=H_{base}(x)-H_{current}(x) Igain=Hbase(x)−Hcurrent(x)
即信息增益越小每一层必须比上一层的熵值小才好(信息增益)
衡量特征强弱的指标除了信息增益之外,还有信息增益率和CART(使用基尼系数计算)
注意:多项类别计算熵值和时注意加权。
连续值怎么处理?
对于回归问题,选取连续值中的一个切分数据,寻找最优的点。(连续值离散化)
连续值离散化在遥感影像中的直方图匹配中也有重要应用。通过将遥感影像中连续的灰度值设置一定的区间bins来分类数据,从而得到一系列离散的数据点。
决策树剪枝策略
预防过拟合,所有的数据无限地分类,直到每个节点都只有一个值,准确率全是100%。太庞大,不合适。
预剪枝
限制树的深度,叶子节点个数,叶子节点样本数,信息增益率
后剪枝
通过一定评价标准, C α ( T ) = C ( T ) + α ∗ ∣ T l e a f ∣ C_α(T)=C(T)+α*|T_{leaf}| Cα(T)=C(T)+α∗∣Tleaf∣
α α α 一般看需求,关注叶子节点数量可以增大,需要数据精确则减小。
回归问题
决策树应用回归问题,评估标准采用方差计算。
集成算法
单一的决策树在面对一些复杂的问题存在一定的限制和不足。因此通过集成算法将模型进行组合,一次来解决复杂问题。通常集成算法有三种方式:
- Bagging:训练多个分类器取平均
- Boosting:从弱学习器开始加强,通过加权来进行计算
- Stacking:聚合多个分类或回归模型
Bagging方式
使用bagging方式集成算法的典型代表就是随机森林(Random Forest)算法
这种方式将许多决策树并行起来对数据进行分类处理,每颗树模型都是并行计算,不会产生干扰。
此时有一个问题,多棵决策树一起分类,数据还是原来的,结果不还是一样的嘛,有什么意义?
当然,随机森林之所以称之为随机森林因为它随机嘛(说了话跟说了话是的,(●ˇ∀ˇ●))
- 每一颗树使用的样本都是随机的(样本随机性)
- 每一颗树随机取特征中的几个(特征随机性)
这样就保证了每一颗树模型产生的结果不会完全相同了。
特征
- 它能够处理很高维度( feature很多)的数据,并且不用做特征选择(强特征就在上面,弱特征在下面)
- 在训练完后,它能够给出哪些feature比较重要
- 容易做成并行化方法,速度比较快
- 可以进行可视化展示,便于分析
Boosting 方式
代表:AdaBoost、XGBoost
这种方式能够提升算法精度,将决策树串联起来进行一步一步的分类,每一次都比上一次好。
对于一个回归问题,首先第一棵树模型对数据进行训练,然后第二棵树对A的不足,loss进行改进弥补,然后第三棵树将前两棵树当作整体,进行loss改进,以此类推。
加入的树模型一定要比前面的模型强,即加入后的总体loss减少。
Stacking方式
暴力、拿一堆分类器来堆
单纯为了刷结果,不考虑时间成本,效率等等。
决策树构造实例
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
from math import log
import operator
import pickle
def crateDataSet():
dataSet = [[0, 0, 0, 0, 'no'],
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels = ['F1-AGE','F2-wORK', 'F3-HOME','F4-LOAN']
return dataSet, labels
def createTree(dataSet,labels,featurelabels):
classList = [example[-1] for example in dataSet]
if classList.count(classList[0]) == len(classList):
return classList[0]
if len(dataSet[0]) == 1:
return majorityCnt(classList)
bestFeat = chooseBestFeatureTosplit(dataSet)
bestFeatLabel=labels[bestFeat]
featurelabels.append(bestFeatLabel)
myTree={bestFeatLabel:{}}
del labels[bestFeat]
featValue=[example[bestFeat] for example in dataSet]
uniqueVals=set(featValue)
for value in uniqueVals:
sublabels = labels[:]
myTree[bestFeatLabel][value]=createTree(spiltDataSet(dataSet,bestFeat,value),sublabels,featurelabels)
return myTree
def majorityCnt(classList):
classCount = {}
for vote in classList:
if vote not in classCount.keys(): classCount[vote] = 0
classCount[vote] += 1
sortedclassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedclassCount[0][0]
def chooseBestFeatureTosplit(dataset):
numFeatures = len(dataset[0]) - 1
baseEntropy = calcShannonEnt(dataset)
bestInfoGain = 0
bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataset]
uniqueVals = set(featList)
newEntropy = 0
for val in uniqueVals:
subDataSet =spiltDataSet(dataset,i,val)
prob =len(subDataSet)/float(len(dataset))
newEntropy += prob*calcShannonEnt(subDataSet)
infoGain =baseEntropy-newEntropy
if(infoGain>bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
def spiltDataSet(dataset,axis,val):
retDataSet = []
for featVec in dataset:
if featVec[axis] == val:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
##计算熵值
def calcShannonEnt(dataset):
numexamples = len(dataset)
labelCounts ={}
for featVec in dataset:
currentlabel= featVec[-1]
if currentlabel not in labelCounts.keys():
labelCounts[currentlabel]=0
labelCounts[currentlabel]+=1
shannonEnt = 0
for key in labelCounts:
prob = float(labelCounts[key])/numexamples
shannonEnt-=prob*log(prob,2)
return shannonEnt
if __name__ == '__main__':
dataset,labels= crateDataSet()
featlabel=[]
myTree=createTree(dataset,labels,featlabel)