一、原理
1.什么是决策树
(1)由一个根节点,若干内部节点和若干叶节点构成;
(2)可以解决分类和回归问题;
(3)非参数学习算法。
2.决策树的目标
通过一种衡量标准,来计算通过不同特征进行分支选择后的分类情况,找出来最好的那个特征当成根节点,以此类推。
3.两大问题
(1)每个节点在哪个维度上做划分?
(2)某个维度在哪个值上做划分?
4.两大措施(衡量标准)
(1)信息熵entropy;
(2)基尼系数gini.
5.信息熵
熵在信息论中代表随机变量不确定性的度量。
熵越大,数据的不确定性越高。
6.基尼系数
在分类问题中,假设有k个类别,样本点属于第i类的概率为Pi, 则概率分布的基尼系数定义为:
7.决策树的最大挑战:过拟合
应对的措施:剪枝,降低过拟合风险,提高模型泛化能力。
(1)预剪枝(常用技术)
定义:边建立决策树,边进行剪枝;
方法:限制深度、叶子节点个数、叶子节点样本数、信息增益等
(2)后剪枝
定义:当建立完决策树后,再来进行剪枝操作。
方法:通过一定的衡量标准(计算叶子节点的损失)
二、实战
import pandas as pd import numpy as np from collections import Counter import pydotplus as pydotplus from sklearn.model_selection import train_test_split import sklearn.tree as tree from sklearn.model_selection import GridSearchCV import sklearn.metrics as metrics import graphviz from PIL import Image import sklearn.ensemble as ensemble # ensemble learning: 集成学习 print("###############################step1: 数据探索########################################### ") #print(" ") print("*******************************1. 加载数据集,查看数据信息************************************************") df = pd.read_csv("broadband.csv") pd.set_option('display.width', 1000)#加了这一行那表格的一行就不会分段出现了 pd.set_option('display.max_columns', None) #显示所有的列,配合前一行所有列可以显示在同一行 print(df.head()) print(df.info()) #需要括号 print("*******************************2. 把表的列名全部转换成小写************************************************ ") df.rename(str.lower, axis='columns', inplace=True) #把表的列名全部转换成小写(意义是什么???) inplace=True:原表中操作 print(df.sample()) #查看一个样本数据 print(" ") print("*******************************3. 查看因变量 broadband 分布情况,看是否存在不平衡**************************** ") print("Broadband:", Counter(df['broadband'])) print(" ") print("###############################step2: 拆分测试集和训练集########################################### ") y = df['broadband'] #标签 X = df.iloc[:, 1:-1] # 客户 id 没有用,故丢弃 cust_id 第一个冒号代表所有行 1:-1代表:正数第一列到倒数第一列(编号是从0开始的) print(X.sample()) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=123)# 40%分为测试集,random_state=123保证每次运行的结果是一样的 print("###############################step3: 构建决策树模型########################################### ") print("*******************************1. 直接使用交叉网格搜索来优化决策树模型,边训练边优化*****************************") print("*******************************网格搜索参数,选择最优参数*****************************") param_grid = {'criterion': ['entropy', 'gini'], # 树的深度评估指标 'max_depth': [2, 3, 4, 5, 6, 7, 8], # 树的深度 'min_samples_split': [4, 8, 12, 16, 20, 24, 28]} # 最小拆分的叶子样本数 print("*******************************2. 定义一棵树对象*****************************") clf = tree.DecisionTreeClassifier() print("***************3. 传入模型,网格搜索的参数,评估指标,cv交叉验证的次数****************") clfcv = GridSearchCV(estimator=clf, param_grid=param_grid, scoring='roc_auc', cv=4) #roc_auc评价一个模型好坏的指标, cv=4:交叉验证的次数 print("*******************************4. 训练模型*****************************") print(clfcv.fit(X_train, y_train)) print("*******************************5. 使用模型来对测试集进行预测*****************************") test_result = clfcv.predict(X_test) print(test_result) print("Test_Result:", Counter(test_result)) print("*******************************6. 模型评估*****************************") print("决策树准确度:") #参考意义并不大 print(metrics.classification_report(y_test, test_result)) print("*******************************7. 决策树的AUC*****************************") print("决策树 AUC:") #评估预测的准确性 fpr_test, tpr_test, th_test = metrics.roc_curve(y_test, test_result) #得到了三个值 print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test)) #只要前两个值 , print("*******************************8. 网格搜索后的最优参数*****************************") print(clfcv.best_params_) #有下划线代表一般是算出来的 print("*******************************9. 将最优参数代入到模型中,重新训练、预测*****************************") clf2 = tree.DecisionTreeClassifier(criterion='entropy', max_depth=5, min_samples_split=4) clf2.fit(X_train, y_train) test_res2 = clf2.predict(X_test) print("*******************************10. 绘制图形*****************************") #画图方法1: #https://blog.csdn.net/qq_41954204/article/details/100846162 配置graphviz环境 # dot_data = tree.export_graphviz(clf2, out_file=None) # # # graph = graphviz.Source(dot_data) # # # graph.render('use2') #画图方法2: # # 画图,保存到pdf文件 # # 设置图像参数 # dot_data = tree.export_graphviz(clf2, out_file=None) # graph = pydotplus.graph_from_dot_data(dot_data) # # # 保存图像到pdf文件 # graph.write_pdf("use3.pdf") print(" ") print("###############################step4: 随机森林建模########################################### ") print("*******************************1. 网格搜索*****************************") param_grid = { 'criterion':['entropy','gini'],# 衡量标准 'max_depth':[5, 6, 7, 8], # 每棵决策树的深度 'n_estimators':[11,13,15], # 决策树个数 - 随机森林特有参数 'max_features':[0.3,0.4,0.5], # 每棵决策树使用的变量占比 - 随机森林特有参数 'min_samples_split':[4,8,12,16] # 叶子的最小拆分样本量 } print("*******************************2. 随机森林算法*****************************") rfc = ensemble.RandomForestClassifier() rfc_cv = GridSearchCV(estimator=rfc, param_grid=param_grid, scoring='roc_auc', cv=4) print(rfc_cv.fit(X_train, y_train)) print("*******************************3. 使用随机森林对测试集进行预测*****************************") predict_test = rfc_cv.predict(X_test) print('随机森林精确度...') print(metrics.classification_report(predict_test, y_test)) print("*******************************4. AUC的值*****************************") print('随机森林 AUC...') fpr_test, tpr_test, th_test = metrics.roc_curve(predict_test, y_test) # 构造 roc 曲线 print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test)) print("*******************************5. 查看最佳参数(有可能不是最优参数,仍然需要调参)*****************************") print(rfc_cv.best_params_) print("*******************************6. 调整决策边界,调参*****************************") param_grid = { 'criterion':['entropy','gini'], 'max_depth':[7, 8, 10, 12], # 前面的 5,6 也可以适当的去掉,反正已经没有用了 'n_estimators':[11, 13, 15, 17, 19], # 决策树个数 - 随机森林特有参数 'max_features':[0.4, 0.5, 0.6, 0.7], # 每棵决策树使用的变量占比 - 随机森林特有参数 'min_samples_split':[2, 3, 4, 8, 12, 16] # 叶子的最小拆分样本量 } print("*******************************7. 复上述步骤,再次训练,寻找最优参数*****************************") rfc_cv = GridSearchCV(estimator=rfc, param_grid=param_grid, scoring='roc_auc', cv=4) rfc_cv.fit(X_train, y_train) print("*******************************8. 使用随机森林对测试集进行预测*****************************") predict_test = rfc_cv.predict(X_test) print('随机森林精确度...') print(metrics.classification_report(predict_test, y_test)) print('随机森林 AUC...') fpr_test, tpr_test, th_test = metrics.roc_curve(predict_test, y_test) # 构造 roc 曲线 print('AUC = %.4f' %metrics.auc(fpr_test, tpr_test)) print("*******************************9. 查看最优参数*****************************") print(rfc_cv.best_params_) """ 树模型参数: 1.criterion gini or entropy 2.splitter best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候) 3.max_features None(所有),log2,sqrt,N 特征小于50的时候一般使用所有的 4.max_depth 数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下 5.min_samples_split 如果某节点的样本数少于min_samples_split,则不会继续再尝试选择最优特征来进行划分如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。 6.min_samples_leaf 这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝,如果样本量不大,不需要管这个值,大些如10W可是尝试下5 7.min_weight_fraction_leaf 这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。 8.max_leaf_nodes 通过限制最大叶子节点数,可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制具体的值可以通过交叉验证得到。 9.class_weight 指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。 10.min_impurity_split 这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值则该节点不再生成子节点。即为叶子节点 。 11.n_estimators:要建立树的个数 """
三、那些遇到的坑
1.sklearn中决策树绘图包graphviz的安装教程及配置环境变量
graphviz是我们算法模块中比较重要的一个库,我们会使用它来对我们的重要算法:决策树进行绘图。通常来说graphviz都不太可能是自带的。Mac系统下安装graphviz相对简单,可以参考博文:https://blog.csdn.net/qq_36847641/article/details/78224910。Windows系统可以参考博文:https://blog.csdn.net/qq_41954204/article/details/100846162进行安装。
注:特殊的地方在于他要配置系统环境变量,配置完之后重启pycharm.
2.决策树绘图:
四、思考
决策树、随机森林是非参数学习算法,如何选择最优参数?
说明:
1.本文为个人学习笔记;
2.学习视频来源:https://space.bilibili.com/474347248/channel/detail?cid=143235
3.数据来源:唐国梁Tommy,为了方便志同道合的伙伴一起学习,我将数据上传到个人盘分享:
链接:https://pan.baidu.com/s/1YcQ8hljPYY7s0TWiV2lfZg
提取码:yb10
4.本文代码运行环境基于pycharm.(原代码是基于jupyter实现的);
5.代码的一些注释是为了理解,不太标准规范化,但不影响功能实现;
5.欢迎一起讨论学习:386825951@qq.com