一,决策树
决策树是一种分类算法.在生成决策树算法,主要又两个阶段,训练阶段与分类阶段. 训练阶段,是从给定的训练集数据中,构造出一颗决策树. 分类阶段, 是从根开始,按照决策树的分类属性,逐层往下划分,直到叶节点获得概念(决策 \ 分类 )结果.
决策树问题,首先要面对的,是对当根节点这个问题.
这里,我们引入信息熵,信息熵表示数据的混乱程度.
我们用数学方式表示一下, 假设P(x) = P(x)*P(y), 两个事件相互独立,log(xy) = log(x) + log (y).. H(x),H(y)表示事件发生的不确定性, 事件发生的概率越大,H(x)越小.
图型表示一下信息熵. P为概率,小于1的值. 这里我们取对数,在图像上的反应就是P越小,lnP越小. 注意.这里我们把负数放进去,里面变成-lnP. 这里相当于把log曲线,沿着x轴翻转. 最终结果就是, P越小, 总的值越大,数据约乱,熵越高. P越大, 总值约小,数据纯度越高,熵越小.
这边引进一个例子来表示一下.
A = [1,1,2,3,5,7,6,9] 数据某值出现概率低, 数据混乱, 熵高
B = [1,1, 2,2,2,1,2,1] 数据某值出现概率高,数据纯度高,熵低
这里,还有一个比较数据混乱程度,或者纯度的指标.GINI系数
Gini系数表示,概率越小,gini系数越大, 数据越纯.
我们再来介绍一下在运用决策树算法时候,如何选择最优根
1. 计算目标属性(target,已知的分类结果纯度)的信息熵(或者计算Gini系数),来验证数据的纯度:
2. 再计算其他单独属性最为根节点时候的信息熵, 当其做为根时,可能其下又许多子分类,这些子分类也需单独计算信息熵,再把球出来的信息熵与类别概率相乘.
举个栗子:
下面为目标属性,即打不打球的信息熵
下面是基于天气,根据天气来划分是否打球,天气有多种类型. 我们需要即使不同总类型下是否打球.
![]()
所有,以天气作为根节点的话,信息熵为:
![]()
通过这样,把数据单独计算出来,就可以计算出某属性做为根的信息熵了
计算第三部之前,我们先介绍一下评判决策树的标准, 我们构造树的基本想法是,随着树的深度的增加,节点的信息熵迅速减低(数据由:乱-到纯).
3. 计算数据的信息增益: gain = (目标的信息熵) - (某一属性做为根时候的信息熵)
信息增益越大, 数据节点信息熵下降越快. 根节点信息熵越大越好.
比较所有属性的信息增益,我们选择outlook,这个信息增益最大的作为我们的根节点.
当然,我们比较信息增益就可以万无一失的吗?不然下面来介绍一下决策树的几个改进算法
ID3: 信息增益 (上面提到的,不多解释了).
这个算法有一个严重的问题.例如
上面的属性列中,多以一组ID标签,标签值为连续的数值. 当我们再计算这组ID属性的信息增益的时候,有一个好玩的事情.
假设,ID 为根
我们来计算一下叶子节点的信息熵, 单独叶子节点只有一个,P = 1 .log(1) = 0,相乘再累加,所有熵等于0 . 信息增益 = 目标的信息熵 - 0 = 目标的信息熵. 这就使得信息增益最大化了, 按照信息增益的原则,这边会把ID做为根节点, 这样问题就大了, ID 属性与 打不打球这目标毫无干系, 这样将导致分类结果非常差.
这时,决策树进行升级.
C4.5: 信息增益率 = ID3/ 自身的信息增益
以上面例子为例,上面的ID 属性 数据非常不纯, 信息熵非常大. 就算ID3信息增益越大,除于自身也将变得非常的小了. C4.5算法,克服了信息增益选择属性是,喜好偏向多值的属性的不足,这一缺点.
CART:Gini系数, 也是可以当作根选择的标准
接下来我们讲一下,评价函数;
t : 表示叶子节点 . N : 指的是当前叶子节点有几个样本,或者当前叶子节点权重. H(t) : 当前叶子节点的熵值或者Gini系数
有个问题,划分的越准确真的就越好吗?
不然, 我们划分的太精确,非常容易过拟合, 就是过度学习了,把一些不是区分的特征也学习了. 比如西瓜, 学到西瓜是圆的,就把圆的都归类为西瓜,这样分类就MMB了,会被导数掐死的.
咳咳.....所有,这里需要引入几个东西来防止数据过拟合, 预剪枝,后剪枝,设置树的深度.
预剪枝: 在构建决策树的过程时,提前停止,设置到达某个值就停下来.
后剪枝: 决策树构建好后,再进行剪枝. 观察决策树,看看那些分类已经可以了,把后面的剪掉.
这里我们更新一下评价函数,给评价函数添加一个惩罚项,
原来的:
更新后的:
T: 为叶子节点的个数. C(T)为原来的评价函数
决策树可以处理分类为题,那可不可以处理回归问题呢,答案是可以的.
当决策树处理连续值时, 可以把数据进行离散化,把数据划分为多个区间,给个区间做为一个离散点. 还可以对这连续数据进行分界点的选取.(贪婪算法), 对一组连续数据,从第二个开始划分,计算左子树的熵与右子树的熵,并对熵的结果进行求和. 接下到到第三个开始划分,一直划分到倒数第二个. 对这些划分结果的熵就行比较,选最小熵做为分界点.
二 代码实例
import pandas as pd
iris_data = pd.read_csv('iris.data')
iris_data.columns = ['sepal_length_cm', 'sepal_width_cm', 'petal_length_cm', 'petal_width_cm', 'class']
iris_data.head()
import matplotlib.pyplot as plt
iris_data.hist()
from PIL import Image
img=Image.open('test.jpg')
plt.imshow(img)
plt.show()
iris_data.describe()
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sb
#数据集去空之后,对数据集进行类别分布的可视化
sb.pairplot(iris_data.dropna(), hue='class')
iris_data.columns
plt.figure(figsize=(15, 15))
#循环提取iris_data数据的给个列标签与列标签的索引赋值
for column_index, column in enumerate(iris_data.columns):
#类别数据的话跳过
if column == 'class':
continue
plt.subplot(2, 2, column_index + 1)
#绘制单变量,与类别数据分布的小提琴图
sb.violinplot(x='class', y=column, data=iris_data)
from sklearn.model_selection import train_test_split
#合并待处理样本的列数据
all_inputs = iris_data[['sepal_length_cm', 'sepal_width_cm',
'petal_length_cm', 'petal_width_cm']].values
#目标样本分类
all_classes = iris_data['class'].values
#拆分数据集,
(training_inputs,
testing_inputs,
training_classes,
testing_classes) = train_test_split(all_inputs, all_classes, test_size=0.3, random_state=1)
#导入决策树的DTC包
from sklearn.tree import DecisionTreeClassifier
# 1.criterion gini or entropy
# 2.splitter best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候)
# 3.max_features None(所有),log2,sqrt,N 特征小于50的时候一般使用所有的
# 4.max_depth 数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下
# 5.min_samples_split 如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分
# 如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
# 6.min_samples_leaf 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被
# 剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5
# 7.min_weight_fraction_leaf 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起
# 被剪枝默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,
# 或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。
# 8.max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。
# 如果加了限制,算法会建立在最大叶子节点数内最优的决策树。
# 如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制
# 具体的值可以通过交叉验证得到。
# 9.class_weight 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多
# 导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重
# 如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。
# 10.min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度
# (基尼系数,信息增益,均方差,绝对差)小于这个阈值
# 则该节点不再生成子节点。即为叶子节点 。
#所有参数均设置为默认状态
decision_tree_classifier = DecisionTreeClassifier()
# 在训练集上训练分类器
decision_tree_classifier.fit(training_inputs, training_classes)
# 利用分类精度验证测试集上的分类器
decision_tree_classifier.score(testing_inputs, testing_classes)
from sklearn.model_selection import cross_val_score #交叉验证
import numpy as np
decision_tree_classifier = DecisionTreeClassifier()
# cross_val_score returns a list of the scores, which we can visualize
# to get a reasonable estimate of our classifier's performance
#10次交叉验证
cv_scores = cross_val_score(decision_tree_classifier, all_inputs, all_classes, cv=10)
print (cv_scores)
#kde=False
sb.distplot(cv_scores)
plt.title('Average score: {}'.format(np.mean(cv_scores)))
#设置决策树最大的深度,避免模型股过拟合
decision_tree_classifier = DecisionTreeClassifier(max_depth=1)
cv_scores = cross_val_score(decision_tree_classifier, all_inputs, all_classes, cv=10)
print (cv_scores)
sb.distplot(cv_scores, kde=False)
plt.title('Average score: {}'.format(np.mean(cv_scores)))
from sklearn.model_selection import GridSearchCV #模型调优,填入模型会自动调优
from sklearn.model_selection import StratifiedKFold #分层采样数据
decision_tree_classifier = DecisionTreeClassifier()
#设置最大深度,与叶子节点的最大类别特征
parameter_grid = {'max_depth': [1, 2, 3, 4, 5],
'max_features': [1, 2, 3, 4]}
#数据切割数
skf = StratifiedKFold(n_splits=10)
cross_validation = skf.get_n_splits(all_inputs, all_classes)
#采用决策树分类器。第一个参数为分类,第二个参数为填入需要调优的参数,类型为列表或字典。第三个为需要交叉严验证的数据。
grid_search = GridSearchCV(decision_tree_classifier, param_grid=parameter_grid,cv=cross_validation)
#对测试的样本就行预测
grid_search.fit(all_inputs, all_classes)
print("Best score:", grid_search.best_score_)
print("Best param:", grid_search.best_params_)
#可视化评分选择
grid_visualization = []
#对每一个评分,计算均值评分,并追加到列表中。grid_search.cv_results_为决策树分类器交叉验证的结果
for grid_pair in grid_search.cv_results_['mean_test_score']:
grid_visualization.append(grid_pair)
#是数据列表变成数组
grid_visualization = np.array(grid_visualization)
#设置数组的维度
grid_visualization.shape = (5, 4)
#绘制热力图
sb.heatmap(grid_visualization, cmap='Reds')
#x的值为最大特征
plt.xticks(np.arange(4) + 0.5, grid_search.param_grid['max_features'])
#y的值为最大深度,这里对结果数据进行了一次逆序
plt.yticks(np.arange(5) + 0.5, grid_search.param_grid['max_depth'][::-1])
plt.xlabel('max_features')
plt.ylabel('max_depth')
decision_tree_classifier = grid_search.best_estimator_
decision_tree_classifier
import sklearn.tree as tree
from sklearn.externals.six import StringIO
with open('iris_dtc.dot', 'w') as out_file:
out_file = tree.export_graphviz(decision_tree_classifier, out_file=out_file)
#http://www.graphviz.org/
import pydotplus
from IPython.display import Image
dot_data = tree.export_graphviz(decision_tree_classifier, out_file=None, feature_names=iris_data.columns[0:4], class_names=iris_data.columns[0:-1], filled=True, rounded=True, special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
from sklearn.ensemble import RandomForestClassifier #随机森林分类器
random_forest_classifier = RandomForestClassifier()
parameter_grid = {'n_estimators': [5, 10, 25, 50],
'criterion': ['gini', 'entropy'],
'max_features': [1, 2, 3, 4],
'warm_start': [True, False]}
skf = StratifiedKFold(n_splits=10)
cross_validation = skf.get_n_splits(all_inputs, all_classes)
# cross_validation = StratifiedKFold(n_splits=10)
grid_search = GridSearchCV(random_forest_classifier,
param_grid=parameter_grid,
cv=cross_validation)
grid_search.fit(all_inputs, all_classes)
print('Best score: {}'.format(grid_search.best_score_))
print('Best parameters: {}'.format(grid_search.best_params_))
grid_search.best_estimator_