机器学习,决策树

机器学习,决策树,第1张

一、决策树

1.任意一个数据集的所有特征都可以拿来做分支,以特征作为节点,构建决策树,给数据集作分类。关键概念:全局最优、局部最优。以贪心算法实现局部最优达到接近全局最优结果的算法,所有的树模型都是此类算法。

2.常用算法:

1.ID3算法,引入了不纯度的概念,优化目标即使得叶节点总的不纯度最低。缺点:不能直接处理连续型变量,需离散化;对缺失值敏感,需提前对缺失值处理;没有剪枝的设置,容易过拟合。

2.C4.5算法&CART算法,C4,5:改善大概就是降低分类水平太多的特征的权重;增加了处理连续变量的手段。

3.KNN算法的假设:每一个特征对于推断的重要性一样,KNN最大的缺陷;决策树设计的初衷就是认为每个特征对于推断的重要性不一样;CART进一步认为,每个特征下的分类对于推断的重要性也不一样。

决策树的基本流程总结:1.计算全部特征的不纯度指标。2.选取不纯度指标最优的特征来分枝(补充概念:分类特征所占比例均衡分布的节点有最高的不纯性)。3.在第一个特征的分枝下,计算全部特征的不纯度指标。3.选取不纯度指标最优的特征继续分枝,无特征可用,决策树停止生长,分类结束。

二、sklearn

sklearn是机器学习里集成度非常高的模块,封装了很多的类,其中之一就是决策树,sklearn.tree模块,包含了五个类。重要的就是分类树和回归树

分类树

1.分类,但只是画一颗树,用了所有的特征

#coding:utf-8
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import pandas as pd
import graphviz #需要搭配tree模块的export_graphviz()模块使用
import os
os.environ["PATH"] += os.pathsep + 'E:/graphviz/graph/bin' #需按照决策树画图工具,自行百度,该语句是不用添环境变量
wine = load_wine()#实例化对象并传给wine变量,下载数据集
wine_data = wine.data #字典的内置属性,只将数据读取
wine_target = wine.target
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜色强度','色调','od280/od315稀释葡萄酒','脯氨酸']#将特征名字翻译成中文
#print(wine_data)
#print(wine_target)#0,1,2,三分类的数据集
print(wine_data.shape)#内置属性,无需参数
print(pd.concat([pd.DataFrame(wine_data),pd.DataFrame(wine_target)],axis=1))#在列的维度进行拼接
print(wine.feature_names)#特征的名字
print(wine.target_names)
Xtrain,Xtest,Ytrain,Ytest = train_test_split(wine_data,wine_target,test_size=0.3)#百分之三十的测试机
#print(Xtrain.shape)
#print(Ytrain)
'''
建立模型的三部曲,1.分类器实例化
2.将输入和输出的训练数据放入模型,就是clf
3.给模型打分,其实就是评价指标
'''
clf = tree.DecisionTreeClassifier(criterion="entropy")
clf = clf.fit(Xtrain,Ytrain)#fit为训练的接口,调用即可
score = clf.score(Xtest,Ytest)
'''
返回预测的准确度,也是直接调用接口score()
用测试集打分
'''
print(score)
dot_data = tree.export_graphviz(clf
                                ,feature_names = feature_name
                                ,class_names = ["琴酒","雪莉","贝尔摩德"]
                                ,filled=True
                                ,rounded=True)#让决策树按照这些信息画出决策树
'''
export_graphviz()需要注意的几个参数:
1.输入自己训练好的模型,也即是clf
2.输入特征名字,feature_name
3.标签名字,分的类
4.filled,是否给树填充颜色,颜色越深,不纯度越低
5.rounded,节点是否变方框
'''
graph = graphviz.Source(dot_data)#传参,传进上面的信息
#print(graph)
print(clf.feature_importances_)#告诉我们决策树选择了哪些特征,做了多大贡献。内置属性,调用即可
print([*zip(feature_name,clf.feature_importances_)])#将特征名字对应上

1.criterion计算不纯度或者表示不纯度

2.sklearn表示,既然一棵树不能保证最优,建不用的树选取最好的,每次分支时,不使用全部特征,随机选取不同特征,选取不纯度相关指标最优的作为分枝的节点,就能产生不同的树。引用新参数random_state保证随机性,此时准确率固定,即认为最优。但随即参数在高维度特征的数据集时作用明显,特征数越少越不明显,再怎么随机最优结果都相近。

clf = tree.DecisionTreeClassifier(criterion="entropy",random_state=30)

3.除了random_state,还有个参数:splitter,也是随机,两个输入值,一个是best,顾名思义就是优先选择更重要的特征,另一个是"random",一定程度上防止过拟合

