Python数据挖掘入门与实践 第二章2.1 书本代码

第2章:用scikit-learn 估计器分类

scikit-learn 估计器

看到这本书的代码。对于我这个30岁初学编程的人来讲太为精妙。
忍不住想要写下来保存起来~
会对代码做出一些我的理解,初学者请多多包含。
注释的‘##’为代码的上方或者右侧。

近邻算法
即将用到的数据集叫作电离层(Ionosphere),这些数据是由高频天线收集的。这些天线的目的是侦测在电离层和高层大气中存不存在由自由电子组成的特殊结构。如果一条数据能给出特殊结构存在的证据,这条数据就属于好的那一类(在数据集中用“g”表示),否则就是坏的(用“b”表示)。我们要做的就是建立分类器,自动判断这些数据的好坏。

数据集有大佬上传了github

该数据集每行有35个值,前34个为17座天线采集的数据(每座天线采集两个数据)。最后一个值不是“g”就是“b”,表示数据的好坏,即是否提供了有价值的信息。

import numpy as np
import csv
## 创建训练集和测试集。导入并运行train_test_split函数。下文详解
from sklearn.cross_validation import train_test_split  
## K近邻分类器
from sklearn.neighbors import KNeighborsClassifier 

data_filename = r'路径\ionosphere.data' ##上面git的文件下载后的存放位置。
## 定于两个矩阵X和y,全部是0或者False,接下来的代码我觉得很有意思。
X = np.zeros((351, 34), dtype='float') 
y = np.zeros((351,), dtype='bool') 


with open(data_filename, 'r') as input_file:## 打开上面得data文件
    reader = csv.reader(input_file)
	
	## 这个enumerate()函数,直接返回每一行的行号和数据,行号开始可选:
	## enumerate(sequence, [start=0]) 参数:
	## sequence -- 一个序列、迭代器或其他支持迭代对象。
	## start -- 下标起始位置。
    for i, row in enumerate(reader):
    	## 每一行的数据,截取row的前面34个数据(row[:-1]),并且定义为浮点型。
        data = [float(datum) for datum in row[:-1]]
        X[i] = data
        ## 如果是'g'就返回True。第一次看到这么写代码,感觉好兴奋。
        y[i] = row[-1] == 'g'

## train_test_split(原始数据X,目标值y,test_size=(默认是0.25),
## random_state=14(书本上写了14,但是我查了半天也没找到14的意思))
## 只知道设置一个值,可以让每一次代码运行的随机取值都一样。
## 但是具体每个值有什么区别?或许可以给自己挖一个坑,研究一下。
## 要不就下一章节试着讨论吧。

## 即,把X和y,按照random_state=14的规定,拆分成4个数据集。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=14)
estimator = KNeighborsClassifier()
estimator.fit(X_train, y_train) ##用fit函数来训练数据。
y_predicted = estimator.predict(X_test) ##用predicted函数来预测数据。

## 这句也很精妙的感觉! 如果y_test等于y_predicted的话,那么就是True,
## True默认是1,False默认是0,直接取平均值那么就可以算出准确度是多少。
accuracy = np.mean(y_test == y_predicted) * 100
print("The accuracy is {0:.1f}%".format(accuracy))

到这里,第一段落就结束了,可以跑一跑程序,正确率是86.4%。
由于设置了random_state=14,所以跑出来的结果应该都是一样的!
又由于默认的test数据集/总数据集是25%,所以351*0.25=88个test数据。
又由于准确率是86.4%,所以可以知道,一共是76个数据的结果是被准确预测的。

顺便说一句,sklearn.cross_validation运行的时候,会被提示将被删除。
所以,需要修改一下就不会出现提示啦。
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score (下文的交叉检验)

交叉检验

在先前的几个实验中,我们把数据集分为训练集和测试集,用训练集训练算法,在测试集上评估效果。
倘若碰巧走运,测试集很简单,我们就会觉得算法表现很出色。
反之,我们可能会怀疑算法很糟糕。
也许由于我们一时不走运,就把一个其实很不错的算法给无情抛弃了,这岂不是很可惜。

交叉检验能解决上述一次性测试所带来的问题。
既然只切一次有问题,那就多切几次,多进行几次实验。
每次切分时,都要保证这次得到的训练集和测试集与上次不一样,还要确保每条数
据都只能用来测试一次。算法描述如下。
(1) 将整个大数据集分为几个部分(fold)。
(2) 对于每一部分执行以下操作:
 将其中一部分作为当前测试集
 用剩余部分训练算法
 在当前测试集上测试算法
(3) 记录每次得分及平均得分。
(4) 在上述过程中,每条数据只能在测试集中出现一次,以减少(但不能完全规避)运气成分。

scikit-learn提供了几种交叉检验方法。有个辅助函数实现了上述交叉检验步骤,现在把它导进来。

