CART算法中选取最优分裂属性的策略
需要说明的是CART是个二叉树,也就是当使用某个特征划分样本集合时只会有两个子集合:1. 等于给定的特征值的样本集合D1 ;2. 不等于给定的特征值的样本集合D2。CART二叉树实际上是对拥有多个取值的特征的二值处理。
首先对样本集合D包含的每个特征属性A根据其取值构造系列二分子集
然后计算D基于该属性A的每一种取值划分所获得的二分子集的Gini指数
然后选取Gini指数最小的属性取值作为最优划分点
重复上述过程
代码实现
参数介绍
sklearn.tree.DecisionTreeClassifier( criterion=’gini’, splitter=’best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
特征的重要性
前期我们也介绍了,特征选取的重要性,详情点击下面的文章即可查看详细的解说:
特征选取之单变量统计、基于模型选择、迭代选择
clf.feature_importances_ 返回特征的重要性 以后可以用决策树对特征进行选择
我通过反复的的测试和迭代,发现决策树选好特征数量,可以高效的提升模型的效果和各项评估指标,前面的KNN算法,我们通过了特征选取之后,反而对模型的效果造成了干扰,但是决策树不一样。
机器学习需要灵活的转变,不一定是按照某一种固定的思维来,比如网格搜索出来的参数一定是完美的吗?不一定哦,有时手动调参出来的参数,比网格搜索要好,因为网格搜索一般加入了一些不需要的参数进去。
参数splitter
splitter也是用来控制决策树中的随机选项的,有两种输入值:
输入”best",决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看)
输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。这也是防止过拟合的一种方式。
当你预测到你的模型会过拟合,用splitter和random_state这两个参数来帮助你降低树建成之后过拟合的可能性。
剪枝参数
在不加限制的情况下,一棵决策树会生长到衡量不纯度的指标最优,或者没有更多的特征可用为止。这样的决策树 往往会过拟合,这就是说,它会在训练集上表现很好,在测试集上却表现糟糕。我们收集的样本数据不可能和整体 的状况完全一致,因此当一棵决策树对训练数据有了过于优秀的解释性,它找出的规则必然包含了训练样本中的噪 声,并使它对未知数据的拟合程度不足。
为了让决策树有更好的泛化性,我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大,正确的剪枝策略是优化 决策树算法的核心。sklearn为我们提供了不同的剪枝策略:
max_depth:限制树的最大深度,超过设定深度的树枝全部剪掉
这是用得最广泛的剪枝参数,在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所 以限制树深度能够有效地限制过拟合。在集成算法中也非常实用。实际使用时,建议从=3开始尝试,看看拟合的效 果再决定是否增加设定深度。
min_samples_leaf & min_samples_split:
min_samples_leaf限定,一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分 枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生。一般搭配max_depth使用。这个参数的数量设置得太小会引 起过拟合,设置得太大就会阻止模型学习数据。一般来说,建议从=5开始使用。
min_samples_split限定,一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则 分枝就不会发生。
代码案例
导入第三方库
#导入所需要的包 from sklearn.metrics import precision_score from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier import numpy as np import pandas as pd import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from sklearn.preprocessing import LabelEncoder from sklearn.metrics import classification_report from sklearn.model_selection import GridSearchCV #网格搜索 import matplotlib.pyplot as plt#可视化 import seaborn as sns#绘图包
初次导入数据,不加入任何参数进行训练
# 加载模型 model = DecisionTreeClassifier() # 训练模型 model.fit(X_train,y_train) # 预测值 y_pred = model.predict(X_test) ''' 评估指标 ''' # 求出预测和真实一样的数目 true = np.sum(y_pred == y_test ) print('预测对的结果数目为:', true) print('预测错的的结果数目为:', y_test.shape[0]-true) # 评估指标 from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100)) print('预测数据的精确率为:{:.4}%'.format( precision_score(y_test,y_pred)*100)) print('预测数据的召回率为:{:.4}%'.format( recall_score(y_test,y_pred)*100)) # print("训练数据的F1值为:", f1score_train) print('预测数据的F1值为:', f1_score(y_test,y_pred)) print('预测数据的Cohen’s Kappa系数为:', cohen_kappa_score(y_test,y_pred)) # 打印分类报告 print('预测数据的分类报告为:','\n', classification_report(y_test,y_pred))
效果一般般,下面进行改进措施
手动对max depth 进行调参
def cv_score(d): clf = DecisionTreeClassifier(max_depth=d) clf.fit(X_train, y_train) return(clf.score(X_train, y_train), clf.score(X_test, y_test)) depths = np.arange(1,10) scores = [cv_score(d) for d in depths] tr_scores = [s[0] for s in scores] te_scores = [s[1] for s in scores] # 找出交叉验证数据集评分最高的索引 tr_best_index = np.argmax(tr_scores) te_best_index = np.argmax(te_scores) print("bestdepth:", te_best_index+1, " bestdepth_score:", te_scores[te_best_index], '\n') # 可视化 %matplotlib inline from matplotlib import pyplot as plt depths = np.arange(1,10) plt.figure(figsize=(6,4), dpi=120) plt.grid() plt.xlabel('max depth of decison tree') plt.ylabel('Scores') plt.plot(depths, te_scores, label='test_scores') plt.plot(depths, tr_scores, label='train_scores') plt.legend()
取到5和9的时候,看起来还不错,但是一般是不会取到9,因为可能会造成过拟合
下面对其中的某一个参数进行调优,主要是介绍这种思想
min_impurity_decrease调参 def minsplit_score(val): clf = DecisionTreeClassifier(criterion='gini',max_depth=5, min_impurity_decrease=val) clf.fit(X_train, y_train) return (clf.score(X_train, y_train), clf.score(X_test, y_test), ) # 指定参数范围,分别训练模型并计算得分 vals = np.linspace(0, 0.2, 100) scores = [minsplit_score(v) for v in vals] tr_scores = [s[0] for s in scores] te_scores = [s[1] for s in scores] bestmin_index = np.argmax(te_scores) bestscore = te_scores[bestmin_index] print("bestmin:", vals[bestmin_index]) print("bestscore:", bestscore) plt.figure(figsize=(6,4), dpi=120) plt.grid() plt.xlabel("min_impurity_decrease") plt.ylabel("Scores") plt.plot(vals, te_scores, label='test_scores') plt.plot(vals, tr_scores, label='train_scores') plt.legend()
这里看起来效果有了提升
通过手动调参,主要确定了两个参数的范围
网格搜索
import numpy as np from sklearn.model_selection import GridSearchCV parameters = {'splitter':('best','random') ,'criterion':("gini","entropy") ,"max_depth":[np.arange(4,10,1)] ,"max_depth":[*range(1,10)] ,'min_samples_leaf':[*range(1,50,5)] } clf = DecisionTreeClassifier(random_state=25) GS = GridSearchCV(clf, parameters, cv=5) # cv交叉验证 GS.fit(X_train,y_train) GS.best_params_
又确定了几个主要的参数
带入最佳参数进行训练
# 加载模型 model = DecisionTreeClassifier(criterion='gini',max_depth=5, splitter= 'random') # 训练模型 model.fit(X_train,y_train) # 预测值 y_pred = model.predict(X_test) ''' 评估指标 ''' # 求出预测和真实一样的数目 true = np.sum(y_pred == y_test ) print('预测对的结果数目为:', true) print('预测错的的结果数目为:', y_test.shape[0]-true) # 评估指标 from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100)) print('预测数据的精确率为:{:.4}%'.format( precision_score(y_test,y_pred)*100)) print('预测数据的召回率为:{:.4}%'.format( recall_score(y_test,y_pred)*100)) # print("训练数据的F1值为:", f1score_train) print('预测数据的F1值为:', f1_score(y_test,y_pred)) print('预测数据的Cohen’s Kappa系数为:', cohen_kappa_score(y_test,y_pred)) # 打印分类报告 print('预测数据的分类报告为:','\n', classification_report(y_test,y_pred))
效果还是一般,总的来说有了提升,但是不够明显,需要进一步
下面进行特征选取,在决策树里面,特征选取重要性还是比较的强,下面进行多方面的特征选取,看看是否可以提升模型的效果
决策树模型特征选取
feature_weight = model.feature_importances_ feature_name = df.columns[:-1] feature_sort = pd.Series(data = feature_weight ,index = feature_name) feature_sort = feature_sort.sort_values(ascending = False) plt.figure(figsize=(10,8)) sns.barplot(feature_sort.values,feature_sort.index, orient='h')