clf = tree.DecisionTreeClassifier(criterion="entropy"
                                  ,random_state=30
                                  ,splitter="random")

4.但防止过拟合最重要的参数还是剪枝参数优化,比如max_depth、min_samples_leaf & min_samples_split、class_weight & min_weight_fraction_leaf等。但终归是调参,还是看数据和方法。

5.训练好模型还需要注意一些重要属性和接口,apply()和predict().

注意!!!!:所有接口中要求输入X_train和X_test的部分,输入的特征矩阵必须至少是一个二维矩阵。sklearn不接受任何一维矩阵作为特征矩阵被输入。如果你的数据的确只有一个特征,那必须用reshape(-1,1)来给矩阵增维;如果你的数据只有一个特征和一个样本,使用reshape(1,-1)来给你的数据增维。reshape(-1,1)是一维数据在行上做变化,reshape(1,-1)是一维数据在列上做变化

-1是指未设定行数,程序随机分配,所以这里-1表示任一正整数
所以reshape(-1,1)表示(任意行,1列),也就是一维变二维,[]变为[[]]

回归树

1.交叉验证是用来观察模型的稳定性的一种方法,我们将数据划分为n份,依次使用其中一份作为测试集,其他n-1份作为训练集,多次计算模型的精确性来评估模型的平均准确程度。训练集和测试集的划分会干扰模型的结果,因此用交叉验证n次的结果求出的平均值,是对模型效果的一个更好的度量。

简单实现

#coding:utf-8
from sklearn.datasets import load_boston #数据集,标签是连续的,不是离散,作回归
from sklearn.model_selection import cross_val_score #交叉验证分数,很直观
from sklearn.tree import DecisionTreeRegressor#决策树

boston = load_boston()
boston_data = boston.data
boston_target = boston.target
regressor = DecisionTreeRegressor(random_state=0)#1.依旧是实例化,将回归模型赋值给变量
print(cross_val_score(regressor,boston.data,boston.target,cv=10,scoring = "neg_mean_squared_error"))

'''
cross_val_score()有五个重要的参数“
1.任何一个实例化后的模型,regressor,不限于回归
2.不需要划分训练集和测试集的特征矩阵,完整数据,boston.data
因为交叉验证自动划分
3.完整的数据标签
4.cv=10,做10次交叉验证
5.scoring = "neg_mean_squared_error",评估做交叉验证的指标
模型指标
R平方越接近1越好
'''

 回归树拟合正弦曲线

