决战紫禁之巅 | sklearn参数介绍及使用

2019-07-19 19:22:02 浏览数 (1)

本篇将介绍决策树sklearn的使用,超参数的定义和用法,以一个简单的实战内容实现决策树的分类和回归实现。

▍sklearn决策树及超参数介绍

与参数模型(神经网络的权重,线性/逻辑回归的回归系数)不同,决策树模型是一种非参数模型,并且它不对数据有任何先验性假设。决策树模型既可以做分类,又可以做回归,在sklearn中,分类决策树和回归决策树模型是分开使用的,分别是:

  • 分类决策树:DecisionTreeClassifier
  • 回归决策树:DecisionTreeRegressor

两个模型的超参数大部分都相同,虽然超参一样,但是有些意义是不相同的,比如特征选择标准。下面通过sklearn的分类决策树模型的实践操作来了解各个参数的含义和使用方法。

数据采用对Taitanic清洗加工过后的干净数据集。由于特征工程已经做好,这里忽略。

接下来我们看看加入一些超参数控制会有什么变化。为了清楚地理解每个超参数的含义,我们先分开单独进行调试。

criterion

这个参数的含义是特征选择的标准

如果是分类模型,该参数可选 "gini" 基尼指数或者 "entropy" 熵两种。如果是回归模型,可以选 "mse" 均方差或者 "mae"均值差的绝对值和。

决策树分类模型默认使用 "gini",但大多数情况下选择 "gini" 与 "entropy" 并没有什么太大的区别。下面是分别使用 "gini" 和 "entropy" 进行建模得到的结果,一模一样。

代码语言:javascript复制
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(max_depth = 3)
clf.fit(X_train,y_train)
print('Accuracy using the defualt gini impurity criterion...',clf.score(X_test,y_test))

clf = DecisionTreeClassifier(max_depth = 3, criterion = "entropy")
clf.fit(X_train,y_train)
print('Accuracy using the entropy criterion...',clf.score(X_test,y_test))
---------------------------------------------------------------
Accuracy using the defualt gini impurity criterion... 0.8789237668161435
Accuracy using the entropy criterion... 0.8789237668161435

splitter

这个参数的含义是特征划分点选择标准

可以使用 "best" 或者 "random"。

  • best:在特征的所有划分点中找出最优的划分点。
  • random:随机的在部分划分点中找局部最优的划分点。
代码语言:javascript复制
t = time.time()
clf = DecisionTreeClassifier(max_depth = 3, splitter = 'best')
clf.fit(X_train,y_train)
print('Best Split running time...',time.time() - t)
print('Best Split accuracy...',clf.score(X_test,y_test))

t = time.time()
clf = DecisionTreeClassifier(max_depth = 3, splitter = 'random')
clf.fit(X_train,y_train)
print('Random Split running time...',time.time() - t)
print('Random Split accuracy...',clf.score(X_test,y_test))
-------------------------------------------------
Best Split running time... 0.03300189971923828
Best Split accuracy... 0.8789237668161435
Random Split running time... 0.003999948501586914
Random Split accuracy... 0.8834080717488789

本例中我们可以看到,随机地选择划分点会比选择所有划分点快很多。默认的 "best" 适合样本量不大的时候,而如果样本数据量非常大,且 "random" 效果也不差,此时决策树构建推荐 "random"。

max features

这个超参的含义是选择最有划分点是所考虑的最大特征数量。

可以使用很多种类型的值:

  • None:默认值,意味着划分时考虑所有的特征数。
  • log2:意味着划分时最多考虑 log2N个特征。
  • sqrt & auto:意味着划分时最多考虑√N个特征。
  • int:代表考虑的特征绝对数。
  • float:代表考虑特征百分比,即考虑(百分比xN)取整后的特征数。其中N为样本总特征数。

一般来说,如果样本特征数不多,比如小于50,我们用默认的"None"就可以了,如果特征数非常多,我们可以灵活使用刚才描述的其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。

代码语言:javascript复制
test_score = []
train_score = []

