利用sklearn提供的决策树实现简单的二分类任务并剪枝
实验报告
实验目的
自选数据集(例如lris数据集)实现决策树算法,并进行验证集评估
实验过程
数据分析与处理
选择使用简单的二分类数据集 breast_cancer,数据的采样方法为留出法,随机抽取20%的数据留做测试集。
-
数据集具体信息
数据规模: (569, 30)
feature name: ['mean radius', 'mean texture', 'mean perimeter', 'mean area', 'mean smoothness', 'mean compactness', 'mean concavity', 'mean concave points', 'mean symmetry', 'mean fractal dimension', 'radius error', 'texture error', 'perimeter error', 'area error', 'smoothness error', 'compactness error', 'concavity error', 'concave points error', 'symmetry error', 'fractal dimension error', 'worst radius', 'worst texture', 'worst perimeter', 'worst area', 'worst smoothness', 'worst compactness', 'worst concavity', 'worst concave points', 'worst symmetry', 'worst fractal dimension']
label name: ['malignant' 'benign']
训练集大小: (455, 30)
测试集大小: (114, 30)
决策树原理以及核心代码
-
原理:
利用信息增益,增益率,基尼系数等判断准则,通过一系列判断,希望将数据正确划分的方法
-
较为典型的算法
ID3算法:通过信息增益划分属性
C4.5算法: 通过增益率来划分属性
CATR算法:通过基尼指数来划分属性
-
核心代码
#数据准备 test_size = 0.2 cancer = load_breast_cancer() x = cancer.data y= cancer.target x_train,x_test,y_train,y_test = train_test_split(x,y,random_state=42,test_size=test_size) # print(cancer.DESCR) feature_name = cancer.feature_names name = feature_name.tolist() print("数据规模:",cancer.data.shape) print("label:",cancer.target) print("feature name:",name) print("label name:",cancer.target_names) print("训练集:",x_train.shape) print("测试集:",x_test.shape) #模型准备 id3_tree = DecisionTreeClassifier(criterion='entropy')#这里的模型主要有id3和cart两种算法, 将'entropy'改为'gini'可以使用CART算法 id3_tree.fit(x_train,y_train) #结果预测 predict = id3_tree.predict(x_test) print("准确率为 ",accuracy_score(y_test,predict)) print("测试集准确率:",id3_tree.score(x_train,y_train)) #决策树可视化 plt.figure(figsize=(30,20)) _=tree.plot_tree(c45_tree2,filled=True,feature_names=name) plt.show()
实验结果以及改进
实验结果
-
ID3算法准确率以及可视化结果
未剪枝前决策树的深度较大, 虽然在训练集上的正确率很高, 但是在测试集上的正确率不够理想, 有可能是模型发生了过拟合
-
CART算法的准确率和可视化结果
未剪枝前CART算法效果不如ID3算法, 和前者一样, 决策树的深度对于breast_cancer这样的小数据集来说有些大, 在训练集上拟合程度很高的同时, 在测试集上的表现效果不是非常理想, 初步怀疑是模型发生了过拟合
决策树的剪枝
通过以下三张方法进行对决策树的剪枝
限制最大深度
通过动态调整参数并画出相应参数下对应在测试集和训练集上正确率的图像
accu_score = [] train_score = [] for i in range(1,11): tree2 = tree.DecisionTreeClassifier(criterion='entropy',max_depth=i,splitter='best',random_state=30) tree2.fit(x_train,y_train) pre = tree2.predict(x_test) accu_score.append(accuracy_score(y_test,pre)) train_score.append(tree2.score(x_train,y_train)) fig,axes = plt.subplots(2,1,figsize=(20,15)) axes[0].plot(range(1,11),accu_score,label="max_depth") axes[0].set_ylabel("test_accuracy") axes[0].legend() axes[1].plot(range(1,11),train_score,label="max_depth") axes[1].set_ylabel("train_accuracy") axes[1].legend() plt.tight_layout() plt.show()
可以看到随着max_depth的逐渐上升,训练集上的正确率不断上升,而测试集上的正确率在max_depth=3之后下降,模型的泛化能力在max_depth=3之后逐步下降,过拟合现象发生
max_depth=3时决策树如图, 此时在测试集上正确率为0.9649122807017544, 训练集正确率为0.9802197802197802, 模型的泛化能力有了很大提升
- CART算法结果
当max_depth=3后在测试集上表现逐渐下降,但是当max_depth=3时模型在训练集上的效果仍然不太好,很难直观的从图像看出max_depth为多少时模型的效果最好
模型测试集准确率为0.9385964912280702 , 训练集准确率为: 0.9956043956043956, 虽然和剪枝前相比没有泛化性能上的提升, 但是整体结构确实简单了很多
其他参数调整的尝试--min_samples_leaf
仍然是动态参数调整, 对应相应正确率画出图像
accu_score = [] train_score = [] for i in range(1,21): tree3 = tree.DecisionTreeClassifier(criterion='entropy',max_depth=3,splitter='best',random_state=30,min_samples_leaf=i) tree3.fit(x_train,y_train) accu_score.append(tree3.score(x_test,y_test)) train_score.append(tree3.score(x_train,y_train)) fig,axes = plt.subplots(2,1,figsize=(20,15))
可以看到在1-21的区间范围内决策树在测试集上的准确率几乎没有任何提升,反而在训练集上的准确率逐渐下降
CART算法与ID3算法相似,调整min_leaf后在测试集上的表现并没有提高
后剪枝
sklearn中decisiontree提供了一些封装好的函数用来进行剪枝操作
通过cost_complexity_pruning_path方法获取 Cost-Complexity Pruning Path,即一系列的 alpha 值和对应的总不纯度。再用这些alpha值训练一系列决策树最终选择效果最好的决策树.
tree3 = tree.DecisionTreeClassifier(criterion='entropy',splitter='best',random_state=30) tree3.fit(x_train,y_train) path = tree3.cost_complexity_pruning_path(x_train,y_train) ccp_alphas, impurities = path.ccp_alphas,path.impurities trees = [] for alpha in ccp_alphas: tree3 = DecisionTreeClassifier(criterion='gini',ccp_alpha=alpha) tree3.fit(x_train,y_train) trees.append(tree3) trees = trees[:-1] ccp_alphas = ccp_alphas[:-1] train_accu = [tree.score(x_train,y_train) for tree in trees] test_accu = [tree.score(x_test,y_test) for tree in trees] index = np.argmax(test_accu) best_tree = trees[index]
可以看到在经过后剪枝后的决策树在测试集上的正确率有了较大的提升, 同时树的结构也较为简单, 这表明决策树的泛化能力有了很大提升,但同时模型的拟合程度有所下降。
CART算法决策树在经过后剪枝后, 在测试集上的正确率有了较大的提升, 同时树的结构也较为简单, 这表明决策树的泛化能力有了很大提升, 但是模型的拟合程度有所下降。
实验总结
- 没有实现决策树的前剪枝操作
- DecisionTreeClassifier(criterion='gini')中,criterion除了entropy和gini还有log_loss选择,但是这种算法产生的结果无论是正确率还是可视化决策树的图像都和entropy相同,查阅了一些资料,也没有找到较为合理的解释,个人认为log_loss可能作为交叉熵损失,在此处和信息增益的算法可能相同。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理