#coding:utf-8
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
rng = np.random.RandomState(1)#命名成rng对象
'''
#生成随机数种子,固定添加的噪声,随机是指任意添加,
但每个数据加的噪声是一样的。
'''
#print(rng.rand(10))
#z = rng.rand(5,1)
X = np.sort(5 * rng.rand(80,1),axis=0)
Y = np.sin(X).ravel()
Y[::5] += 3 * (0.5-rng.rand(16))#隔5个取一个,共16个。生成16个随机数,用0.5减,因为0.5在中间
'''
#如果之间计算正弦,Y跟X的结构一样,二维,但只能是一维输出,就一个输出
所以ravel()降维函数变Y为一维,一维数组在python里不分行列,所以是80个数
'''
#print(Y.shape)
#print(z)
#print(Y)
#print(X)
'''
(80,1)是生成80个随机数,0到1之间,80行一列,弄成二维是
因为要放到回归树去拟合,sklearn的fit等接口不允许导入一维特征
*5是要将数放到0到5之间,sort()是升序函数
axis=0,为0轴,应该是指行,行作一个轴,在行的方向上作升序,也可以说
x轴
'''
#plt.figure()
#plt.scatter(X,Y,edgecolors="black",c="darkorange",label="data")#scatter()专门画散点图的
#plt.show()
'''
此为数据的分布,完美的正弦,但真实分布不可能
添加噪声模拟现实分布
'''
regr_1 = DecisionTreeRegressor(max_depth=2)#实例化对象,模型赋值给变量regr_1,参数是最大深度
regr_2 = DecisionTreeRegressor(max_depth=5)#两个模型是作对比,看不同的拟合效果,防止过拟合
regr_1.fit(X,Y)#放数据进模型训练
regr_2.fit(X,Y)
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)
plt.figure()
plt.scatter(X,Y,s=20,edgecolors="black",c="darkorange",label="data")
plt.plot(X_test,y_1,color="blue",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("DecisionTreeRegressor")
plt.legend()#显示图例
plt.show()

'''
#arange生成有序的数组
[:,np.newaxis],类切片功能,用来增维,升到二维放回归树,直接放后面可以了
放后面是行数变大,放前面是列数变大
.shape,内置属性,看是多少维的数据(4,)(4,1)等
'''

max_depth=5,深度太深过拟合,跟着噪声走了,没有拟合正弦曲线,所以剪枝参数可以有效防止过拟合。 

泰坦尼克

#coding:utf-8
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score #交叉验证分数,很直观
import matplotlib.pyplot as plt

data = pd.read_csv(r"D:\PycharmPro\sklearn\data.csv")#路径加r,转义字符,直接读取路径\
#print(data.info())#查看数据有哪几种类型,有对象,有整型。但分类器只能出来数字,机器只能处理数字
pd.set_option('display.max_columns', None)#显示全部列
#print(data.head())
#筛选特征,不是所有的特征都对分类有用
data.drop(['Cabin','Name','Ticket'],inplace=True,axis=1)
'''
删掉之后,inplace=True是原地 *** 作,直接修改原来的数据
表格,axis=1代表列,删除列
data.drop(['Cabin','Name','Ticket'],inplace=False)
'''
#print(data.head())
#处理缺失值,pandas里面的基本 *** 作
data["Age"] = data["Age"].fillna(data["Age"].mean())#fillna填充NaN,mean()均值填充
#print(data["Age"])
#print(data.info())
data = data.dropna()#dropna有返回值,需要重新赋值给变量
#data = data.dropna(axis=0)#参数是行,删除行。但默认删除行,两个语句一个意思
#print(data.info())
labels = data["Embarked"].unique().tolist()#统计特定数据有几种,重复的删掉,都保留一个
#print(labels.index("S"))
data["Embarked"] = data["Embarked"].apply(lambda x: labels.index(x))
'''
apply()函数,data["Embarked"]调用,让data["Embarked"]执行apply()括号的 *** 作
也就是将列表里所有的项转换成列表里的索引,lambda参数。
因为机器只能处理数字,索引将数据里面的内容(文字,字符等)转换成各自列表里的索引就相当于转换成了
数字,但仅限于没有关联性的数据,或者说没有顺序的数据
'''
data["Sex"] = (data["Sex"] == "male").astype(int)
#print((data["Sex"] == "male").astype(int))#把布尔值转换成数字,也就是0或1
#print(data["Sex"])
#print(data.head())#所有的都转换成数字,数据预处理完成
#提取标签和特征矩阵,将标签和特征矩阵分开。分测试集和训练集
x = data.iloc[:,data.columns != "Survived"]#
y = data.iloc[:,data.columns == "Survived"]
#print(y)
#print(x)
#print(data.columns)#所有列的标题
#print(data.columns != "Survived"),布尔型的列表
Xtrain, Xtest, Ytrain, Ytest = train_test_split(x,y,test_size=0.3)
#print(Xtrain)
#print(Xtrain.shape)
#Xtrain.index = range(Xtrain.shape[0])
'''
#shape[0],取数据表中的行数,0表示行
该语句就是重新将Xtrain的索引重新排列为0到shape[0]的有序数字
但有四个数据集,写个for循环.重新索引很有必要
'''
for i in [Xtrain, Xtest, Ytrain, Ytest]:#python解释器真的强
    i.index = range(i.shape[0])
#print(Ytrain)重新索引数据,处理完成!可以用sklearn了
'''
三部曲:1.分类器实例化
2。fit接口放入数据,参数是训练集
3.返回预测的准确率,评估指标,参数是测试集
'''
clf = DecisionTreeClassifier(random_state=25)
clf = clf.fit(Xtrain,Ytrain)
score = clf.score(Xtest,Ytest)
#print(score)
#交叉验证
clf = DecisionTreeClassifier(random_state=25)
score = cross_val_score(clf,x,y,cv=10).mean()#交叉验证求平均
#print(score)
tr = []
te = []#建立空列表,把下面循环测的分数保存
for i in range(10):
    clf = DecisionTreeClassifier(random_state=25
                                 ,max_depth=i+1)#依次增加深度
    clf = clf.fit(Xtrain,Ytrain)
    score_tr = clf.score(Xtrain,Ytrain)
    score_te = cross_val_score(clf,x,y,cv=10).mean()#会有10个分数,需要建空列表保存
    tr.append(score_tr)
    te.append(score_te)#append,列表的内置填充函数,得到两个列表,分别保存训练分数和测试分数

print(max(te))
plt.plot(range(1,11),tr,color="red",label="train")
plt.plot(range(1,11),te,color="blue",label="test")#两条曲线对比,看模型是过拟合还是欠拟合
plt.xticks(range(1,11))#x轴的刻度,1到10
plt.legend()#图例
plt.show()

 深度加深,训练过拟合,测试分数评委

在i=3,训练和测试重合,选取max_depth=3,再调参。加参数,criterion="entropy"交叉熵

 训练集变得平滑,交叉熵反而有用。

开始剪枝调参,将准确率提高,但是不能每次将训练分数和测试分数一直画出来弄,用到网格搜索,网格搜索可以很好的地看到是否过拟合或者欠拟合,效率更高地调参,明天更!!

#coding:utf-8
import pandas as pd
import numpy as np
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score #交叉验证分数,很直观
import matplotlib.pyplot as plt

data = pd.read_csv(r"D:\PycharmPro\sklearn\data.csv")#路径加r,转义字符,直接读取路径\
#print(data.info())#查看数据有哪几种类型,有对象,有整型。但分类器只能出来数字,机器只能处理数字
pd.set_option('display.max_columns', None)#显示全部列
#print(data.head())
#筛选特征,不是所有的特征都对分类有用
data.drop(['Cabin','Name','Ticket'],inplace=True,axis=1)
'''
删掉之后,inplace=True是原地 *** 作,直接修改原来的数据
表格,axis=1代表列,删除列
data.drop(['Cabin','Name','Ticket'],inplace=False)
'''
#print(data.head())
#处理缺失值,pandas里面的基本 *** 作
data["Age"] = data["Age"].fillna(data["Age"].mean())#fillna填充NaN,mean()均值填充
#print(data["Age"])
#print(data.info())
data = data.dropna()#dropna有返回值,需要重新赋值给变量
#data = data.dropna(axis=0)#参数是行,删除行。但默认删除行,两个语句一个意思
#print(data.info())
labels = data["Embarked"].unique().tolist()#统计特定数据有几种,重复的删掉,都保留一个
#print(labels.index("S"))
data["Embarked"] = data["Embarked"].apply(lambda x: labels.index(x))
'''
apply()函数,data["Embarked"]调用,让data["Embarked"]执行apply()括号的 *** 作
也就是将列表里所有的项转换成列表里的索引,lambda参数。
因为机器只能处理数字,索引将数据里面的内容(文字,字符等)转换成各自列表里的索引就相当于转换成了
数字,但仅限于没有关联性的数据,或者说没有顺序的数据
'''
data["Sex"] = (data["Sex"] == "male").astype(int)
#print((data["Sex"] == "male").astype(int))#把布尔值转换成数字,也就是0或1
#print(data["Sex"])
#print(data.head())#所有的都转换成数字,数据预处理完成
#提取标签和特征矩阵,将标签和特征矩阵分开。分测试集和训练集
x = data.iloc[:,data.columns != "Survived"]#
y = data.iloc[:,data.columns == "Survived"]
#print(y)
#print(x)
#print(data.columns)#所有列的标题
#print(data.columns != "Survived"),布尔型的列表
Xtrain, Xtest, Ytrain, Ytest = train_test_split(x,y,test_size=0.3)
gini_threholds = np.linspace(0,0.5,50)#linspace生成有序的随机数列表,arange有步长,等差数列
'''
gini_thretholds的取值是0到0.5,信息增益
entropy_threthods = np.linsppace(0,1,50),取值是0到1
'''
#参数字典
parameters = {'splitter':('best','random')
              ,'criterion':('gini','entropy')
              ,'max_depth':[*range(1,10)]
              ,'min_samples_leaf':[*range(1,50,5)]
              ,'min_impurity_decrease':[*np.linspace(0,0.5,20)]
              }#*号是解压的意思,将range(1,10)的数字释放出来
clf = DecisionTreeClassifier(random_state=25)#同样是实例化分类器
GS = GridSearchCV(clf,parameters,cv=10)
GS.fit(Xtrain,Ytrain)
'''
min_impurity_decrease,非常不好调,除非用网格搜索,否则一般不用这个参数
GridSearchCV(clf,parameters,cv=10)三个参数:
第一个是clf,分类器,将模型放进来
第二个是参数字典,按照这些参数一个个搜索去做训练,然后保存不同的模型
第三个是交叉验证,cv=10,10次
然后就是放进数据训练了
'''
print(GS.best_params_)#返回参数列表和参数取值的最佳组合
print(GS.best_score_)#网格搜索后模型的评判标准


输出:
{'criterion': 'entropy', 'max_depth': 3, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'splitter': 'best'}
0.8311827956989248

Process finished with exit code 0

跑了几分钟,比较慢,但效果还行,依然不建议,还是手动调参。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/717223.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-25
下一篇 2022-04-25

发表评论

登录后才能评论

评论列表(0条)

保存