scikit-learn官网:http://scikit-learn.org/stable/index.html
中文翻译网址:https://sklearn.apachecn.org/docs/master/2.html
算法原理推荐书籍:《数据挖掘导论》、《机器学习》
非参数的有监督学习方法,决策树算法的本质是一种图结构
通过对记录的特征的提问,对样本进行分类
关于决策树的节点概念:
1、根节点:没有进边,有出边,包含最初的,针对特征的提问
2、中间节点:既有进边也有出边,进边只有一条,出边可以有很多条,都是针对特征的提问
3、叶子节点:有进边,没有出边,每个叶子节点都有一个类别标签
4、子节点和父节点:在两个相连的节点中,更接近根节点的是父节点,另一个是子节点
决策树算法的核心要解决两个问题:
1、如何从数据表中找出最佳节点和最佳分支?
2、如何让决策树停止生长,防止过拟合?
from sklearn import tree #导入需要的模块
clf = tree.DecisionTreeClassifier() #实例化,需要使用的参数填入()中
clf = clf.fit(x_train,y_train) #用训练集数据训练模型,使用fit接口
result = clf.score(x_train,y_train) #导入测试数据集,利用score接口调用需要的信息
sklearn.DecisionTreeClassifier(criterion=‘gini’,splitter=‘best’,max_depth=None,min_samples_split=2,min_samples_leaf=1,min_weight -raction_leaf=0.0,max_features=None,random_state=None,max_leaf_nodes=None,min_impurity=0.0,min_impurity_split=None,class_weight=None,presort=False)
8个重要的参数
决策树需要找出最佳节点和最佳的分枝方法,衡量“最佳”的指标叫“不纯度”,不纯度越低,决策树对训练集的拟合越好。
不纯度基于节点计算,书中的每个节点都有一个不纯度,并且子节点的不纯度一定是低于父节点的,可以说,在同一颗决策树上,叶子节点的不纯度是最低的。
criterion是用来决定不纯度的计算方法,可选项:
random_state用来设置分枝中的随机模式的参数,默认为None,在高维度时随机性会表现更明显,低纬度的数据随机性几乎 不会显现,输入任意整数,会一直长出同一棵树,让模型稳定下来。
splitter也是用来控制决策树中的随机选项的,有两种输入值:输入“best”,决策树在分枝时虽然随机,但会优先选择更重要的特征进行分枝;输入“random”,分枝时会更随机,树会更深,对训练集的拟合将会降低。
当预测到模型会过拟合时,用这两个参数会帮助降低过拟合的可能性。
过拟合:在训练集上表现很好,在测试集上表现糟糕
为了使决策树局有更好的泛化性,要对其进行剪枝。剪枝策略对决策树影响巨大,正确的剪枝策略是优化决策树算法的核心。
sklearn提供多种剪枝策略:
class_weight & min_weight_fraction_leaf:都是完成样本标签平衡的参数。样本不平衡是指在一组数据集中,标签的一类天生占有很大的比例。要使用class_weight参数对样本标签进行一定的均衡,给少量的标签更多的权重,让模型偏向少数类,向捕获少数类的方向建模,该参数默认为None,表示自动给予数据集中的所有标签相同的权重。有了权重之后,样本量就不再是单纯地记录数目,而是受输入的权重影响了。此时剪枝就需要搭配min_weight_fraction_leaf这个基于权重的剪枝参数来使用。基于权重的剪枝参数(min_weight_fraction_leaf)将比不知道样本权重的标准(min_samples_leaf)更少偏向主导类。如果样本是加权的,则使用基于权重的预修剪标准更容易优化树结构,这确保叶节点至少包含样本权重总和的一小部分。
属性是在模型训练之后,能够调用查看的模型的各种性质。决策树中最重要的属性是feature_importances_,查看各个特征对模型的重要性。
sklearn中许多算法的接口都是相似的,如fit和score。决策树最常用的接口还有apply和predict,apply中输入测试集的特征返回每个测试样本所在叶子结点的索引,predict输入测试集的特征返回每个测试样本的标签(分类/回归结果)。
**所有接口中要求输入X_train和X_test的部分,输入的特征矩阵必须至少是一个二维矩阵,sklearn不接受任何一维矩阵作为特征矩阵被输入。**如果数据的确只有一个特征,那必须使用reshape(-1,1)来给矩阵增维;如果数据只有一个特征和一个样本,使用reshape(1,-1)来给数据增维。
sklearn.DecisionTreeRegressor(criterion=‘mse’,splitter=‘best’,max_depth=None,min_samples_split=2,min_samples_leaf=1,min_weight -raction_leaf=0.0,max_features=None,random_state=None,max_leaf_nodes=None,min_impurity=0.0,min_impurity_split=None,presort=False)
几乎所有的参数、属性和接口都和分类树一模一样。注意:在回归树中,没有标签分布是否均衡的问题,因此没有class_weight这一参数。
#交叉验证cross_val_score的用法
from sklearn.datasets import load_boston
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeRegressor
boston = load_boston()
regressor = DecisionTreeRegressor(random_state = 0)
cross_val_score(regressor,boston.data,boston.target,cv = 10
#,scoring = "neg_mean_squared_error"
#scoring默认返回R方
)
'''
结果:
array([ 0.48141081, 0.60461936, -1.32633384, 0.54154398, 0.75705408,
0.33934083, 0.18757206, 0.40679147, -1.9602183 , -0.32967889])
'''
观察决策树是如何拟合一条曲线的,用回归树来拟合正弦曲线,并添加一些噪声来观察回归树的表现。
#导入需要的库
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
#创建一条含有噪声的正弦曲线
#基本思路:创建一组随机的,分布在0-5上的横坐标的取值(x),然后将这组数据放到sin函数中去生成纵坐标的值(y),接着再到y上去添加噪声
rng = np.random.RandomState(1)#设置随机数种子
x=np.sort(5 * rng.rand(80,1),axis = 0)#80行1列,因为fit不支持一维数据
y = np.sin(x).ravel()
plt.figure()
plt.scatter(x,y,s=20,edgecolor = 'black',c='darkorange',label = 'data')
#结果:
#np.random.rand(数据结构),生成随机数组的函数
#了解降维函数ravel()的用法
np.random.random((2,1))
np.random.random((2,1)).ravel()
np.random.random((2,1)).ravel().shape
#结果:(2,)
y[::5] += 3 * (0.5-rng.rand(16))#加上噪声
plt.scatter(x,y,s=20,edgecolor = 'black',c='darkorange',label = 'data')
#结果:
#实例化&训练模型
regr_1 = DecisionTreeRegressor(max_depth = 2)
regr_2 = DecisionTreeRegressor(max_depth = 5)
regr_1.fit(x,y)
regr_2.fit(x,y)
#结果:DecisionTreeRegressor(max_depth=5)
#测试集导入模型,预测结果
x_test = np.arange(0.0,5.0,0.01)[:,np.newaxis]
y_1 = regr_1.predict(x_test)
y_2 = regr_2.predict(x_test)
#np.arange(开始点,结束点,步长)生成有序数组的函数
#了解增维切片np.newaxis的用法
l = np.array([1,2,3,4])
l
l.shape
l[:,np.newaxis]
l[:,np.newaxis].shape
l[np.newaxis,:].shape
#结果:(1, 4)
#绘制图像
plt.figure()
plt.scatter(x,y,s=20,edgecolor = 'black',c = 'darkorange',label = 'data')
plt.plot(x_test,y_1,color = 'cornflowerblue',label = "max_depth=2",linewidth = 2)
plt.plot(x_test,y_2,color = 'yellowgreen',label = "max_depth=5",linewidth = 2)
plt.xlabel('data')
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()
可以看到,如果树的最大深度(由max_depth参数控制)设置得太高,则决策树学习得太精细,他从训练数据中学了很多细节,包括噪声得呈现,从而使 模型偏离真实的正弦曲线,形成过拟合。
数据集:https://www.kaggle.com/c/titanic,下载不了可私信我吖~~
目标:通过分类树模型来预测哪些人可能成为幸存者
#导入所需要的库
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
import matplotlib.pyplot as plt
from sklearn.model_selection import GridSearchCV
#导入数据集,探索数据
data = pd.read_csv(r"C:\Users\86188\Desktop\coding\kaggle-titanic-master\input\train.csv")
#data = pd.read_csv("C:/Users/86188/Desktop/coding/kaggle-titanic-master/input/train.csv")
data.head(10)
data.info()
'''
结果:
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
'''
#筛选特征
data.drop(["Cabin","Name","Ticket"],inplace=True,axis=1)#删除列
#data = data.drop(["Cabin","Name","Ticket"],inplace = False)
data
#处理缺失值
data["Age"] = data["Age"].fillna(data["Age"].mean())#填补缺失值
data.info()
'''
结果:
RangeIndex: 891 entries, 0 to 890
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Sex 891 non-null object
4 Age 891 non-null float64
5 SibSp 891 non-null int64
6 Parch 891 non-null int64
7 Fare 891 non-null float64
8 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(2)
memory usage: 62.8+ KB
'''
data = data.dropna(axis=0)#删除缺失值,Embarked只有两个缺失值,可以直接删除
data.info()
'''
结果:
Int64Index: 889 entries, 0 to 890
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 889 non-null int64
1 Survived 889 non-null int64
2 Pclass 889 non-null int64
3 Sex 889 non-null object
4 Age 889 non-null float64
5 SibSp 889 non-null int64
6 Parch 889 non-null int64
7 Fare 889 non-null float64
8 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(2)
memory usage: 69.5+ KB
'''
labels = data['Embarked'].unique().tolist()
data["Embarked"] = data["Embarked"].apply(lambda x:labels.index(x))
data["Sex"] = (data["Sex"] == "male").astype("int")
data
x = data.iloc[:,data.columns != "Survived"]
#x
y = data.iloc[:,data.columns == "Survived"]
#以上将数据处理完毕,划分训练集和测试集
from sklearn.model_selection import train_test_split
Xtrain, Xtest, Ytrain, Ytest = train_test_split(x,y,test_size = 0.3)
#Ytrain
for i in [Xtrain, Xtest, Ytrain, Ytest]:
i.index = range(i.shape[0])
#Xtrain#对索引进行重新赋值
clf = DecisionTreeClassifier(random_state = 25)
clf = clf.fit(Xtrain,Ytrain)
score = clf.score(Xtest,Ytest)
score#结果不太好
#结果:0.704119850187266
#使用交叉验证
from sklearn.model_selection import cross_val_score
clf = DecisionTreeClassifier(random_state = 25)
score = cross_val_score(clf,x,y,cv = 10).mean()
score#效果还是不太好
#结果:0.7469611848825333
#调参,绘制学习曲线
import matplotlib.pyplot as plt
tr = []
te = []
for i in range(10):
clf = DecisionTreeClassifier(random_state = 25
,max_depth = i+1
,criterion = 'entropy')
clf = clf.fit(Xtrain,Ytrain)
score_tr = clf.score(Xtrain,Ytrain)
score_te = cross_val_score(clf,x,y,cv = 10).mean()
tr.append(score_tr)
te.append(score_te)
print(max(te))
#结果:0.8166624106230849
plt.plot(range(1,11),tr,color = "red",label = "train")
plt.plot(range(1,11),te,color = "blue",label = "train")
plt.xticks(range(1,11))
plt.legend()
plt.show()
import numpy as np
gini_threholds = np.linspace(0,0.5,20)#0.5是根据gini的定义确定的范围
#entropy_threholds = np.linspace(0,1,50)
#np.arange(0,1,0.1)
#网格搜索:能够帮助我们同时调整多个参数的技术,枚举技术
#一串参数和这些参数是对应的,希望网格搜索来搜索到参数的取值范围
parameters = {"criterion":("gini","entropy")
,"splitter":("best","random")
,"max_depth":[*range(1,10)]
,"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 = GS.fit(Xtrain,Ytrain)
GS.best_params_#从我们输入的参数和参数取值的列表中,返回最佳组合
'''
结果:
{'criterion': 'gini',
'max_depth': 7,
'min_impurity_decrease': 0.0,
'min_samples_leaf': 11,
'splitter': 'best'}
'''
GS.best_score_#网格搜索后的模型的评判标准
#结果:0.8313108038914491
#导入需要的库
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
from sklearn.datasets import make_moons,make_circles,make_classification
from sklearn.tree import DecisionTreeClassifier
#生成三种数据集
#从sklearn自带的数据库中生成三种类型的数据集:1)月亮型数据,2)环形数据,3)二分型数据
#make_classification库生成随机的二分型数据
X,y = make_classification(n_samples = 100 #生成100个样本
,n_features = 2 #包含2个特征,即生成二维数据
,n_redundant = 0 #添加冗余特征0个
,n_informative = 2 #包含信息的特征是2个
,random_state = 1 #随机模式1
,n_clusters_per_class = 1 #每个簇内包含的标签类别有1个
)
#查看一下X和y
#X#是100行带有2个特征的数据
#y#二分类标签
#画出散点图来观察一下X中特征的分布
plt.scatter(X[:,0],X[:,1])
#结果:
#上图显示,生成的二分型数据的两个簇离彼此很远,这样不利于测试分类器的效果,因此使用np生成随机数组,通过让已经生成的二分型数据点加减0-1之间的随机数,使数据分布变得更散更稀疏
#注意:这个过程只能运行一次,因为多次哦运行之后X会变得非常稀疏,两个簇的数据会混合在一起,分类器的效应会继续下降
rng = np.random.RandomState(2)#生成一直随机模式
X += 2 * rng.uniform(size = X.shape)#加减0-1之间的随机数
linearly_separable = (X,y)#生成新的X
plt.scatter(X[:,0],X[:,1])#画散点图来观察一下特征的分布
#结果:
#用make_moons创建月亮型数据,make_circles创建环型数据,并将三组数据打包起来放在列表datasets中
datasets = [make_moons(noise = 0.3,random_state = 0),
make_circles(noise = 0.2,factor = 0.5,random_state = 1),
linearly_separable]
datasets
len(datasets)
#结果:3
#画出三种数据集和三颗决策树的分类效应图像
#创建画布,宽高比为6*9
figure = plt.figure(figsize = (6,9))
#设置用来安排图像显示位置的全局变量i
i=1
#开始迭代数据,对datasets中的数据进行for循环
for ds_index,ds in enumerate(datasets):
#对X中的数据进行标准化处理,然后分训练集和测试集
X,y = ds
X = StandardScaler().fit_transform(X)
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.4,random_state = 42)
#找出数据集中两个特征的最大值和最小值,让最大值+0.5,最小值-0.5,创造一个比两个特征区间本身更大一点的区间
x1_min,x1_max = X[:,0].min()-0.5,X[:,0].max()+0.5
x2_min,x2_max = X[:,1].min()-0.5,X[:,1].max()+0.5
#用特征向量生成网格数据,网格数据其实就相当于坐标轴上无数个点
#函数np.arange在给定的两个数之间返回均匀间隔的值,0.2为步长
#函数meshgrid用以生成网格数据,能够将两个一维数组生成两个二维矩阵
#如果第一个数组是narray,维度是n,第二个参数是marray,维度是m,那么生成的第一个二维数组是以narray为行,m行的矩阵,而第二个二维数组是以marray的转置为列,n列的矩阵
#生成的网格数据,是用来绘制决策边界的,因为绘制决策边界的函数contourf要求输入的两个特征都必须是二维的
array1,array2 = np.meshgrid(np.arange(x1_min,x1_max,0.2),
np.arange(x2_min,x2_max,0.2))
#接下来生成彩色画布
#用ListedColormap为画布创建颜色,#FF0000正红,#0000FF正蓝
cm = plt.cm.RdBu
cm_bright = ListedColormap(['#FF0000','#0000FF'])
#给画布上加上一个子图,数据为len(datasets)行,2列,放在位置i上
ax = plt.subplot(len(datasets),2,i)
#到这里为止,已经生成了0-1之间的坐标系3个了,接下来为坐标系放上标题
#有三个坐标系,但只需要在第一个坐标系上有标题,因此设定if ds_index==0这个条件
if ds_index==0:
ax.set_title("Input data")
#将数据集的分布放到坐标系上
#先放训练集
ax.scatter(X_train[:,0],X_train[:,1],c=y_train,cmap = cm_bright,edgecolors = 'k')
#放测试集
ax.scatter(X_test[:,0],X_test[:,1],c=y_test,cmap = cm_bright,alpha = 0.6,edgecolors='k')
#为图设置坐标轴的最大值和最小值,并设定没有坐标轴
ax.set_xlim(array1.min(),array1.max())
ax.set_ylim(array2.min(),array2.max())
ax.set_xticks(())
ax.set_yticks(())
#每次循环之后,改变i的取值让图每次位列不同的位置
i += 1
#至此将数据集本身的图像布置完毕,运行以上代码可以看见三个已经处理好的数据集
#####以下为决策树模型######
#迭代决策树,首先用subplot增加子图,subplot(行,列,索引)这样的结构,并使用索引i定义图的位置
#在这里,len(datasets)其实就是3,2是两列
#在函数最开始,定义i=1,并且在上边建立数据集的图像的时候,已经让i+1,所以i在每次循环中的取值是2,4,6
ax = plt.subplot(len(datasets),2,i)
#决策树的建模过程:实例化->fit训练->score接口得到预测的准确率
clf = tree.DecisionTreeClassifier(max_depth = 5)
clf.fit(X_train,y_train)
score = clf.score(X_test,y_test)
#绘制决策树边界,为此,为网格中的每个点指定一种颜色[x1_min,x1_max] * [x2_min,x2_max]
#分类树的接口,predict_proba,返回每一个输入的数据点所对应的标签类概率
#类概率是数据点所在的叶节点中相同类的样本数量/叶节点中的样本总数量
#由于决策树在训练的时候导入的训练集x_train里面包含两个特征,所以在计算类概率的时候,也必须导入结构相同的数组,即是说,必须有两个特征
#ravel()能够将一个多维数组转换成一维数组
#np.c_是能够将两个数组组合起来的函数
#先将两个网格数据降维成一维数组,再将两个数组连接变成含有两个特征的数据,再代入决策树模型,生成的z包含数据的索引和每个样本点对应的类概率,再切片,切出类概率
Z = clf.predict_proba(np.c_[array1.ravel(),array2.ravel()])[:,1]
#np.c_[np.array([1,2,3]),np.array([4,5,6])]
#将返回的类概率作为数据,放到contourf里面绘制轮廓、
Z = Z.reshape(array1.shape)
ax.contourf(array1,array2,Z,cmap = cm,alpha = 0.8)
#将数据集的分布放到坐标系上
#将训练集放到图中去
ax.scatter(X_train[:,0],X_train[:,1],c=y_train,cmap = cm_bright,edgecolors = 'k')
#将测试集放到图中去
ax.scatter(X_test[:,0],X_test[:,1],c=y_test,cmap = cm_bright,edgecolors = 'k',alpha = 0.6)
#为图设置坐标轴的最大值和最小值
ax.set_xlim(array1.min(),array1.max())
ax.set_ylim(array2.min(),array2.max())
#设定坐标轴不显示标尺也不显示数字
ax.set_xticks(())
ax.set_yticks(())
#有三个坐标系,但只需要在第一个坐标系上有标题,因此设定if ds_index==0这个条件
if ds_index==0:
ax.set_title("Decision Tree")
#写在右下角的数字
ax.text(array1.max()-0.3,array2.min()+0.3,("{:.1f}%".format(score*100)),size=15,horizontalalignment = 'right')
#让i继续加1
i += 1
plt.tight_layout()
plt.show()
图中每一条线都是决策树在二维平面上画出的一条决策边界,每当决策树分枝一次,就有一条线出现,当数据的维度更高的时候,这条决策边界就会由线变成面,甚至变成想象不出的多维图形。
分类树天生不擅长环型数据,每个模型都有自己的决策上限,所以一个怎样调整都无法提升表现的可能也是有的。当一个模型怎么调整都不行的时候,可以选择换其他的模型使用。
最擅长月亮型数据的是最近邻算法、RBF支持向量机和高斯过程;最擅长环型数据的是最近邻算法和高斯过程;最擅长对半分的数据的是朴素贝叶斯、神经网络和随机森林。
参考: https://www.bilibili.com/video/BV1MA411J7wm?p=2&spm_id_from=pageDriver