前言
本文试图提纲挈领的对决策树和随机森林的原理及应用做以分析
决策树
算法伪代码
def 创建决策树:
if (数据集中所有样本分类一致): #或者其他终止条件
创建携带类标签的叶子节点
else:
寻找划分数据集的最好特征
根据最好特征划分数据集
for 每个划分的数据集:
创建决策子树(递归)
注意算法要采用递归的方法
依据选取划分数据集特征的方法,有三种常用方法:
ID3:信息增益
C4.5:信息增益率
CART:基尼系数
对于连续特征:
比如m个样本的连续特征A有m个,从小到大排列为a1,a2,...,am,则CART算法取相邻两样本值的中位数,一共取得m-1个划分点,其中第i个划分点Ti表示Ti表示为:Ti=(ai+ai+1)/2。对于这m-1个点,分别计算以该点作为二元分类点时的基尼系数。选择基尼系数最小的点作为该连续特征的二元离散分类点,这样我们就做到了连续特征的离散化。要注意的是,与离散属性不同的是,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程。
过拟合问题-——剪枝
过多的枝叶会导致算法的过拟合,所以常使用减枝的方法防止过拟合:常用的剪枝方法有前置法和后置法,前置法指的是在要构建决策树时,依据终止条件确定是否提前终止;后置法指构建好决策树后,再依据条件确定是否用单一节点代替子树。
回归问题
一般采用均方误差作为评价特征的标准
随机森林
bootstrap方法:从样本集进行有放回的重采样。
随机森林步骤:
1. 样本的随机:从样本集中用Bootstrap随机选取n个样本
2. 特征的随机:从所有属性中随机选取K个属性,选择最佳分割属性作为节点建立CART决策树
3. 重复以上两步m次,即建立了m棵CART决策树
4. 这m个CART形成随机森林,通过投票表决结果,决定数据属于哪一类(投票机制有一票否决制、少数服从多数、加权多数)
参考:https://www.cnblogs.com/fionacai/p/5894142.html
sklearn.tree.DecisionTreeClassifier
class sklearn.tree.
DecisionTreeClassifier
(criterion=’gini’, splitter=’best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
Parameters: criterion='gini' or 'entropy'
max_depth 设置树的最大深度 默认为‘None’
min_sample_split 分割的最小样本数 默认为2
详细内容的中文说明可以参考 https://www.cnblogs.com/pinard/p/6056319.html
sklearn.tree.DecisionTreeRegressor
Parameters: criterion='mse' 还可以使用‘friedman_mse’或者'mae'
max_depth 设置树的最大深度 默认为‘None’
sklearn.ensemble
.RandomForestClassifier
Parameters: n_estimator=10 生成决策树的个数
criterion='gini' or 'entropy'
应用举例
使用随机森林进行回归的例子
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
N=100
x = np.random.rand(N) * 6 - 3
x.sort()
y=0.1*x**3+np.exp(-x*x/2)+ np.random.randn(N) * 0.2 #3次函数+高斯函数+噪声
x = x.reshape(-1, 1)
x_bar=np.linspace(-3,3,50).reshape(-1,1)
y_bar=0.1*x_bar**3+np.exp(-x_bar*x_bar/2)
plt.plot(x,y,'r.')
plt.plot(x_bar,y_bar)
plt.show()
plt.plot(x,y,'r.')
x_test=np.linspace(-3,3,50).reshape(-1,1)
dt = DecisionTreeRegressor(criterion='mse')
depth=[3,6,18]
color='rgy'
for d,c in zip(depth,color):
dt.set_params(max_depth=d)
dt.fit(x, y)
y_test = dt.predict(x_test)
plt.plot(x_test,y_test,'-',color=c, linewidth=2, label='Depth=%d' % d)
plt.legend(loc='upper left')
plt.xlabel(u'X')
plt.ylabel(u'Y')
plt.grid(b=True)
plt.show()
图1
图2
对比图1中的真实曲线和图2中三条拟合折线可以发现,图二中的折现较好的拟合了原始曲线,如果单纯使用一次特征的线性回归肯定是达不到这样的效果的,其次,我们可以看到当树最大深度为3时,折线段较少,细小的弯曲无法体现,而当深度达到18时,折线波折又过多,有过拟合的嫌疑,当深度为6时,相对较好,因此,我们推断树的深度在拟合过程中起到了重要作用。
数据集 Adult
字段名 | 含义 | 类型 |
age | 年龄 | Double |
workclass | 工作类型 | string |
fnlwgt | 序号 | string |
education | 教育程度* | string |
education_num | 受教育时间 | double |
maritial_status | 婚姻状况* | string |
occupation | 职业* | string |
relationship | 关系* | string |
race | 种族* | string |
sex | 性别* | string |
capital_gain | 资本收益 | string |
capital_loss | 资本损失 | string |
hours_per_week | 每周工作小时数 | double |
native_country | 国籍 | string |
income | 收入 | string |
表1
基于上面特征,预测收入是否大于50K,我们用决策树和随机森林来解决上述问题。
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import LabelEncoder
column_names = 'age', 'workclass', 'fnlwgt', 'education', 'education-num', 'marital-status', 'occupation', \
'relationship', 'race', 'sex', 'capital-gain', 'capital-loss', 'hours-per-week',\
'native-country', 'income' #不加括号为元组
data = pd.read_csv('adult.data', header=None, names=column_names)
data.isnull().any().any() #是否存在null
for name in column_names:
data[name]=LabelEncoder().fit_transform(data[name]) #自动转换非数字列为数字
x = data[data.columns[:-1]]
y = data[data.columns[-1]]
x_train, x_valid, y_train, y_valid = train_test_split(x, y, test_size=0.3, random_state=0)
DT = DecisionTreeClassifier(criterion='gini',max_depth=10,min_samples_split=5)
RF = RandomForestClassifier(criterion='gini', max_depth=10, min_samples_split=5,n_estimators=20)
LR= LogisticRegression()
models=[DT,RF,LR]
for model in models:
model.fit(x_train,y_train)
y_pre=model.predict(x_valid)
print('验证集准确率:', accuracy_score(y_pre, y_valid))
运行结果为:
验证集准确率: 0.852287849319
验证集准确率: 0.858429726686
验证集准确率: 0.805507216706
对比结果,在随机森林与决策树选取相似的参数时,随机森林的结果略好于决策树,logistic回归结果最差,但注意这里logistic回归没有调参。
附:Adult数据集 https://pan.baidu.com/s/1vTMN9Yp6VPndezUR1UtYtw
致谢:本文基于小象学院邹博老师机器学习课程,部分代码参考该课程,特此感谢。