## 交叉检验 接着上面的代码。
from sklearn.cross_validation import cross_val_score
scores = cross_val_score(estimator, X, y, cv=3, scoring='accuracy')
average_accuracy = np.mean(scores) * 100
print("The average accuracy is {0:.1f}%".format(average_accuracy))

打印的结果为:The average accuracy is 82.3%。
cross_val_score这个函数的参数是:分类器,X,y,cv=3是默认的切分三类
cv也再挖个坑,看看不同的数值,以及这个交叉检验,具体是怎么来检验的。
然后,scoring=‘accuracy’,这个我理解的是,设置为计算准确度,
所以才能得到准确度的得分。

按照上面的例子,可以打印出来看看scores的结果和type分别为:

[0.82051282 0.78632479 0.86324786]
<class ‘numpy.ndarray’>

所以平均值为上述的结果,82.3%。
可以看出,拆分的模块不同,结果也不同。
所以一开始 random_state=14 不设置的话,跑出来的结果一定会不同的。

设置参数

几乎所有的数据挖掘算法都允许用户设置参数,这样做的好处是增强算法的泛化能力。
但是,参数设置可是项技术活,选取好的参数值跟数据集的特征息息相关。
近邻算法有多个参数,最重要的是选取多少个近邻作为预测依据。
scikit-learn管这个参数叫n_neighbors。
书中给出两个极端的例子,n_neighbors过小时,分类结果容易受干扰,随机性很强。
相反,如果n_neighbors过大,实际近邻的影响将削弱。
Python数据挖掘入门与实践 第二章2.1 书本代码
左图(a)中,我们通常希望把测试数据(三角形)归到圆形类别。
然而,如果n_neighbors的值为1,由于三角形附近红色菱形(很可能是噪音)的存在,导致分类结果为菱形,虽然菱形集中在右下角区域。

右图(b)中,我们希望将测试数据归到菱形类别。
然而,如果n_neighbors值为7,三个最近的邻居(都是菱形)被四个圆形给击败了,三角形也因此被归到圆形类别。

如果想测试一系列n_neighbors的值,比如从1到20,可以重复进行多次实验,观察不同的参数值所带来的结果之间的差异。
并且直接用matplotlib画图,这个东西真的好用,有了它搞科研再也不愁画不好数据图了!也是我学python3最可以反应到工作中的工具。

## 参数检验
from matplotlib import pyplot as plt
avg_scores = []  ##平均得分
all_scores = []  ##总得分
parameter_values = list(range(1, 21)) ## 设置n_neighbors的值,从1到20。
for n_neighbors in parameter_values:
    estimator = KNeighborsClassifier(n_neighbors=n_neighbors)
    scores = cross_val_score(estimator, X, y,cv=3, scoring='accuracy')
    avg_scores.append(np.mean(scores))
    all_scores.append(scores)

plt.plot(parameter_values,avg_scores, 'r-o',label='avg_scores')
plt.plot(parameter_values,all_scores, '.--',label='all_scores')
plt.legend(loc='best')
plt.show()

运行的结果是一张图:
Python数据挖掘入门与实践 第二章2.1 书本代码
由于cv=3的设置,所以有3个all_scores的结果。
可以和这个cv的设置一起挖个坑,看看究竟cv是如何分切数据的。

红色就是平均值的结果,可以看到,
虽然有很多曲折变化,但整体趋势是随着近邻数的增加,正确率不断下降。

最后整理下所有的代码,不加多余的注释:

import numpy as np
import csv
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score
from matplotlib import pyplot as plt 

data_filename = r'下载的地址\ionosphere.data'
X = np.zeros((351, 34), dtype='float')
y = np.zeros((351,), dtype='bool')

with open(data_filename, 'r') as input_file:
    reader = csv.reader(input_file)
    for i, row in enumerate(reader):
        data = [float(datum) for datum in row[:-1]]
        X[i] = data
        y[i] = row[-1] == 'g'

X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=14)
estimator = KNeighborsClassifier()
estimator.fit(X_train, y_train)
y_predicted = estimator.predict(X_test)
accuracy = np.mean(y_test == y_predicted) * 100
print("The accuracy is {0:.1f}%".format(accuracy))

## 交叉检验
scores = cross_val_score(estimator, X, y, cv=3, scoring='accuracy')
average_accuracy = np.mean(scores) * 100
print("The average accuracy is {0:.1f}%".format(average_accuracy))

## 参数检验
avg_scores = []
all_scores = []
parameter_values = list(range(1, 21))
for n_neighbors in parameter_values:
    estimator = KNeighborsClassifier(n_neighbors=n_neighbors)
    scores = cross_val_score(estimator, X, y,cv=3, scoring='accuracy')
    avg_scores.append(np.mean(scores))
    all_scores.append(scores)

plt.plot(parameter_values,avg_scores, 'r-o',label='avg_scores')
plt.plot(parameter_values,all_scores, '.--',label='all_scores')
plt.legend(loc = 'best')
plt.show()

第一篇学习笔记就到这里结束了。
也挖了不少的坑,希望自己有动力写下去,谢谢~