一、 Sklearn库
Scikit learn 也简称 sklearn, 自2007年发布以来,scikit-learn已经成为Python重要的机器学习库了。支持包括分类、回归、降维和聚类四大机器学习算法。还包含了特征提取、数据处理和模型评估三大模块。sklearn是Scipy的扩展,建立在NumPy和matplotlib库的基础上。利用这几大模块的优势,可以大大提高机器学习的效率。sklearn拥有着完善的文档,上手容易,具有着丰富的API,在学术界颇受欢迎。sklearn已经封装了大量的机器学习算法,包括LIBSVM和LIBINEAR。同时sklearn内置了大量数据集,节省了获取和整理数据集的时间。
SKlearn官网:http://scikit-learn.org/stable/index.html
上图是它的官网,从官网上看到Sklearn包含了很多种机器学习的方式,分别有:Classification 分类,Regression 回归,Clustering 非监督分类,Dimensionality reduction 数据降维,Model Selection 模型选择,Preprocessing 数据预处理
每个模块中有又包含了多种机器学习算法,
Ø 分类:识别某个对象属于哪个类别,常用的算法有:SVM(支持向量机)、nearest neighbors(最近邻)、random forest(随机森林),常见的应用有:垃圾邮件识别、图像识别。
Ø 回归:预测与对象相关联的连续值属性,常见的算法有:SVR(支持向量机)、 ridge regression(岭回归)、Lasso,常见的应用有:药物反应,预测股价。
Ø 聚类:将相似对象自动分组,常用的算法有:k-Means、 spectral clustering、mean-shift,常见的应用有:客户细分,分组实验结果。
Ø 降维:减少要考虑的随机变量的数量,常见的算法有:PCA(主成分分析)、feature selection(特征选择)、non-negative matrix factorization(非负矩阵分解),常见的应用有:可视化,提高效率。
Ø 模型选择:比较,验证,选择参数和模型,常用的模块有:grid search(网格搜索)、cross validation(交叉验证)、 metrics(度量)。它的目标是通过参数调整提高精度。
Ø 预处理:特征提取和归一化,常用的模块有:preprocessing,feature extraction,常见的应用有:把输入数据(如文本)转换为机器学习算法可用的数据。
由于帮助文档是英文,如下图,对于国内的编程人员说来说是个挑战,很遗憾到现在没有很好的中文版本的文档。
我们知道在工程应用中,用python手写代码来从头实现一个算法的可能性非常低,这样不仅耗时耗力,还不一定能够写出构架清晰,稳定性强的模型。更多情况下,是分析采集到的数据,根据数据特征选择适合的算法,在工具包中调用算法,调整算法的参数,获取需要的信息,从而实现算法效率和效果之间的平衡。而sklearn,正是这样一个可以帮助我们高效实现算法应用的工具包。
如果你有兴趣,并且想进一步深入学习机器学习算法,可以读取它的源码来学习。这里推荐两本书给大家
该书特点:以实例为重,给出了常用算法的伪代码,和《模式识别》、《模式分类》等专著比起来,该书略去了各个定理的证明部分,并通过大量枚举具体的分类实例,来简要说明算法的流程和意义。
任何想深入讨谈分类、回归、相关分析或者预测的问题,无论你想使用的是神经网络还是支持向量机,还是最原始的贝叶斯分类器,这本书都提供了一个比较完整的该领域的轮廓。
虽然对于具体的分类器和算法的分析浅尝辄止,但是该书最大的优点是能让初学者以最快的速度总览全局,掌握该领域的基本要点,而且该书对于数学和统计的相关概念,是我看过的相关书籍里要求最低的,高中生应该都能看懂的。
作者:http://cs.nju.edu.cn/zhouzh/
很著名的西瓜书,机器学习入门必备,书里举的很多例子都通俗易懂,特别是决策树章节关于西瓜特征分类的描述。很多算法论述都省略了大量细节,初学者想要靠它自行推导还是存在很大难度,大家别觉得恐怖,可以慢慢看多看几遍,才能体会到它的美感。
二、 决策树
1. 什么是决策树
决策树算法,人如其名,结构就像一棵树,有分叉的枝丫和树叶。枝丫的分叉处是关于目标某一个特征的判断,枝丫本体则是关于该特征的判断结果,而叶子则是判断过后产生的决策结果。
上图就是一个最为简单的分类树决策,当我们看天气预报时,根据降雨、雾霾、气温、活动范围是室内活动还是室外活动等等特征将自己的行为分类为出门和不出门。简单来说,决策树可以被看做由一大堆if-then的判断,每一条枝丫都是一条规则。
给决策树(Decision Tree)下个定义就是一种非参数的有监督学习方法,有监督学习就是需要输入标签的数据,它能够从一系列有特征和标签的数据中总结出决策规则。并用树状图的结构来呈现这些规则,以解决分类和回归问题。决策树算法容易理解,适用各种数据,在解决各种问题时都有良好表现,尤其是以树模型为核心的各种集成算法,在各个行业和领域都有广泛的应用。比如:金融行业可以用决策树做贷款风险评估
保险行业可以用决策树做险种推广预测
医疗行业可以用决策树生成辅助诊断处置模型,
游戏行业内的流失预测模型。通过回溯用户历史行为,选择登录间隔、游戏内停留时长甚至某一关卡是否过关等等,从而去判断究竟是哪一环节让用户对游戏失去兴趣,从而实施对策。
2. 决策树的工作过程
决策树是在机器学习中非常有用的一个模型。
我们简单了解一下决策树如何工作的,决策算法本质上是一种图的结构,我们只需要问一系列的问题就可以对数据进行分类了,比如说看看面的数据组,这是一系列已知的物种以及所属类别的数据,
前八列都是特征,第九列就是我们所谓的标签,上表是我们在机器学习中最常见的一种数据表格。决策树就是把这张表总结起来看起来非常简单的树(下图),来让我们做出非常简单的决策,
如我们的目标是将上表中的数据分为哺乳动物和非哺乳类,根据已经收集的(上表)数据,决策树算法为我们算出下面的这颗决策树。
决策树利用如上图所示的树结构进行决策,每一个非叶子节点是一个判断条件,每一个叶子节点是结论。从跟节点开始,经过多次判断得出结论。
下面我们用上面的数据按照这个树来不断提问,比如第五行的数据蛙,他是不是恒温,答案为不是,蛙就是非哺乳动物。再用猫举例,猫是否恒温,答案是恒温,它选择左侧的节点,问是否胎生,答案是,继续选择左侧的节点,答案是哺乳动物。
现在只要给你任一动物,你只要按照这个树,不断的提问就可以知道它是否是哺乳动物。
假设有一种新物种是java,它是恒温,体表带毛,并且是胎生,我们就可以通过这个巨册书判断它的所属类别。
我们可以看出,在这个决策过程中,我们是一只对记录的特征进行提问,最初的问题所在地方叫做根节点,在得到结论前的每一个问题都是中间节点,而得到的每一个结论(动物的类别)都叫做叶子结点。
知识点:
父节点和子节点是相对的,说白了子节点由父节点根据某一规则分裂而来,然后子节点作为新的父亲节点继续分裂,直至不能分裂为止。而根节点是没有父节点的节点,即初始分裂节点,叶子节点是没有子节点的节点,如下图所示:
决策树利用如上图所示的树结构进行决策,每一个非叶子节点是一个判断条件,每一个叶子节点是结论。从跟节点开始,经过多次判断得出结论。
决策树算法的核心解决两个问题:
Ø 如何从数据表中找出最佳节点和最佳分枝?
决策树是对特征提问,如何找出最佳节点和最佳分支,我怎么知道哪些特征提问,才能生成有效的树呢?
Ø 如何让决策树停止增长,防止过拟合?
如果我有无数个特征,决策树会长成什么样子,他是不是会长成无数层深,我们要怎么样让它停止成长呢?怎么样防止过拟合呢?
所有决策树相关模型调整的方法,都围绕这个问题展开,这两个问题背后的原理非常复杂,如果大家对里面的原理感兴趣建议学习相关数学知识。
3. Sklearn中的决策树
sklearn中决策树中的类都在tree这个模块下,这个模块包含5个类,不包括集成算法:
4. Sklearn的建模流程
建模编码流程:
Ø 导入所需库
Ø 建立评估模型对象
Ø 训练数据
Ø 通过模型提取所需信息,
对应流程代码:
from sklearn import tree #导入指定模块
clf = tree.DecisionTreeClassifier() #实例化
clf = clf.fit(a_train , b_train) #用训练的数据训练模型
result = clf.score(a_test , b_test) @导入测试集,从街口中调用需要的信息
只要记忆这个过程,编写就变得很简单了。
上面代码中score方法是提取我们需要的信息,他是导入测试集,然后可以得到这个模型的评估指标,默认是预测的精确性。
5. DecisionTreeClassifier重要参数
sklearn中决策树算法参数共有13个,如下:
class 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)
A. 重要参数:criterion
为了要将表格转化为一棵树,决策树需要找出最佳节点和最佳的分枝方法,对分类树来说,衡量这个“最佳”的指标叫做“不纯度”。通常来说,不纯度越低,决策树对训练集的拟合越好。现在使用的决策树算法在分枝方法上的核心大多是围绕在对某个不纯度相关指标的最优化上。
不纯度基于节点来计算,树中的每个节点都会有一个不纯度,并且子节点的不纯度一定是低于父节点的,这是有详细的数学证明的。也就是说,在同一棵决策树上,叶子节点的不纯度一定是最低的。
Criterion这个参数正是用来决定不纯度的计算方法的。sklearn提供了两种选择:
1)输入”entropy“,使用信息熵(Entropy)
2)输入”gini“,使用基尼系数(Gini Impurity)
下面是两个参数的计算方法。
其中t代表给定的节点,i代表标签的任意分类,p (i|t)代表标签分类i在节点t上所占的比例。如果你实在无法理解也不要紧,因为sklearn是不需要你懂的,哈哈。注意,当使用信息熵时,sklearn实际计算的是基于信息熵的信息增益(Information Gain),即父节点的信息熵和子节点的信息熵之差。
比起基尼系数,信息熵对不纯度更加敏感,对不纯度的惩罚最强。但是在实际使用中,信息熵和基尼系数的效果基本相同。就个人经验来说信息熵的计算比基尼系数缓慢一些,因为基尼系数的计算不涉及对数。另外,因为信息熵对不纯度更加敏感,所以信息熵作为指标时,决策树的生长会更加“精细”,更加具体到我们的训练值上,因此对于高维数据或者噪音很多的数据,信息熵很容易过拟合,基尼系数在这种情况下效果往往比较好。当然,这不是绝对的。
到这里,决策树的基本流程其实可以简单概括如下:
直到没有更多的特征可用,或整体的不纯度指标已经最优,决策树就会停止生长。
l 建立一棵树
#导入所需要的算法库和模块
from sklearn import tree
from sklearn.datasets import load_wine #用到sklearn自带的数据集,是红酒数据集
from sklearn.model_selection import train_test_split
(我用的环境是pycharm ,python3.7,macos)
#下面探索数据
wine = load_wine()
print(wine)
执行结果:
{'data': array([[1.423e+01, 1.710e+00, 2.430e+00, ..., 1.040e+00, 3.920e+00,
1.065e+03],
[1.320e+01, 1.780e+00, 2.140e+00, ..., 1.050e+00, 3.400e+00,
1.050e+03],
[1.316e+01, 2.360e+00, 2.670e+00, ..., 1.030e+00, 3.170e+00,
1.185e+03],
...,
[1.327e+01, 4.280e+00, 2.260e+00, ..., 5.900e-01, 1.560e+00,
8.350e+02],
[1.317e+01, 2.590e+00, 2.370e+00, ..., 6.000e-01, 1.620e+00,
8.400e+02],
[1.413e+01, 4.100e+00, 2.740e+00, ..., 6.100e-01, 1.600e+00,
5.600e+02]]), 'target': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2]), 'target_names': array(['class_0', 'class_1', 'class_2'], dtype='<U7'), 'DESCR': '.. _wine_dataset:nnWine recognition datasetn------------------------nn**Data Set Characteristics:**nn :Number of Instances: 178 (50 in each of three classes)n :Number of Attributes: 13 numeric, predictive attributes and the classn :Attribute Information:n tt- Alcoholn tt- Malic acidn tt- Ashntt- Alcalinity of ash n tt- Magnesiumntt- Total phenolsn tt- Flavanoidsn tt- Nonflavanoid phenolsn tt- Proanthocyaninsntt- Color intensityn tt- Huen tt- OD280/OD315 of diluted winesn tt- Prolinenn - class:n - class_0n - class_1n - class_2nttn :Summary Statistics:n n ============================= ==== ===== ======= =====n Min Max Mean SDn ============================= ==== ===== ======= =====n Alcohol: 11.0 14.8 13.0 0.8n Malic Acid: 0.74 5.80 2.34 1.12n Ash: 1.36 3.23 2.36 0.27n Alcalinity of Ash: 10.6 30.0 19.5 3.3n Magnesium: 70.0 162.0 99.7 14.3n Total Phenols: 0.98 3.88 2.29 0.63n Flavanoids: 0.34 5.08 2.03 1.00n Nonflavanoid Phenols: 0.13 0.66 0.36 0.12n Proanthocyanins: 0.41 3.58 1.59 0.57n Colour Intensity: 1.3 13.0 5.1 2.3n Hue: 0.48 1.71 0.96 0.23n OD280/OD315 of diluted wines: 1.27 4.00 2.61 0.71n Proline: 278 1680 746 315n ============================= ==== ===== ======= =====nn :Missing Attribute Values: Nonen :Class Distribution: class_0 (59), class_1 (71), class_2 (48)n :Creator: R.A. Fishern :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)n :Date: July, 1988nnThis is a copy of UCI ML Wine recognition datasets.nhttps://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.datannThe data is the results of a chemical analysis of wines grown in the samenregion in Italy by three different cultivators. There are thirteen differentnmeasurements taken for different constituents found in the three types ofnwine.nnOriginal Owners: nnForina, M. et al, PARVUS - nAn Extendible Package for Data Exploration, Classification and Correlation. nInstitute of Pharmaceutical and Food Analysis and Technologies,nVia Brigata Salerno, 16147 Genoa, Italy.nnCitation:nnLichman, M. (2013). UCI Machine Learning Repositoryn[http://archive.ics.uci.edu/ml]. Irvine, CA: University of California,nSchool of Information and Computer Science. nn.. topic:: Referencesnn (1) S. Aeberhard, D. Coomans and O. de Vel, n Comparison of Classifiers in High Dimensional Settings, n Tech. Rep. no. 92-02, (1992), Dept. of Computer Science and Dept. of n Mathematics and Statistics, James Cook University of North Queensland. n (Also submitted to Technometrics). nn The data was used with many others for comparing various n classifiers. The classes are separable, though only RDA n has achieved 100% correct classification. n (RDA : 100%, QDA 99.4%, LDA 98.9%, 1NN 96.1% (z-transformed data)) n (All results using the leave-one-out technique) nn (2) S. Aeberhard, D. Coomans and O. de Vel, n "THE CLASSIFICATION PERFORMANCE OF RDA" n Tech. Rep. no. 92-01, (1992), Dept. of Computer Science and Dept. of n Mathematics and Statistics, James Cook University of North Queensland. n (Also submitted to Journal of Chemometrics).n', 'feature_names': ['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium', 'total_phenols', 'flavanoids', 'nonflavanoid_phenols', 'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline']}
上面可以看到输出结果是一个字典,(大括号如果不理解,请看一些python基础教程),有标签,有特征。
#只是输出特征
print(wine.data)
输出结果:
[[1.423e+01 1.710e+00 2.430e+00 ... 1.040e+00 3.920e+00 1.065e+03]
[1.320e+01 1.780e+00 2.140e+00 ... 1.050e+00 3.400e+00 1.050e+03]
[1.316e+01 2.360e+00 2.670e+00 ... 1.030e+00 3.170e+00 1.185e+03]
...
[1.327e+01 4.280e+00 2.260e+00 ... 5.900e-01 1.560e+00 8.350e+02]
[1.317e+01 2.590e+00 2.370e+00 ... 6.000e-01 1.620e+00 8.400e+02]
[1.413e+01 4.100e+00 2.740e+00 ... 6.100e-01 1.600e+00 5.600e+02]]
#只是输出标签
print(wine.target)
输出结果:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 22 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
#我们来输出一张表看看,需要用到pandas库
import pandas as pd
#把数据标签和数据特征变成两张表后,把用concat把他们结合起来,因为是横向链接,所以axis=1
print(pd.concat([pd.DataFrame(wine.data),pd.DataFrame(wine.target)],axis=1))
输出结果:0 1 2 3 4 ... 9 10 11 12 0
0 14.23 1.71 2.43 15.6 127.0 ... 5.640000 1.04 3.92 1065.0 0
1 13.20 1.78 2.14 11.2 100.0 ... 4.380000 1.05 3.40 1050.0 0
2 13.16 2.36 2.67 18.6 101.0 ... 5.680000 1.03 3.17 1185.0 0
3 14.37 1.95 2.50 16.8 113.0 ... 7.800000 0.86 3.45 1480.0 0
4 13.24 2.59 2.87 21.0 118.0 ... 4.320000 1.04 2.93 735.0 0
5 14.20 1.76 2.45 15.2 112.0 ... 6.750000 1.05 2.85 1450.0 0
6 14.39 1.87 2.45 14.6 96.0 ... 5.250000 1.02 3.58 1290.0 0
7 14.06 2.15 2.61 17.6 121.0 ... 5.050000 1.06 3.58 1295.0 0
8 14.83 1.64 2.17 14.0 97.0 ... 5.200000 1.08 2.85 1045.0 0
9 13.86 1.35 2.27 16.0 98.0 ... 7.220000 1.01 3.55 1045.0 0
10 14.10 2.16 2.30 18.0 105.0 ... 5.750000 1.25 3.17 1510.0 0
11 14.12 1.48 2.32 16.8 95.0 ... 5.000000 1.17 2.82 1280.0 0
12 13.75 1.73 2.41 16.0 89.0 ... 5.600000 1.15 2.90 1320.0 0
13 14.75 1.73 2.39 11.4 91.0 ... 5.400000 1.25 2.73 1150.0 0
14 14.38 1.87 2.38 12.0 102.0 ... 7.500000 1.20 3.00 1547.0 0
15 13.63 1.81 2.70 17.2 112.0 ... 7.300000 1.28 2.88 1310.0 0
16 14.30 1.92 2.72 20.0 120.0 ... 6.200000 1.07 2.65 1280.0 0
17 13.83 1.57 2.62 20.0 115.0 ... 6.600000 1.13 2.57 1130.0 0
18 14.19 1.59 2.48 16.5 108.0 ... 8.700000 1.23 2.82 1680.0 0
19 13.64 3.10 2.56 15.2 116.0 ... 5.100000 0.96 3.36 845.0 0
20 14.06 1.63 2.28 16.0 126.0 ... 5.650000 1.09 3.71 780.0 0
21 12.93 3.80 2.65 18.6 102.0 ... 4.500000 1.03 3.52 770.0 0
22 13.71 1.86 2.36 16.6 101.0 ... 3.800000 1.11 4.00 1035.0 0
23 12.85 1.60 2.52 17.8 95.0 ... 3.930000 1.09 3.63 1015.0 0
24 13.50 1.81 2.61 20.0 96.0 ... 3.520000 1.12 3.82 845.0 0
25 13.05 2.05 3.22 25.0 124.0 ... 3.580000 1.13 3.20 830.0 0
26 13.39 1.77 2.62 16.1 93.0 ... 4.800000 0.92 3.22 1195.0 0
27 13.30 1.72 2.14 17.0 94.0 ... 3.950000 1.02 2.77 1285.0 0
28 13.87 1.90 2.80 19.4 107.0 ... 4.500000 1.25 3.40 915.0 0
29 14.02 1.68 2.21 16.0 96.0 ... 4.700000 1.04 3.59 1035.0 0
.. ... ... ... ... ... ... ... ... ... ... ..
148 13.32 3.24 2.38 21.5 92.0 ... 8.420000 0.55 1.62 650.0 2
149 13.08 3.90 2.36 21.5 113.0 ... 9.400000 0.57 1.33 550.0 2
150 13.50 3.12 2.62 24.0 123.0 ... 8.600000 0.59 1.30 500.0 2
151 12.79 2.67 2.48 22.0 112.0 ... 10.800000 0.48 1.47 480.0 2
152 13.11 1.90 2.75 25.5 116.0 ... 7.100000 0.61 1.33 425.0 2
153 13.23 3.30 2.28 18.5 98.0 ... 10.520000 0.56 1.51 675.0 2
154 12.58 1.29 2.10 20.0 103.0 ... 7.600000 0.58 1.55 640.0 2
155 13.17 5.19 2.32 22.0 93.0 ... 7.900000 0.60 1.48 725.0 2
156 13.84 4.12 2.38 19.5 89.0 ... 9.010000 0.57 1.64 480.0 2
157 12.45 3.03 2.64 27.0 97.0 ... 7.500000 0.67 1.73 880.0 2
158 14.34 1.68 2.70 25.0 98.0 ... 13.000000 0.57 1.96 660.0 2
159 13.48 1.67 2.64 22.5 89.0 ... 11.750000 0.57 1.78 620.0 2
160 12.36 3.83 2.38 21.0 88.0 ... 7.650000 0.56 1.58 520.0 2
161 13.69 3.26 2.54 20.0 107.0 ... 5.880000 0.96 1.82 680.0 2
162 12.85 3.27 2.58 22.0 106.0 ... 5.580000 0.87 2.11 570.0 2
163 12.96 3.45 2.35 18.5 106.0 ... 5.280000 0.68 1.75 675.0 2
164 13.78 2.76 2.30 22.0 90.0 ... 9.580000 0.70 1.68 615.0 2
165 13.73 4.36 2.26 22.5 88.0 ... 6.620000 0.78 1.75 520.0 2
166 13.45 3.70 2.60 23.0 111.0 ... 10.680000 0.85 1.56 695.0 2
167 12.82 3.37 2.30 19.5 88.0 ... 10.260000 0.72 1.75 685.0 2
168 13.58 2.58 2.69 24.5 105.0 ... 8.660000 0.74 1.80 750.0 2
169 13.40 4.60 2.86 25.0 112.0 ... 8.500000 0.67 1.92 630.0 2
170 12.20 3.03 2.32 19.0 96.0 ... 5.500000 0.66 1.83 510.0 2
171 12.77 2.39 2.28 19.5 86.0 ... 9.899999 0.57 1.63 470.0 2
172 14.16 2.51 2.48 20.0 91.0 ... 9.700000 0.62 1.71 660.0 2
173 13.71 5.65 2.45 20.5 95.0 ... 7.700000 0.64 1.74 740.0 2
174 13.40 3.91 2.48 23.0 102.0 ... 7.300000 0.70 1.56 750.0 2
175 13.27 4.28 2.26 20.0 120.0 ... 10.200000 0.59 1.56 835.0 2
176 13.17 2.59 2.37 20.0 120.0 ... 9.300000 0.60 1.62 840.0 2
177 14.13 4.10 2.74 24.5 96.0 ... 9.200000 0.61 1.60 560.0 2
最后一列是标签,前面列都是我们的特征。
#查看特征值
print(wine.feature_names)
输出结果:
['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium', 'total_phenols', 'flavanoids', 'nonflavanoid_phenols', 'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline']
#输出标签的名字
print(wine.target_names)
输出结果:
['class_0' 'class_1' 'class_2']
#split方法是把训练集分拆为两个数据集,一个是测试集,一个是训练集,size=0.3意思是说我们用30%是测试集,70%作为我们的训练集,
xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)
#输出训练集
print(xtrain.shape)
输出结果:
(124,13)
意思是124行,13个标签
#输出原有的数据集
print(wine.data.shape)
输出结果:
(178,13)
上面数据说明178的百分之30,就是124.
#下面建模,可以参考前面的建模流程
clf=tree.DecisionTreeClassifier(criterion="entropy")
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度print(score)
输出结果:
0.9444444444444444 #结果还不错
#下面要用到graphviz这个库,如果不熟悉可以参看我另外一篇文章(机器学习之数据的可视化:https://zhuanlan.zhihu.com/p/58839149)
import graphviz# 自定义特征名字
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']#class_name我们知道有三类,就赋值为["琴酒", "雪莉", "贝尔摩德"]
dot_data = tree.export_graphviz(clf, feature_names=feature_name,class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True,rounded=True)
# filled=True 填充颜色
# rounded=True 圆弧矩形graph = graphviz.Source(dot_data)
graph.view()
输出结果:
注意:export_graphviz是如何影响这里输出的树呢?
比如说不输入feature_names=feature_name参数,代码修改如下:
dot_data = tree.export_graphviz(clf, , class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True,rounded=True)
输出结果:
可以看到最上面的节点写的是x[6],不是特征名称只是数组索引了,
这是因为feature_names中保存的是中文特征名称。
Class_names影响的是标签的中文名称,filled是所有的节点用颜色标示,
round参数影响输出节点为是否为方形。
技巧:
在写代码过程中,如果有多个参数,建议把逗号写在前面,如:
dot_data = tree.export_graphviz(clf, class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True
,rounded=True)
这样注释代码会很便捷。
#下面我们继续探索
print(clf.feature_importances_)输出结果:
[0.02010349 0. 0. 0. 0. 0.100378690. 0. 0. 0.28330831 0.03496086 0.1192440.44200465]
上面的特征中说明只有6个有数据,只有这6个特征用到了,数值越大越重要,就是对上面的树越重要
#特征重要性和特征对应起来,用*zip函数,*zip()函数是zip()函数的逆过程,将zip对象变成原先组合前的数据。
print(*zip(feature_name,clf.feature_importances_))
输出的结果:
('酒精', 0.08104609046276126) ('苹果酸', 0.0) ('灰', 0.0) ('灰的碱性', 0.0) ('镁', 0.0) ('总酚', 0.0) ('类黄酮', 0.45851091902235463) ('非黄烷类酚类', 0.0) ('花青素', 0.0) ('颜 色强度', 0.09440490422199845) ('色调', 0.0) ('od280/od315稀释葡萄酒', 0.0) ('脯氨酸', 0.3660380862928856)
从上面的数据看出,我们可以把那些0的特征树去掉,也不会对结果有任何影响,对于上面的树影响最大的,数值也会越大。
通过上面的实际例子,我们已经在只了解一个参数的情况下,建立一棵完整的决策树,但是由于score会在某个值附近波动,结果画出的树会每次都不同,
clf=tree.DecisionTreeClassifier(criterion="entropy")
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
上面这段代码多次运行你也会发现结果会不同,上面的测试集和训练集是随机分离的。
其实,无论决策树模型如何进化,在分枝上的本质都还是追求某个不纯度相关的指标的优化,而正如我们提到的,不纯度是基于节点来计算的,也就是说,决策树在建树时,是靠优化节点来追求一棵优化的树,但最优的节点能够保证最优的树吗?集成算法被用来解决这个问题:sklearn表示,既然一棵树不能保证最优,那就建更多的不同的树,然后从中取最好的。怎样从一组数据集中建不同的树?在每次分枝时,不从使用全部特征,而是随机选取一部分特征,从中选取不纯度相关指标最优的作为分枝用的节点。这样,每次生成的树也就不同了。
如果要让数据输出很稳定,可以用random_state这个参数,
clf=tree.DecisionTreeClassifier(criterion="entropy",random_state=30)
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)
决策树是带有随机性的,虽然很多人可能没有意识到这个问题,sklearn官网也是用莺尾花作为案例,但是莺尾花只有4个特征,在如何随机也是生成不同的树概率也是非常小的,导致很多人没有意识到这个问题。
B. 重要参数: random_state & splitter
random_state用来设置分枝中的随机模式的参数,默认None,在高维度时随机性会表现更明显,低维度的数据(比如鸢尾花数据集),随机性几乎不会显现。输入任意整数,会一直长出同一棵树,让模型稳定下来。
splitter也是用来控制决策树中的随机选项的,有两种输入值,输入”best",决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看),输入“random",决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。这也是防止过拟合的一种方式。当你预测到你的模型会过拟合,用这两个参数来帮助你降低树建成之后过拟合的可能性。当然,树一旦建成,我们依然是使用剪枝参数来防止过拟合。
代码如下:
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
wine = load_wine()
xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)
clf=tree.DecisionTreeClassifier(criterion="entropy",random_state=30,splitter="random")
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)
import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf
, feature_names=feature_name
, class_names=["琴酒", "雪莉", "贝尔摩德"]
, filled=True
, rounded=True
)
graph = graphviz.Source(dot_data)
graph.view()
输出结果:
你会发现层次加深,说明数据随机性非常友好,提升了模型的效果。如果有效的参数就保留,因为我们永远追求的是最好的结果,最高的分数。
C. 剪枝参数
在不加限制的情况下,一棵决策树会生长到衡量不纯度的指标最优,或者没有更多的特征可用为止。这样的决策树往往会过拟合,过拟合这就是说,它会在训练集上表现很好,在测试集上却表现糟糕。我们收集的样本数据不可能和整体的状况完全一致,因此当一棵决策树对训练数据有了过于优秀的解释性,它找出的规则必然包含了训练样本中的噪声,并使它对未知数据的拟合程度不足。
下面我们训练集训练出来的树对测试数据集的拟合程度,代码如下:
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
wine = load_wine()
xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)
clf=tree.DecisionTreeClassifier(criterion="entropy",random_state=30,splitter="random")
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)
import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf
, feature_names=feature_name
, class_names=["琴酒", "雪莉", "贝尔摩德"]
, filled=True
, rounded=True
)
graph = graphviz.Source(dot_data)
#graph.view()
score_train = clf.score(xtrain,ytrain)
print(score_train)
输出结果:
1.0
结果说明精确性是1.0,这是一个过拟合的状况,因为在训练集上的效果明显比测试集效果好,我们测试集上的分数有94%,如果这个训练树在测试也好,在训练集也好,就是很完美了。我们应该冷静看这个数据结果,因为这是用的红酒数据集,在现实生活中不可能有这么高的分数数据集。
为了让决策树有更好的泛化性,我们要对决策树进行剪枝。剪枝策略对决策树的影响巨大,正确的剪枝策略是优化决策树算法的核心。sklearn为我们提供了不同的剪枝策略:
1) max_depth
限制树的最大深度,超过设定深度的树枝全部剪掉
这是用得最广泛的剪枝参数,在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所以限制树深度能够有效地限制过拟合。在集成算法中也非常实用。实际使用时,建议从=3开始尝试,看看拟合的效果再决定是否增加设定深度。
2) min_samples_leaf
min_samples_leaf 限定,一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生
一般搭配max_depth使用,在回归树中有神奇的效果,可以让模型变得更加平滑。这个参数的数量设置得太小会引起过拟合,设置得太大就会阻止模型学习数据。一般来说,建议从=5开始使用。如果叶节点中含有的样本量变化很大,建议输入浮点数作为样本量的百分比来使用。同时,这个参数可以保证每个叶子的最小尺寸,可以在回归问题中避免低方差,过拟合的叶子节点出现。对于类别不多的分类问题,=1通常就是最佳选择。
3) min_samples_split
min_samples_split限定,一个节点必须要包含至少min_samples_split个训练样本,这个节点才允许被分枝,否则分枝就不会发生。
如果一个样本包20个样本,我们在不限定的情况下会不断分下去的,如果设定min_samples_split=15,那么这个节点就不会分了。
执行下面代码,设置max_depth=3:
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_splitwine = load_wine()xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)clf=tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30,splitter="random"
,max_depth=3)
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮'
,'非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True
, rounded=True
)graph = graphviz.Source(dot_data)
graph.view()
输出结果:
0.7962962962962963
输出只有三层,三层之外的全部被砍掉了,上面的分数来看数据的精准性下降了很多。
限制了过拟合之后,没有提升它在测试上的表现
#设置min_samples_leaf,之后看看表现
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_splitwine = load_wine()xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)clf=tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30,splitter="random"
#,max_depth=3
,min_samples_leaf=10#,min_samples_split=70
)
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚'
,'类黄酮','非黄烷类酚类','花青素','颜 色强度'
,'色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True
, rounded=True
)graph = graphviz.Source(dot_data)
graph.view()输出结果:
0.8148148148148148
每个节点的样本量samples都是大于10的。
#下面我们只允许有70的样本允许分割
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_splitwine = load_wine()xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)clf=tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30,splitter="random"
,max_depth=3,min_samples_leaf=10,min_samples_split=70)
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚','类黄酮','非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True
, rounded=True
)graph = graphviz.Source(dot_data)
graph.view()score_train = clf.score(xtrain,ytrain)
print(score_train)输出结果:
可以看出来样本量只有大于70的才允许分割了。上图中会看到48小于70所以不分割了节点了。
注意:每个人的树不一样,不要追求运行结果完全一致。
4) max_features
一般max_depth使用,用作树的”精修“
max_features限制分枝时考虑的特征个数,超过限制个数的特征都会被舍弃。和max_depth异曲同工,max_features是用来限制高维度数据的过拟合的剪枝参数,但其方法比较暴力,是直接限制可以使用的特征数量而强行使决策树停下的参数,在不知道决策树中的各个特征的重要性的情况下,强行设定这个参数可能会导致模型学习不足。如果希望通过降维的方式防止过拟合,建议使用PCA,ICA或者特征选择模块中的降维算法。
5) min_impurity_decrease
min_impurity_decrease限制信息增益的大小,信息增益小于设定数值的分枝不会发生。这是在0.19版本种更新的功能,在0.19版本之前时使用min_impurity_split。
D. 确认最优的剪枝参数
那具体怎么来确定每个参数填写什么值呢?这时候,我们就要使用确定超参数的曲线来进行判断了,继续使用我们已经训练好的决策树模型clf。超参数的学习曲线,是一条以超参数的取值为横坐标,模型的度量指标为纵坐标的曲线,它是用来衡量不同超参数取值下模型的表现的线。在我们建好的决策树里,我们的模型度量指标就是score。
#输出图形
from sklearn import tree
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_splitwine = load_wine()xtrain,xtest,ytrain,ytest = train_test_split(wine.data,wine.target,test_size=0.3)clf=tree.DecisionTreeClassifier(criterion="entropy"
,random_state=30,splitter="random"
,max_depth=3,min_samples_leaf=10,min_samples_split=70)
clf=clf.fit(xtrain,ytrain) #把训练数据集放入到分类器中,fit来寻找相应标签
score = clf.score(xtest,ytest) #score是针对上面模型评估精确度
print(score)import graphviz
feature_name = ['酒精','苹果酸','灰','灰的碱性','镁','总酚'
,'类黄酮','非黄烷类酚类','花青素','颜 色强度','色调','od280/od315稀释葡萄酒','脯氨酸']
dot_data = tree.export_graphviz(clf, feature_names=feature_name, class_names=["琴酒", "雪莉", "贝尔摩德"], filled=True
, rounded=True
)graph = graphviz.Source(dot_data)
graph.view()import matplotlib.pyplot as plt
test = []
for i in range(10):clf=tree.DecisionTreeClassifier(max_depth=i+1,criterion="entropy"
,random_state=30)clf=clf.fit(xtrain,ytrain)score_train = clf.score(xtrain, ytrain)test.append(score)
plt.plot(range(1,11),test,color='red',label='max_depth')
plt.legend()
plt.show()
输出结果:
横坐标是每次评估模型打分的结果,纵坐标是次数,图形说明,树深度到3的时候就已经达到极限了。我们选择树深度就是3,因为从上图可以看出后续在有更深的深度,数据集也不会有更好的表现。
思考:
剪枝参数一定能够提升模型在测试集上的表现吗? - 调参没有绝对的答案,一切都是看数据本身。
这么多参数,一个个画学习曲线?
无论如何,剪枝参数的默认值会让树无尽地生长,这些树在某些数据集上可能非常巨大,对内存的消耗。所以如果你手中的数据集非常大,你已经预测到无论如何你都是要剪枝的,那提前设定这些参数来控制树的复杂性和大小会比较好。
思考:
剪枝参数一定会提升模型在测试集上的表现么?调参是没有答案的,一起都要看数据本身。
这么多参数,一个一个花学习曲线么?
无论如何,剪枝参数默认值是不会无尽生长的,这些书在某些数据街上可能非常巨大,对内存消耗。搜一如果你手中的数据集非常大,已经预测到你要剪枝,那体现设定这些参数来控制树的复杂性和大小很有必要。
E. 目标权重参数
1) class_weight
完成样本标签平衡的参数。样本不平衡是指在一组数据集中,标签的一类天生占有很大的比例。比如说,在银行要判断“一个办了信用卡的人是否会违约”,就是是vs否(1%:99%)的比例。这种分类状况下,即便模型什么也不做,全把结果预测成“否”,正确率也能有99%。因此我们要使用class_weight参数对样本标签进行一定的均衡,给少量的标签更多的权重,让模型更偏向少数类,向捕获少数类的方向建模。该参数默认None,此模式表示自动给与数据集中的所有标签相同的权重。
2) min_weight_fraction_leaf
有了权重之后,样本量就不再是单纯地记录数目,而是受输入的权重影响了,因此这时候剪枝,就需要搭配min_ weight_fraction_leaf这个基于权重的剪枝参数来使用。另请注意,基于权重的剪枝参数(例如min_weight_ fraction_leaf)将比不知道样本权重的标准(比如min_samples_leaf)更少偏向主导类。如果样本是加权的,则使用基于权重的预修剪标准来更容易优化树结构,这确保叶节点至少包含样本权重的总和的一小部分。
F. 重要属性和接口
属性是在模型训练之后,能够调用查看的模型的各种性质。对决策树来说,最重要的是feature_importances_,能够查看各个特征对模型的重要性。
sklearn中许多算法的接口都是相似的,比如说我们之前已经用到的fit和score,几乎对每个算法都可以使用。除了这两个接口之外,决策树最常用的接口还有apply和predict。apply中输入测试集返回每个测试样本所在的叶子节点的索引,predict输入测试集返回每个测试样本的标签。返回的内容一目了然并且非常容易,大家感兴趣可以自己下去试试看。
总结:
七个参数:Criterion,两个随机性相关的参数(random_state,splitter),四个剪枝参数(max_depth, ,min_sample_leaf,max_feature,min_impurity_decrease)
一个属性:feature_importances_
四个接口:fit,score,apply,predict
有了这些知识,基本上分类树的使用大家都能够掌握了,接下来再到实例中去磨练就好。