columns = list(dataset.columns)
columns.remove('Unnamed: 0')
columns.remove('Survived')

max_features = range(len(columns)-1)
for feat in max_features:
    clf = DecisionTreeClassifier(max_features = feat   1)
    clf.fit(X_train,y_train)
    train_score.append(clf.score(X_train,y_train))
    test_score.append(clf.score(X_test,y_test))

plt.figure(figsize = (8,5))
plt.plot(max_features,train_score,c='b')
plt.plot(max_features, test_score, c='r')
plt.xlabel('Max Features')
plt.ylabel('Accuracy')
plt.legend(['Training set','Test set'])
plt.show()

我们看到训练分数是恒定的并接近100%,而验证分数要低得多。这显然是一个过度拟合的情况。这是因为我们还没有限制树的深度。我们重复一下,加上限制树的深度。

max_depths

这个超参的含义是树模型的深度。

如果不加控制,那么决策树会自动地按规则学习全部数据,也就是树的深度会最大化。但我们知道这往往会造成过拟合,那么深度是多少才是最优的呢?我们且不管其他参数,先遍历深度的一个范围值1~32,通过深度与AUC分数的关系来判断深度的最优值位置。

从结果来看,随着树深度不断地增加,训练集分数可以达到接近100%(过度学习),而测试集分数则开始下降,甚至不稳定。很容易看到,从树深度为5的时候二者开始了有了偏差,也就是说如果树深度超过5,就会缺少泛化能力,发生过拟合现象,此时max_depths最优值可初定为5。

这时,我们重新建立一个分类决策树模型,并将树深度depth设为5,对比一下无任何深度限制的模型结果。

代码语言:javascript复制
from sklearn.tree import DecisionTreeClassifier
classifier0 = DecisionTreeClassifier()
classifier1 = DecisionTreeClassifier(max_depth=5)
classifier0.fit(X_train, y_train)
classifier1.fit(X_train, y_train)
y_pred0 = classifier0.predict(X_test)
y_pred1 = classifier1.predict(X_test)

from sklearn.metrics import roc_curve, auc
fpr0, tpr0, thresholds0 = roc_curve(y_test, y_pred0)
roc_auc0 = auc(fpr0,tpr0)
fpr1, tpr1, thresholds1 = roc_curve(y_test, y_pred1)
roc_auc1 = auc(fpr1,tpr1)
print("roc score is {0} when max_depth is None".format(roc_auc0))
print("roc score is {0} when max_depth is 5".format(roc_auc1))
-------------------------------------------
roc score is 0.8615964371360055 when max_depth is None
roc score is 0.8878896882494005 when max_depth is 5

很容易发现,depth为5的模型测试集roc分数更高,说明它有效地减缓了过拟合,增强了泛化能力。我们通过下面的分类效果对比会有更清楚的认识。

左边图树深度无任何限制,学习过于激进;右图树深度限制为5,较左图有很好的泛化能力。

min_samples_split

这个超参的含义是限制子树继续划分的条件。

如果节点样本数量少于这个值,那么节点就不进行划分。默认值为2,样本量不大的情况下,可以直接使用默认值。如果样本量数量级非常大,则推荐增大这个值。

从结果来看,随着样本比例不断增大,AUC分数越来越低,因为值越高,就会越早的阻止树向下生长,而过多的阻止向下生长就会引起欠拟合,导致分数很低。这里,训练集和测试集大约在样本比例为0.5的时候才有了偏差,之前都比较接近。我们希望的是二者在拟合接近的情况下AUC分数越高越好,所以此时可以选择样本比例较小的值来最为最优值。本例由于样本量不大,将使用默认值。

min_samples_leaf

这个超参的含义是限制叶子节点最少的样本数。

如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝。 默认是1,可以输入最少的样本数的整数,或者最少样本数占样本总数的百分比。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。

训练集和测试集基本能够保持一个趋势,随着数值增大,AUC分数下降发生欠拟合。由于数据集样本量不大,所以直接采用默认参数1。

min_weight_fraction_leaf

