前置知识点:
- 数据的处理与清洗
- 交叉验证
- 网格搜索
1.数据处理:
- 查看数据信息:data.info() data.head()
- 将文字转为数字:我们的决策树的分类器只能处理数字,所以我们的标签的类型必须是数字,所以如果下面的特征是object的类型的话,都需要转换成数字才能够使用
- 筛选特征
- 处理缺失值
-
#1.导入所需要的库 import pandas as pd from sklearn.tree import DecisionTreeClassifier import matplotlib.pyplot as plt from sklearn.model_selection import GridSearchCV from sklearn.model_selection import train_test_split data=pd.read_csv(r"data.csv") # 文字转换为数字,年龄和船舱存在缺失值 data.info() # 数据的预处理 #筛选特征 #像name和ticket等不太容易转化为数字特征值的数据,并且和存活率也没有太大的关系的可以忽略这个特征 #像Cabin特征,由于缺失的太多,所以也可以忽略 #data.drop可以删除指定的数据,后面传入的是索引 #inplace=True,就是用删除完毕的表去覆盖原表,默认的是FALSE,也就是返回一张新的表,但不会修改原来的表 data.drop(["Cabin","Name","Ticket"],inplace=True,axis=1) # 处理缺失值 年龄一般均值 data["Age"] = data["Age"].fillna(data["Age"].mean()) # 对于缺失值数量太少的可以直接把所有的删除 data = data.dropna() # 把文字转为数字,决策树不能处理文字 # 把Embarked里的数据去重转为列表 labels=data["Embarked"].unique().tolist() # 用索引替代值--适用于值比较少的,也就是将S转换为0,C转换为1,Q转换为是2 data["Embarked"]=data["Embarked"].apply(lambda x: labels.index(x)) # 通过布尔转换数据类型 data["Sex"]=(data["Sex"]=="male").astype("int") data.info()
2.提取特征
# 取出数据,把幸存人数作为标签
x=data.iloc[:,data.columns!="Survived"]
y=data.iloc[:,data.columns=="Survived"]
# 划分测试集,训练集--重排索引
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])
3.跑模型:
# 跑模型
clf = DecisionTreeClassifier(random_state=25) # 实例化
clf=clf.fit(Xtrain,Ytrain)
score=clf.score(Xtest,Ytest)
score # 表现的不好
0.7340823970037453
优化模型:
# 优化模型
# 使用交叉验证来看看可不可以通过多次训练来提高预测分数
from sklearn.model_selection import cross_val_score
clf = DecisionTreeClassifier(random_state=25)
score = cross_val_score(clf,x,y,cv=10).mean() #做10次取平均值
score # 分数提高了但不多
0.7469611848825333
测试不同深度对分数的影响:
先看下面的画图在看这里:
我们可以接着尝试将criterion也就是不纯度的指标换成entropy试试看
但是之前说过拟合的时候不要使用entropy,为什么现在有要使用entropy呢?
因为我们这里的深度为3的这一点,过拟合还不是非常严重,我们把我们的这个指标换成entropy之后
因为这样训练集的分数在3这个点应该会增高,因为它对模型有了更好的拟合
但是测试集会如何变化我们是不知道的
# 优化--测试不同深度对于分数的影响
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))
画图分析一下:
分别绘制训练集和测试集的结果
为什么要同时绘制训练集和测试集的结果?
因为通过这样的数据对比,
如果我们训练集的曲线高于测试集的曲线很多,我们就知道模型是过拟合的:我们就需要剪枝操作
如果测试集上表现很好,也就是分数很高,但是训练集上表现非常糟糕,也就是分数很低,我们就知道是欠拟合的:我们就需要将模型更多地拟合到我们的训练集当中,从而修正我们的模型
plt.plot(range(1,11),tr,color="red",label="train")
plt.plot(range(1,11),te,color="blue",label="test")
#横坐标上显示1-10的整数
plt.xticks(range(1,11))
#图例
plt.legend()
#呈现出来
plt.show()
从下面的图中可以看到随着树的深度的加深,过拟合的情况越来越严重
其中在3这个深度应该是除了1以外过拟合最轻微的层数了
所以我们应该将最大深度取到3
4.网格搜索:
跑起来需要很久,因为他在试参数哪个可以优化模型
#第一步:实例化模型
clf=DecisionTreeClassifier(random_state=25)
#第二步:实例化网格搜索这个类,并填充参数
#第一个参数是我们想要运算的模型
#第二个参数是需要传入参数和参数取值范围的列表
import numpy as np
#linspace在你指定的范围内,取出你指定的数字个数字
#下面的代码就是在0-0.5之间取50个有顺序的随机数(从小到大排列,但是两个数之间的间隔不一定相同)
#我们将其命名为gini_threhold,也就是基尼系数边界的意思
#因为基尼系数的取值是0-0.5,
gini_threhold=np.linspace(0,0.5,50)
#当然也有信息熵的边界取值,范围是0-1
#entropy_threhold=np.linspace(0,1,50)
#定义parameters
#parameters本质上是一串参数和这些参数对应的我们希望网格搜索来搜索的参数的取值范围,也就是一个字典
parameters={
#格式:参数的名字,在加上参数的选项
#导入进去之后,网格搜索会帮助我们将所有的这些参数进行匹配,选出一个最好的
#帮criterion这个参数试试看基尼系数和信息熵
"criterion":("gini","entropy")
#帮splitter这个参数试试看best和random
,"splitter":("best","random")
#对于最大深度,一般是提供一个取值范围的列表
#这里的*是解包的作用,将range(1,10)中的数据提取出来,放入一个list列表中
,"max_depth":[*range(1,10)]
#一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生
#这里我们同样传入一个取值范围的列表,其是从1到50之间,以步长为5进行搜索,
# 也就是[1, 6, 11, 16, 21, 26, 31, 36, 41, 46]中哪一个参数更好
,"min_samples_leaf":[*range(1,50,5)]
#min_impurity_decrease信息增益的最小值,当信息增益小于min_impurity_decrease时
#决策树的当前结点将不再进行分支
#信息增益是父节点的信息熵减去子节点的信息熵,,但是要怎么才能确定这个信息增益要在什么程度呢?
#这个非常非常难去讨论,所以说这个min_impurity_decrease在不使用网格搜索的情况下是很难去使用的
#这里我们调用之前上面写的基尼系数的边界值
,"min_impurity_decrease":[*np.linspace(0,0.5,50)]
}
#第三个参数是想要交叉验证的参数
#这里的GridSearch是同时满足了fit,score和交叉验证的三种功能
GS=GridSearchCV(clf,parameters,cv=10)
#第三步:运算模型
#分别传入训练集的数据和标签
GS=GS.fit(Xtrain,Ytrain)