决策树是非常流行的模型,支持分类和回归。决策树是高度可解释的,并为更复杂的算法,例如随机森林提供了基础。
![](http://dingyue.ws.126.net/2023/0426/46834822j00rtpm0o001vc000jg00ahm.jpg)
决策树的结构可以看作是一个有向无环图,是一个节点序列。这个图形是单向的,没有对象可以是其自身的子代。
看看上面的DAG,我们可以看到它从根节点开始,最好的属性变成内部节点,即决策节点。然后,内部节点检查条件并执行决策,将样本空间分为两部分。叶子节点代表一个分类,当记录到达叶子节点时,算法将相应的叶子分配标签。这个过程称为样本空间的递归划分。决策树的术语:
- 父节点-具有子节点的节点
- 子节点-父节点的孩子节点
- 根节点-表示将拆分为两个或多个集(子节点)的样本空间/总体
- 决策节点-分为多个子节点的节点(子节点)
- 叶节点-没有子节点的节点(子节点)
- 分支-决策树的一个子部分
- 剪枝-通过删除节点来减少决策树的大小
拆分标准
![](http://dingyue.ws.126.net/2023/0426/8eaadf58j00rtpm0o002yc000jg00cym.jpg)
决策树使用一些代价函数来选择最优分割。我们试图找到在分类数据方面表现最好的最佳属性/特征。这个过程一直重复,直到到达一个叶节点,因此被称为递归二元分割。
当执行这个过程时,所有的值都被排成一行,树将测试不同的拆分,并选择一个返回最低成本的拆分,这使得这是一个贪婪的方法。
需要注意的是,由于该算法将数据重复划分为更小的子集,因此最终的子集(叶节点)由几个或只有一个数据点组成。这使得算法具有低偏差和高方差。
熵与信息增益
一个广泛使用的决策树度量是熵。香农熵,以香农的名字命名,为我们提供了不确定性的度量。当涉及到数据时,熵告诉我们数据有多混乱。高熵值表示预测能力较低,可以将特征的熵视为该特征中的信息量。
决策树可以在进行拆分时最大限度地提高类的纯度,从而使叶节点中的类更加清晰。熵是在每次分割前后计算出来的。如果熵增加,将尝试另一次拆分。如果熵减小,分割将保持不变。计算整个数据集熵的公式:
![](http://dingyue.ws.126.net/2023/0426/faaa403bj00rtpm0o000ac000lo006qm.jpg)
式中,是簇的数目,()是属于第i个簇的概率。假设我们有一个包含462个正(1)标签和438个负(0)标签的数据集。我们可以通过以下方法计算数据集的熵:
class_1_count=462class_0_count=438prob_class_1=(class_1_count/(class_1_count+class_0_count))prob_class_0=(class_0_count/(class_1_count+class_0_count))entropy=(-prob_class_0)*np.log2(prob_class_0)-prob_class_1*np.log2(prob_class_1)#熵:0.9994869809508898
信息增益使用熵作为不纯度的度量。它是分割前和分割后的熵之差,它会给我们一个不确定性降低多少的数字。这也是ID3分类树算法中使用的关键准则,公式如下。
基尼不纯度
在执行分类任务时,使用基尼指数函数。这个函数告诉我们树中的叶节点有多“纯净”。基尼不纯度总是在0到0.5之间,值越高,簇越无序。公式如下:
![](http://dingyue.ws.126.net/2023/0426/eb1b44e9j00rtpm0o0008c000j8006em.jpg)
其中()是属于第i个簇的概率。上面的公式表明,基尼系数的不确定性是1减去每个分割中不同概率的总和。
决策树剪枝
![](http://dingyue.ws.126.net/2023/0426/a131b52bj00rtpm0o0014c000jg00czm.jpg)
当决策树通过递归进行训练时,我们还可以设置停止树的参数。决策树越复杂,越容易过拟合。我们可以使用超参数修剪树:
- max_depth-决定我们希望树有多深
- min_samples_leaf—每个叶节点中的最小训练样本数
- max_leaf_nodes-最大叶节点数
- min_impurity_decrease—阈值,以确定节点将分裂还是成为叶子
有更多可以更改的参数,有关列表和更详细的说明,请查看文档:https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html
使用Scikit-learn的决策树
让我们用sklearn构建一个决策树分类器。我将使用泰坦尼克号数据集,目标是Survived特征。我正在加载的数据集之前已经被清理过。有关数据集中特征的描述,请参阅下面的数据字典。
![](http://dingyue.ws.126.net/2023/0426/123e54a9j00rtpm0o0015c000q600eum.jpg)
导入必要的库
%load_extautoreload%autoreload2importosimportsysmodule_path=os.path.abspath(os.path.join(os.pardir,os.pardir))ifmodule_pathnotinsys.path:sys.path.append(module_path)importpandasaspdimportnumpyasnpimportmatplotlib.pyplotaspltimportseabornassnsimportwarningswarnings.filterwarnings('ignore')fromsklearn.metricsimportconfusion_matrix,plot_confusion_matrix,accuracy_scorefromsklearn.treeimportDecisionTreeClassifierfromsklearn.model_selectionimporttrain_test_split,cross_val_scorefromsklearn.preprocessingimportOneHotEncoder,StandardScalerfromsklearnimporttree
加载和预览数据集
df=pd.read_csv("titanic_cleaned.csv")df.head()
![](http://dingyue.ws.126.net/2023/0426/c6bc3fa8j00rtpm0o000kc000j2007km.jpg)
定义预测和目标特征,执行训练测试集分割,并对数据进行预处理
X=df.drop('Survived',axis=1)y=df.Survived#训练测试分割X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=42)#导入scaler和encoderfromsklearn.preprocessingimportStandardScaler,OneHotEncoder#在数据帧中定义数字和分类特征numerical=['Age','Fare']categorical=["Pclass","SibSp","Parch","male"]#定义训练和测试索引变量来转换数据帧中的数值X_train_index=X_train.indexX_test_index=X_test.index#实例化一个hotencoder并定义训练和测试ohe=OneHotEncoder()X_train_ohe=X_train[categorical]X_test_ohe=X_test[categorical]#拟合并转换X_train_encoded=ohe.fit_transform(X_train_ohe)X_test_encoded=ohe.transform(X_test_ohe)#实例化StandardScaler并定义连续变量ss=StandardScaler()X_train_cont=X_train[numerical].astype(float)X_test_cont=X_test[numerical].astype(float)#缩放连续特征并将结果转换为数据帧X_train_scaled=pd.DataFrame(ss.fit_transform(X_train_cont),columns=X_train_cont.columns,index=X_train_index)X_test_scaled=pd.DataFrame(ss.transform(X_test_cont),columns=X_test_cont.columns,index=X_test_index)#定义训练和测试的列train_columns=ohe.get_feature_names(input_features=X_train_ohe.columns)test_columns=ohe.get_feature_names(input_features=X_test_ohe.columns)#将已编码的X_train和X_test转换为数据帧X_train_processed=pd.DataFrame(X_train_encoded.todense(),columns=train_columns,index=X_train_index)X_test_processed=pd.DataFrame(X_test_encoded.todense(),columns=test_columns,index=X_test_index)#为预处理过的X_train和X_test组合编码和缩放的数据帧X_train=pd.concat([X_train_scaled,X_train_processed],axis=1)X_test=pd.concat([X_test_scaled,X_test_processed],axis=1)
训练决策树分类器
#用熵函数实例化决策树分类器以获取信息增益dtree=DecisionTreeClassifier(criterion='entropy')#拟合dtree.fit(X_train,y_train)fsm_train_acc=dtree.score(X_train,y_train)fsm_test_acc=dtree.score(X_test,y_test)print('BASELINETRAINACCURACY:{}\nBASELINETESTACCURACY:{}'.format(fsm_train_acc,fsm_test_acc))
决策树分类器在训练集上的性能优于测试集,表明模型过拟合。决策树容易过拟合,因为递归分割过程将一直持续到叶节点,从而导致模型过于复杂。在这里,我们将执行超参数调整和修剪以优化分类器。
绘制树
为了直观地看到拆分,绘制树可能会有所帮助。我们可以用几个额外的库来绘制这棵树。
fromsklearnimportexternalsfromsklearn.externals.siximportStringIOfromIPython.displayimportImagefromsklearn.treeimportexport_graphvizimportpydotplusdot_data=StringIO()export_graphviz(dtree,out_file=dot_data,filled=True,rounded=True,special_characters=True,feature_names=X_train.columns)graph=pydotplus.graph_from_dot_data(dot_data.getvalue())Image(graph.create_png())
![](http://dingyue.ws.126.net/2023/0426/679802bcj00rtpm0o006hc000jg0097m.jpg)
特征重要性
如果我们想检查模型的特征重要性,可以使用决策树分类器中的.feature_importances_属性。特征重要性使用基尼计算。
#离散列的one-hotencoded特征与.get_feature_names方法ohe_cols=list(ohe.get_feature_names(input_features=categorical))#数值列feats=list(numerical)feats.extend(ohe_cols)#使用explain_weights_df函数按升序包含特征重要性feat_imp=eli5.explain_weights_df(dtree,feature_names=feats)feat_imp
![](http://dingyue.ws.126.net/2023/0426/c627e75fj00rtpm0o001fc0008m00r8m.jpg)
热门跟贴