这个超参的含义是限制叶子节点所有样本权重和的最小值。

如果小于这个值,则会和兄弟节点一起被剪枝。默认是0,就是不考虑权重问题。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,就会引入样本权重,这时我们就要注意这个值了。

min_leaf_nodes

这个超参的含义是限制最大叶子节点数。

可以防止过拟合,默认是"None”,即不限制最大的叶子节点数。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值,但是如果特征分成多的话,可以加以限制,具体的值可以通过交叉验证得到。

明显的,从大概叶子节点数30起,测试集auc分数骤降,说明叶子节点过多,发生了过拟合。下面是depth为5的情况下,max_leaf_nodes分别为10和30的决策树分支可视化对比。

class_weight

这个超参的含义是指定样本各类别的的权重。

主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。这里可以自己指定各个样本的权重 {class_label: weight},或者用 “balanced”。

如果使用“balanced”,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。当然,如果你的样本类别分布没有明显的偏倚,则可以不管这个参数,选择默认的"None"。本例中,正负样本(生还和遇难)严重偏倚,因此选择使用 "balanced"。

本例中,我们将class_weight分别使用 "None" 和 "balanced" 在depth=4的情况下对比测试,结果如下:

代码语言:javascript复制
roc score is 0.8818088386433711 when class_weight is None
roc score is 0.8842925659472423 when class_weight is balanced

结果显示在样本类别偏倚的情况下使用 "balanced" 参数调节样本均衡可以提高auc的评估分数。

决策树回归模型中不使用该参数,因为目标不是类别。

GridSearch网格搜索最优超参

这几个超参数都是在其它参数不变的情况下进行的测试,为的是帮助大家了解每个参数的意义和作用,实际上所有参数是互相作用的,单独的存在说明不了什么。下面将所有超参数的范围做一个组合,然后找到所有组合中最优的一组。选择了两个比较重要的超参调节。

代码语言:javascript复制
from sklearn.model_selection import GridSearchCV

# 参数设置
params = {'max_leaf_nodes':[10,20,30],
          'max_depth':[3,5,8]}

gt1 = DecisionTreeClassifier(random_state=0, class_weight='balanced')

clf1 = GridSearchCV(gt1, param_grid=params, cv=3)
clf1.fit(X_train,y_train)
classifier1 = DecisionTreeClassifier(**clf1.best_params_) # 根据以上绘图结果选择一个合理参数值
classifier1.fit(X_train, y_train) # 训练模型
y_pred1 = classifier1.predict(X_test) # 预测测试集结果

fpr1, tpr1, thresholds1 = roc_curve(y_test, y_pred1)
roc_auc1 = auc(fpr1, tpr1)
roc_auc1
-----------
0.8854059609455294

然后我们绘制出roc的曲线图,【机器学习笔记】:一文让你彻底记住什么是ROC/AUC 如下所示:

▍sklearn决策树特征重要性

树模型有个非常好的功能,就是可以对特征的重要性比较,做排序。这也是源于特征选择的标准而产生的。很多时候,这个功能可以作为特征选择步骤中的初步筛选过程。即在海量特征面前,可以先通过树模型特征重要性初步筛选掉一部分特征,然后再精细筛选其它特征。

下面我们来看一下在sklearn中如何使用,sklearn中的特征重要性是feature_importance_属性。我们建立模型后直接调用即可,下面是特征重要性的可视化过程。

代码语言:javascript复制
fig, ax= plt.subplots(figsize=(10,8))

sns.barplot(classifier1.feature_importances_, features, color='g', ax=ax)
ax.set_title('feature_importance')
plt.show()

通过重要性比较,我们可以很清晰地看到哪些特征对我们是很重要的,哪些特征是可有可无的。

▍总结

本篇介绍了sklearn中决策树的超参数,以及如何使用这些超参数。希望结合理论篇的阅读可以加深对决策树的理解。下一篇将开启集成学习的入门和介绍。

参考: [1] https://www.cnblogs.com/pinard/p/6056319.html [2] https://www.kaggle.com/drgilermo/

0 人点赞