第三节 识别毒蘑菇(1 / 1)

Python的scikit-learn(简称sklearn)模块可以非常好地支持决策树分类,尽管它使用的是更加复杂的决策树算法,但依然是在上一节讲述的原理基础上的优化算法。本节将使用scikit-learn的决策树分类来识别“蘑菇数据”中的毒蘑菇。

首先介绍本案例使用的数据。原始数据来源于加州大学欧文分校用于机器学习的数据库(UCI数据库),本书使用的是经过必要处理的数据,此数据可从教材资源平台下载。

接下来在命令行界面安装scikit-learn。

pip install -u scikit-learn

安装scikit-learn之前需要确保模块numpy和scipy已经安装,否则可以使用pip install安装。

pip install -u numpy

pip install -u scipy

进入Python之后,使用如下命令调用scikit-learn的决策树方法。

from sklearn import tree

模块scikit-learn的决策树是基于DecisionTreeClassifier对象,它能够解决二分类问题(如蘑菇是否可食用),也可以解决多分类问题。在使用中,对输入训练数据的维度要求如下。

输入X:样本数量×特征属性数量;

输入Y:样本类别标签,与X要一一对应。

下面先用一个简单的例子来熟悉使用方法。对四个点X=[[0,0],[0,1],[1,0],[1,1]]使用决策树进行分类,四个点分别属于两个类别0和1,它们对应的类别标签是Y=[0,0,1,1]。这可以通过如下简单代码实现。

In[1]:from sklearn import tree

X=[[0,0],[0,1],[1,0],[1,1]]

Y=[0,0,1,1]

clf=tree.DecisionTreeClassifier()

clf=clf.fit(X,Y)

clf命令用来构造决策树,通过clf.fit(X,Y)实现了决策树的构建,此时clf已经是能够进行分类的决策树了,可以用它来进行新数据的分类。例如,输出[[0.3,0],[0.8,1],[1.2,1]]的类别,代码如下。

In[2]:test=[[0.3,0],[0.8,1],[1.2,1]]

In[3]:clf.predict(test)

Out[3]:array([0,1,1])

通过上述输出可以看到决策树给出的分类结果是[0.3,0]类别为0;[0.8,1]类别为1;[1.2,1]类别为1。读者可以在平面直角坐标系上画出这些点,看看分类是否合理。

读者还可以尝试下列与决策树相关的函数,看看它们具有什么功能。

In[4]:dir(clf)

Out[4]:

[……

'apply',

'class_weight',

'classes_',

'criterion',

'decision_path',

'feature_importances_',

'fit',

'fit_transform',

'get_params',

'max_depth',

'max_features',

'max_features_;,

'max_leaf_nodes',

'min_impurity_split',

'min_samples_leaf',

'min_samples_split',

'min_weight_fraction_leaf',

'n_classes_',

'n_features_',

'n_outputs_',

'predict',

'predict_log_proba',

'predict_proba',

'presort',

'random_state',

'score',

'set_params',

'splitter',

'transform',

'tree_']

下面开始进行蘑菇分类。本书提供的蘑菇数据如表3-5所示,包含样本编号和特征属性,其中第一行是类别标记及属性名称,第一列为样本编号。

表3-5

该表共包含8 123条数据,特征属性共有22个,这些属性及其对应的取值含义如下所示。

类别标记:

毒蘑菇,poisonous,p

可食用,edible,e

特征属性及取值

1.cap-shape:bell=b,conical=c,convex=x,flat=f,knobbed=k,sunken=s

2.cap-surface:fibrous=f,grooves=g,scaly=y,smooth=s

3.cap-color:brown=n,buff=b,cinnamon=c,gray=g,green=r,pink=p,purple=u,red=e,white=w,yellow=y

4.bruises:bruises=t,no=f

5.odor:almond=a,anise=l,creosote=c,fishy=y,foul=f,musty=m,none=n,pungent=p,spicy=s

6.gill-attachment:attached=a,descending=d,free=f,notched=n

7.gill-spacing:close=c,crowded=w,distant=d

8.gill-size:broad=b,narrow=n

