1、决策树
1.1 分类树
在sklearn中所有的机器模型算法都可以简化为三行代码。
对应的代码如下所示:
# 第一步骤:实例化一个决策树的模型,其中criterion代表决策树进行决策的准则,有gini和entropy两种方法
clf = tree.DecisionTreeClassifier(criterion="entropy")
# 第二步骤:将之前准备好的数据喂入决策树,将其对应的数据和标签成组输入
clf = clf.fit(Xtrain,Ytrain)
# 第三步骤:准备通过train_test_split方法得到的切分后的数据集进行测试,输出内容为对应的acc得分
score = clf.score(Xtest,Ytest)
在本实验中,我我们采用红酒数据集来进行辅助的学习:
# 第一步:导入已经处理好的红酒数据集
from sklearn.datasets import load_wine
# 第二部:实例化这个数据集接口
wine = load_wine()
# 第三步(选):将数据集可视化操作,记住concat中需要给定对应合并到维度条件
import pandas as pd
pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target),axis=1])
# 如下是对应的可视化数据集的结果
# 拿到了wine整体的数据集,但是没有区别训练集和验证集两种不同的状态
Xtrain,Xtest,Ytrain,Ytest = train_test_split(wine.data, wine.target, test_size=0.3)
# 到此为止,所有的准备工作都已经完成了
# 接下来,执行sk三部曲,并输出你的得分,就可以渐渐看见你需要的树的得分了
决策树可视化
可是?我的树在哪呢?我怎么看不见,因此将我们的决策树实例化就显得非常重要了。
import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf,feature_names=feature_name,class_names=["琴酒",'雪莉','贝尔摩德'],
filled=True,rounded=True)
graph = graphviz.Source(dot_data)
这个树还是比较大的,但是我们可以发现其实在我们的训练数据中,拥有者13个不同的指标及其对应的数据,但是为什么这儿只有这点不同的决策条件呢?
这是因为:决策树本身就具有一定的随机性,在实例化对应的决策树的时候,内部有不少的参数采用了默认的设置内容,包括random_state这类随机种子,因此多次执行上述的三部曲,都有可能出现不同的数值;就决策树本身而言,无论模型如何进化,本质上都在某个不纯度相关的指标上进行优化。因此,集成算法被用来解决这个问题:sklearn表示,既然一棵树不能保证最优,那就建更多不同的树没然后从其中挑选出最好的树。这样,也就导致了每次都会生成一些结果不同的树。
那么?哪些参数会影响我们的树模型呢?
随机参数
- random_state:随机种子,默认为None,表示每一次建立决策树的时候都随机,在高维度特征上表现明显,如果固定输入整数,则会让模型稳定下来,只输出一个树。
- splitter:best or random 表示决策树分支时候的策略,选择best时候,会根据属性feature_importances来进行。
clf.feature_importances_
# 获得决策树的属性,输出其中的关键分类点
# 将zip函数将名字和其对应的全书组成元组,并输出,可以更直观的看到哪一个数据起到决定性作用
[*zip(feature_name,clf.feature_importances_)]
剪枝参数
决策树的模型也并非越深越好,当模型足够深就会出现过拟合的情况,因此我们应当对模型进行限制,防止其出现过拟合的情况
- max_depth:限制树的最大深度,将超过设定深度的树枝全部剪掉
- min_samples_leaf: 决定了一个节点在分支后的每个字典姐必须包含至少X个训练样本,否则不会进行分支,或者香可以分支的方向发展,一般搭配max_depth使用,太小和太大都会导致意想不到的情况发生,因此要进行不断的验证并且结合自己的经验。
- min_samples_split: 刚才的参数决定了叶子节点中个数的,现在这个决定了节点必须要包含Y个样本,才可以被允许分支,否则不会发生这样的问题。
- max_features: 限制建树时候的对于特征的选择,比较暴力,建立搭配PCA或者ICA这种降维算一起使用
- min_impurity_decrease: 用来限制信息增益的带下,如果信息增益小于设定的数值分支就不会发生。
分类树总结
- 八个参数
- 一个属性
- 四个接口:fit、score、apply、predict(其中apply返回对应的索引,predict返回对应的标签)
1.2 回归树
回归树主要不同于决策树的在于其关于label的预测并不是一个指定的,出现截断式的输出,而是一种体现连续化的分布,更多的是拟合一条比较好的曲线去代替其中对应的内容。
回归树衡量分支质量的指标支持三种不同的标准:
- mse使用均方误差mean squared error
- friedman_mse费尔德曼均方误差,这种指标主要针对潜在分支中的问题改进后的均方误差
- mae是u用绝对值平均误差mean absolute error
在本次学习中,我们采用波士顿房价来进行训练,在sklearn中提供相应的数据集用于学习模型
from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
import pandas as pd
boston = load_boston()
# 将波士顿房价进行可视化的输出来展示
pd.concat([pd.DataFrame(boston.data),pd.DataFrame(boston.target)],axis=1)
根据上图我们可以检出,一种拥有13个不同的指标如下所示,其中最后为其对应的标签。
array([‘CRIM’, ‘ZN’, ‘INDUS’, ‘CHAS’, ‘NOX’, ‘RM’, ‘AGE’, ‘DIS’, ‘RAD’, ‘TAX’, ‘PTRATIO’, ‘B’, ‘LSTAT’], dtype=’<U7’)
搭建模型三部曲
regressor = DecisionTreeRegressor(random_state=0)
cross_val_score(regressor, boston.data, boston.target, cv=10)
#array([ 0.48141081, 0.60461936, -1.32633384, 0.54154398, 0.75705408,
# 0.33934083, 0.18757206, 0.40679147, -1.9602183 , -0.32967889])
# 利用数据交叉认证来完成对应,其中cv表示对应的数据需要加载的次数,默认的评价指标采用的是R平方来操作数据分布为[-∞,1]
regressor = DecisionTreeRegressor(random_state=0)
cross_val_score(regressor, boston.data, boston.target, cv=10,scoring="neg_mean_squared_error")
# 同上,-mse的数据分布为[-∞,0]
#array([-18.08941176, -10.61843137, -16.31843137, -44.97803922,
# -17.12509804, -49.71509804, -12.9986 , -88.4514 ,
# -55.7914 , -25.0816 ])
实例一:一维回归的图像绘制,拟合正弦函数
# 导入一些必要的包
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
# 生成一些必要的数据用于训练模型
# 设立随机种子
rng = np.random.RandomState(1)
# 对生成的随机数字进行排序
X = np.sort(5*rng.rand(80,1),axis=0)
y = np.sin(X).ravel() # 去除矩阵中一些无用的维度信息,np中的降维函数
# 为生成的数据添加一些扰动来干扰模型,模拟显示数据可能存在的问题
y[::5] += 3*(0.5 -rng.rand(16))
# 生成三种不同的回归树,深度设置成2、3、5来展示
regr_1 = DecisionTreeRegressor(max_depth=2)
regr_2 = DecisionTreeRegressor(max_depth=5)
regr_3 = DecisionTreeRegressor(max_depth=3)
# 对三个模型喂入一定的数据
regr_1.fit(X,y)
regr_2.fit(X,y)
regr_3.fit(X,y)
# 生成序列化的数据用于测试,病假数据插入对应的模型中进行测试
Xtest = np.arange(0.0, 5.0, 0.01)[:,np.newaxis]
y_1 = regr_1.predict(Xtest)
y_3 = regr_2.predict(Xtest)
y_5 = regr_3.predict(Xtest)
# 可视化视图
plt.figure()
plt.scatter(X,y,s=20,edgecolors="black",c='darkorange',label='data')
plt.plot(Xtest,y_1,color="blue",label='max_2',linewidth=2)
plt.plot(Xtest,y_3,color="green",label='max_5',linewidth=2)
plt.plot(Xtest,y_5,color="yellow",label='max_3',linewidth=2)
plt.xlabel('data')
plt.ylabel('target')
plt.title('Decision Tree Regression')
plt.legend()
plt.show()
实例二:泰坦尼克号数据
泰坦尼克号的沉没是迄今为止最大的海难吗,但在sklearn中并没有提供其对应的数据集,可以在kaggle中找到对应的数据。
# 导入可能使用的方法
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pyplot as plt
from sklearn.model_selection import GridSearchCV,train_test_split,cross_val_predict
# 导入对应的数据集,具有data和test两个数据集,其中test数据集并没有对应的label,主要用于比赛中。
taitanic_data = pd.read_csv("Taitanic_data/data.csv")
如下是taitanic_data数据集的展示:
# 输出数据的头部数据,大概5个,也可以在head中写入需要展示的数据个数
taitanic_data.head()
# 输出数据的信息
taitanic_data.info()
# info中的信息更为具体,可以看见对应的列数和其格式还有是否为空,对后续的数据集操作都有比较好的效果和帮助
在上图中可以发现,其中存在一定的缺失值,age确实的内容较少且age确实会对是否存活有着比较大的影响。类似于Cabin这种无效的且缺失严重的内容可以将其抛弃。类似对于是否存活无关的数据还有Name和Ticket等信息。
# 删除一些无用的数值来进行,可以提前判断某些内容是否影响对应的结果
taitanic_data.drop(['Cabin','Name','Ticket'],inplace=True,axis=1)
如上图就是经过drop操作后的数据,除去了一些无用的特征信息来使得数据更为精纯。
# 处理缺失值,对缺失值较多的列进行填补,如果有些特征确实缺少太多数据,完全可以删除对应的记录
taitanic_data['Age'] = taitanic_data['Age'].fillna(taitanic_data['Age'].mean())
taitanic_data = taitanic_data.dropna()
# 将分类变量转换为数值型变量
# 方法一:主要针对于二分类特征0~1,可以定义某一个数值为True,另一种就是False,然后通过astype()
taitanic_data['Sex'] = (taitanic_data['Sex']=='male').astype('int')
# 方法二:将三分类变量转换为数值型变量
# 将对应的数据转换成为,可以理解为set操作,将重复的数值去掉,转换成list后续可以用来获取对应的下标用来代替标签
labels = taitanic_data['Embarked'].unique().tolist()
taitanic_data['Embarked'] = taitanic_data['Embarked'].apply(lambda x: labels.index(x))
执行上述代码的时候可以发现,会有warning的提醒跳出,如下所示
这里提醒我们最好使用 loc()方法来帮助实现刚才对应任务。
x = taitanic_data.iloc[:,taitanic_data.columns != 'Survived']
y = taitanic_data.iloc[:,taitanic_data.columns == 'Survived']
# 需要注意一下train_test_split的划分数据集,四个数据集的顺序是否正确
Xtrain,Xtest,Ytrain,Ytest = train_test_split(x,y,test_size=0.3)
# 针对一些比较复杂的数据,需要修正对应的索引,并且可以帮助后期的查询辅助等任务
for i in [Xtrain,Xtest,Ytrain,Ytest]:
i.index = range(i.shape[0])
Xtrain.head()
# 训练模型
clf = DecisionTreeClassifier(random_state=25)
clf = clf.fit(Xtrain,Ytrain)
score_ = clf.score(Xtest,Ytest)
# 0.7715355805243446
score = cross_val_socre(clf,x,y,cv=10).mean()
# 0.7212391248121231
# 效果还是太差了,想办法提升一下对应的效果,针对于通道的max_depth进行调整
tr = []
te = []
n = 10
for i in range(n):
clf = DecisionTreeClassifier(random_state=3,max_depth=i+1,criterion='entropy')
clf.fit(Xtrain,Ytrain)
score_tr = clf.score(Xtest,Ytest)
score_te = cross_val_score(clf,x,y,cv=10).mean()
tr.append(score_tr)
te.append(score_te)
print(max(te))
plt.figure()
plt.plot(range(1,n+1),tr,color='red',label='train')
plt.plot(range(1,n+1),te,color='blue',label='test')
plt.xticks(range(1,n+1))
plt.legend()
plt.show()
# 输出的图片如下所示
可以看出,在max_depth=3的时候,效果是最好的。
加餐:网格搜索
# 为了更好的显示数据,搜索出一个比较好的效果,
import numpy as np
# 设置基尼系数的边界,一般取值在(0,0.5)之间,其中每个参数都会倍用来搜索
gini_thresholds = np.linspace(0,0.5,20)
parameters = {
'criterion':('gini','entropy'),
'splitter':('best','random'),
'max_depth':[*range(1,11)],
'min_samples_leaf':[*range(1,50,5)]
,'min_impurity_decrease':[*np.linspace(0,0.5,20)]
}
# 设置好对应的网格搜索内容后可以进行决策树的网格搜索
clf = DecisionTreeClassifier(random_state=25)
GS = GridSearchCV(clf,parameters,cv=10)
GS.fit(Xtrain,Ytrain)
GS.best_score_
利用网格搜索,可以得到对应的搜索数据,但是GridSearch并不会让你的模型有意识的选择部分参数,因此如果在parameters中设定的参数都会参与到网络的选择中,但是根据结果发现,其实通过criterion和max_depth就可以较好的选择出一个模型,其他参数的设定会导致模型效果下降。
GS.best_params_
# 将最好的parameters进行输出,得到最好的效果
{'criterion': 'gini',
'max_depth': 7,
'min_impurity_decrease': 0.0,
'min_samples_leaf': 6,
'splitter': 'best'}
GS.best_score_
# 输出网格搜索中最好的得分
0.829390681003584
实例三:在合成数据集上的表现
就决策树而言,不同的数据分布也会影响其在数据上的不同表现,接下来我们用实验的方式进行探究。看看究竟在那种模式上,决策拥有比较好的效果呢?
# 导入一些常用的库
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 从sklearn数据集中分别导入月亮、环形、分类三种不同的数据集,用于后期探究何种数据上分类效果更好?
from sklearn.datasets import make_noons, make_circles, make_classification
from sklearn.tree import DecisionTreeClassifier
生成数据和数据可视化
# 利用对应的api生成对应的数据来展示
# 其中n_samples表示生成多少个样本,n_features指的是样本的维度,n_redundant表示冗余特征个数
# n_informative表示包含信息的特征个数,n_clusters_per_class表示每个簇内的类别个数
x,y = make_classification(n_samples=100,n_features=2,n_redundant=0,n_informative=2,random_state=2,n_clusters_per_class=1)
class_a = []
class_b = []
for i in range(100):
if y[i]==0:
class_a.append(list(x[i]))
else:
class_b.append(list(x[i]))
class_a = np.array(class_a)
class_b = np.array(class_b)
plt.figure()
plt.scatter(class_a[:,0],class_a[:,1],color='red')
plt.scatter(class_b[:,0],class_b[:,1],color='blue')
plt.show()
如下是对应的可视化结果
可以发现,通过这样生成的图片,具有一个比较明显的分类决策边界,为了使得模型具备鲁棒性,我们给我们的模型添加一定的干扰噪声。
rng = np.random.RandomState(2) # 生成一种随机模式
x += 2* rng.uniform(size=x.shape) # 生成一个和x矩阵大小一致的新矩阵
linearly_separable = (x,y)
# 将三种的数据集整合变,并且打包放在datasets中
datasets = [make_moons(noise=0.3,random_state=0),
make_circles(noise=0.2,factor=0.5,random_state=1),
linearly_separable]
1.3 决策树的优缺点
优点:
- 易于解释和理解,因为可以将树结构可视化实现
- 并不需要大量的数据进行训练
- 可是显示回归和分类,方法具有普适性
- 能够处理多输出问题
- 白盒模型
- 具有一定的容错率,具备鲁棒能力
缺点:
- 决策树学习者可能创建过于复杂的树,这些树不能很好 推广数据。
- 决策树是不稳定的,其中微笑的变化可能会导致生成完全不同的树
- 决策树的学习是基于贪婪算法的,可能会局部最优,但不能保证全局最优
- 如果标签中某一个类占有主要地位,那么决策树会创建一个偏向主导类的树。因此,对于数据集有一定的要求。