9.gill-color:black=k,brown=n,buff=b,chocolate=h,gray=g,green=r,orange=o,pink=p,purple=u,red=e,white=w,yellow=y

10.stalk-shape:enlarging=e,tapering=t

11.stalk-root:bulbous=b,club=c,cup=u,equal=e,rhizomorphs=z,rooted=r,missing=?

12.stalk-surface-above-ring:fibrous=f,scaly=y,silky=k,smooth=s

13.stalk-surface-below-ring:fibrous=f,scaly=y,silky=k,smooth=s

14.stalk-color-above-ring:brown=n,buff=b,cinnamon=c,gray=g,orange=o,pink=p,red=e,white=w,yellow=y

15.stalk-color-below-ring:brown=n,buff=b,cinnamon=c,gray=g,orange=o,pink=p,red=e,white=w,yellow=y

16.veil-type:partial=p,universal=u

17.veil-color:brown=n,orange=o,white=w,yellow=y

18.ring-number:none=n,one=o,two=t

19.ring-type:cobwebby=c,evanescent=e,flaring=f,large=l,none=n,pendant=p,sheathing=s,zone=z

20.spore-print-color:black=k,brown=n,buff=b,chocolate=h,green=r,orange=o,purple=u,white=w,yellow=y

21.population:abundant=a,clustered=c,numerous=n,scattered=s,several=v,solitary=y

22.habitat:grasses=g,leaves=l,meadows=m,paths=p,urban=u,waste=w,woods=d

首先使用pandas读取数据,这是一个强大的数据处理工具。通过显示数据形状可以看到共有8 124行、24列。

In[5]:import pandas as pd

import numpy as np

In[6]:data=pd.read_excel('mushroom.xlsx',header=0)

In[7]:data.shape

Out[7]:(8124,24)

使用如下命令观察前5行数据。

In[8]:data.head(5)

Out[8]:

样本编号标记属性1 属性2 属性3 属性4 属性5 属性6 属性7 属性8 ...

属性13 属性14 属性15 属性16 属性17 属性18\

0 1.0 p x s n t p f c n ...s w w p w o

1 2.0 e x s y t a f c b ...s w w p w o

2 3.0 e b s w t l f c b ...s w w p w o

3 4.0 p x y w t p f c n ...s w w p w o

4 5.0 e x s g f n f w b ...s w w p w o

属性19 属性20 属性21 属性22

0 p k s u

1 p n n g

2 p n n m

3 p k s u

4 e n a g

[5 rows x 24 columns]

进行数据拆分,获得输入数据X和对应的类别标记Y,这个过程是为了准备训练数据。用以下代码获取类别标记。

In[9]:label=data['标记']

#读取标记列

In[10]:label=np.array(label)

#转化成数组,这是Python最常使用的数据格式

In[11]:label.shape

Out[11]:(8124,)

#获得标记的个数。实际标记是8123个,需要剔除最后一个' '标记

In[12]:label=label[0:-1]

#获得8123个标记

因为用来表示类别的“e”和“p”是英文字母,所以需要转化成1和0以便计算机使用,其中1表示可食用,0表示有毒。使用以下代码完成转化,这是一个循环程序。

In[13]:for i in range(0,8123):

if label[i]=='e':

label[i]=1

else:

label[i]=0

接下来就可以设置Y数据了。

In[14]:Y=label

In[15]:Y=Y.astype(Y)

接下来处理训练样本。

In[16]:data=data.drop(['样本编号','标记'],axis=1)

#训练样本需要将两列去掉(样本编号和标记)

In[17]:data.shape

Out[17]:(8124,22)

#训练样本的行列数量

In[18]:newdata=np.array(data)

In[19]:newdata=newdata[0:-1,:]

#去掉最后一行

In[20]:newdata.shape

Out[20]:(8123,22)

#新的数据

特征属性的值同样是用字符表示的,而Python的决策树需要对数值进行处理。所以接下来使用下面的编码将字符直接转化成ASCII编码的整数。表3-6中的最后一列是通常的字符,前两列分别是这个字符的十进制编码和十六进制编码。

表3-6

使用下面的代码将字符转化成它对应的整数。如g转化为103,O转化为79等。这是一个循环程序,其中的ord( )函数负责转化功能。

In[21]:for i in range(0,8123):

for j in range(0,22):

newdata[i,j]=ord(newdata[i,j])

新的训练数据为

In[22]:X=newdata

接下来就可以构造决策树并用于分类了,输出的是描述所生成的决策树的参数。

In[23]:clf=tree.DecisionTreeClassifier()

In[24]:clf.fit(X,Y)

Out[24]:

DecisionTreeClassifier(class_weight=None,criterion='gini',

max_depth=None,

max_features=None,max_leaf_nodes=None,

min_impurity_split=1e-07,min_samples_leaf=1,

min_samples_split=2,min_weight_fraction_leaf=0.0,

presort=False,random_state=None,splitter='best')

使用决策树用于新数据的分类,可以看到对下列4个新的蘑菇数据进行分类后,输出结果表明其中有3种是可食用的,1种是有毒的。

In[25]:test=np.anay([[…],[…],[…],[…]])

np.anay([[120,115,121,...,110,110,103],

[98,115,119,...,110,110,109],

[120,121,119,...,107,115,117],

[120,115,103,...,110,97,103]])

In[26]:clf.predict(test)

Out[26]:array([1,1,0,1])

这个决策树的准确率如何呢?一种简单的方法是使用训练数据中的X,用决策树获得对应的类别标记,也就是预测分类结果(这里用Y_predict表示),然后把它和实际的类别标记Y进行比较。scikit-learn支持这样的比较,使用的方法是accuracy_score。

In[27]:from sklearn.metrics import accuracy_score

In[28]:Y_predict=clf.predict(X)

#这里使用前面训练好的决策树,输入训练样本的X,给出对应的predict

In[29]:accuracy_score(Y,Y_predict)

#这里利用accuracy_score来比较预测值和真实值。

Out[29]:1.0

可以看到这个决策树在训练数据上的准确率是100%(1.0)。这表明使用上述算法构建的决策树完全捕捉了训练数据的分类信息。这也许是因为训练出的决策树确实具有很好的分类效果,但也有可能并不是真正的分类准确率,而是发生了过拟合现象。

为了更好地评估分类准确率,需要使用与训练数据不同的测试数据。可以抽取全部数据中的一部分作为训练数据,另一部分作为测试数据,使用抽取出的训练数据来构建决策树,然后使用测试数据评估准确率。这可以通过如下方式来实现。

In[30] from sklearn.model_selection import train_test_split

In[31]:X_train,X_test,Y_train,Y_test=train_test_split

(X,Y,test_size=0.2)

#这里将全部样本分成两部分,其中训练样本占80%,测试样本占20%,这

#里采用了随机分割样本的方法,所以读者的输出可能与下面的结果不同

In[32]:newclf=tree.DecisionTreeClassifier()

In[33]:newclf.fit(X_train,Y_train)

#利用训练数据来构建决策树

Out[33]:

DecisionTreeClassifier(class_weight=None,criterion='gini',

max_depth=None,

max_features=None,max_leaf_nodes=None,

min_impurity_split=1e-07,min_samples_leaf=1,

min_samples_split=2,min_weight_fraction_leaf=0.0,

presort=False,random_state=None,splitter='best')

#返回新的决策树的参数

In[34]:Y_predict=newclf.predict(X_test)

#注意,这里是将新训练的决策树用在测试样本的X上,并预测出对应Y

In[35]:accuracy_score(Y_test,Y_predict)

Out[35]:1.0

可以看到在测试集上的准确率仍然是100%,这说明这个分类器确实具有很高的准确率。因为测试数据和训练数据是采用随机划分的方式获得的,读者最后获得的输出结果可能会与此不同。

从上述内容可以看出,决策树实现起来简单并且效果很好。本书采用最简单的形式实现这个算法,scikit-learn的决策树有很多参数可以调整,读者可以尝试调整决策树的参数,观察分类性能会发生什么改变。