您的位置:首页 > 编程语言 > Python开发

Python3 机器学习实战自我讲解(二) K-近邻法-海伦约会-手写字体识别

2018-02-04 14:24 537 查看

第二章 k近邻法

2.1 概念

2.1.1 k近邻法简介

k近邻法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法。它的工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。


K近邻算法
优 点 :精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
适用数据范围:数值型和标称型


举个例子,我们来使用k近邻法来分类爱情片和动作片。有人曾统计过很多电影的打斗镜头和接吻镜头,那么如何确定一部没看过的电影是爱情片还是动作片呢?我们可以使用k-nn来解决这个问题。

电影名称打斗镜头接吻镜头电影类型
California Man3104爱情片
He’s Not Really into Dudes2100爱情片
Robo Slayer 3000995动作片
Amped ll982动作片
1890未知
​ 每部电影的打斗镜头数、接吻镜头数以及电影评估类型

现在我们如何判断该电影的类型呢?当然是通过最近邻居来判断啦~如何判断邻居的距离呢,首先我们需要定义距离。

2.1.2 距离度量

我们一般运用欧氏距离:

∥AB||=(x1−x2)2+(y1−y2)2−−−−−−−−−−−−−−−−−−√‖AB||=(x1−x2)2+(y1−y2)2

这样我们可以得到

电影名称与未知电影的距离
California Man20.5
He’s Not Really into Dudes18.7
Robo Slayer 3000117.4
Amped ll118.9
由此可知最近的邻居为《California Man》,所以我们未知电影的类型可以判定为爱情片。

k-近邻算法的一般流程:

(1)收集数据:可以使用任何方法。

(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式。

(3)分析数据:可以使用任何方法。

(4)训练算法:此步骤不适用于k近邻算法。

(5)测试算法:计算错误率。

(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k -近邻算法判定输

入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

2.1.3 实战环节

python3实现:

# -*- coding: UTF-8 -*-
import numpy as np
#数据集,标签
dataSet=np.array([[3,104],[2,100],[99,5],[98,2]])
labels=['爱情片','爱情片','动作片','动作片']

def classy(inx,dataSet,labels):
diffMat=inx-dataSet
distanceMat=np.sqrt(np.sum(diffMat**2,axis=1))
sorted_distanceMat=np.argsort(distanceMat)
classylabel=labels[sorted_distanceMat[0]]
return classylabel
label=classy([18,90],dataSet,labels)
print("电影类型为:{0:s}".format(label))




可以看到,分类结果根据我们的”经验”,是正确的

接下来我们来挑战更有难度的。

def classify0(inX, dataSet, labels, k):
diffMat = inX- dataSet                              #python的广播机制
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5                        #距离计算
sortedDistIndicies = distances.argsort()            #距离排序从小到大
classCount={}                                       #创建字典
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1    #字典的运用
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)    #python2 使用classCount.iteritems()
return sortedClassCount[0][0]                       #选择k个距离最小的中票数最多的


2.2 海伦约会

2.2.1 k-近邻算法实战之约会网站配对效果判定

上一小结学习了简单的k-近邻算法的实现方法,但是这并不是完整的k-近邻算法流程,k-近邻算法的一般流程:

收集数据:可以使用爬虫进行数据的收集,也可以使用第三方提供的免费或收费的数据。一般来讲,数据放在txt文本文件中,按照一定的格式进行存储,便于解析及处理。

准备数据:使用Python解析、预处理数据。

分析数据:可以使用很多方法对数据进行分析,例如使用Matplotlib将数据可视化。

测试算法:计算错误率。

使用算法:错误率在可接受范围内,就可以运行k-近邻算法进行分类。

​ 已经了解了k-近邻算法的一般流程,下面开始进入实战内容。

2.2.2 实战背景

海伦女士一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的任选,但她并不是喜欢每一个人。经过一番总结,她发现自己交往过的人可以进行如下分类:

不喜欢的人

魅力一般的人

极具魅力的人

​ 海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占据一行,总共有1000行。

数据集下方下载:
https://github.com/ZChengming/datingTestSet
海伦收集的样本数据主要包含以下3种特征:

每年获得的飞行常客里程数

玩视频游戏所消耗时间百分比

每周消费的冰淇淋公升数

​ 打开txt文本文件,数据格式如图2.1所示。



2.2.3 准备数据:从文本文件中解析数据

于是我们要写一个函数来将文本文件里的信息转化为矩阵信息,话不多说动手!!

#得到文件行数,创建矩阵,创建标签向量
def file2matrix(filename):
f = open(filename)
arrayOfLines=f.readlines()
numberOfLines = len(arrayOfLines)         #get the number of lines in the file
returnMat = zeros((numberOfLines,3))        #prepare matrix to return
classLabelVector = []                       #prepare labels return
index = 0
for line in arrayOfLines:
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLabelVector




我们检查一下数据内容,没有问题。

2.2.4 归一化特征

如果我们不归一化特征那么就会使结果偏向数值较大的特征,显然是不正确的。

样本玩游戏所耗时间百分比每年获得的飞行常用里程数每周消费的冰淇淋公升数样本分类
10.84000.51
2121340000.93
30200001.12
467320000.12
​ 我们很容易发现,上面方程中数字差值最大的属性对计算结果的影响最大,也就是说,每年获取的飞行常客里程数对于计算结果的影响将远远大于表2.1中其他两个特征-玩视频游戏所耗时间占比和每周消费冰淇淋公斤数的影响。而产生这种现象的唯一原因,仅仅是因为飞行常客里程数远大于其他特征值。但海伦认为这三种特征是同等重要的,因此作为三个等权重的特征之一,飞行常客里程数并不应该如此严重地影响到计算结果。

​ 在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值

归一化特征的公式如下:

newvalue=(oldvalue−minvalue)/(maxvalue−minvalue)newvalue=(oldvalue−minvalue)/(maxvalue−minvalue)

def autoNorm(dataSet):
minValues=dataSet.min(axis=0)                       #按行求最小值
maxValues=dataSet.max(axis=0)                       #按行求最大值
ranges=maxValues-minValues
normDataSet=(dataSet-minValues)/ranges              #python的广播机制
return normDataSet,ranges,minValues
dataMat,labels=file2matrix("datingTestSet2.txt")




检查一下,没有问题,都在0-1之间。

2.2.5 分类器针对约会网站的测试代码

def datingClassTest():
hoRatio=0.1                                         #留百分之10作为测试数据
datingDataMat,datingLabels=file2matrix("datingTestSet2.txt")
normMat,ranges,minValues=autoNorm(datingDataMat)
m=normMat.shape[0]                                  #矩阵的行数
numTestVecs=int(m*hoRatio)
errorCount=0.0
for i in range(numTestVecs):
classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print("预测结果:{0},实际结果:{1}".format(classifierResult,datingLabels[i]))
if(classifierResult!=datingLabels[i]):
errorCount+=1.0
print("正确率为:{0:f}".format(1.0-errorCount/float(numTestVecs)))




正确率高达百分之95!

2.2.6 最后一步使用算法

def classifyPerson():
resultList=['不感兴趣','有那么一丢丢意思','很感兴趣']
percentTats=float(input("每天多少小时玩游戏?"))              #如果是python2请使用raw_input
ffMiles=float(input("每年飞多少公里数?"))
iceCream=float(input("每年吃多少公升冰激凌?"))
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
normMat,ranges,minValues=autoNorm(datingDataMat)
inArr=array([percentTats,ffMiles,iceCream])
classifierResult=classify0(inArr,datingDataMat,datingLabels,3)
print("海伦对他:{}".format(resultList[classifierResult-1]))




2.3 手写体识别实战

思考

机器学习六步走:

收集数据:下载

准备数据:编写函数classify0()

分析数据:查看是否有缺漏

训练算法:KNN不需要

测试算法:提取部分作为测试样本

使用算法:有兴趣有空可以尝试

2.3.1 准备数据:将图像转化为向量

下载数据

https://github.com/ZChengming/-handwritingdata

目录trainingDigits中包含了大约2000个例子,每个例子如下图所示,每个数字大约有200个样本,目录testDigits中包含大约900个测试数据。我们使用目录trainingDigits中的数据训练分类器,使用目录testDigits中的数据测试分类器效果。



首先我们要将图像格式化处理为一个向量。代码如下:

def img2vector(filename):
""" 将图像转为向量
图像32*32转为向量1*1024

输入文件名
输出向量 """
returnMat=zeros((1,1024))
f=open(filename)
for i in range(32):
line_str=f.readline()
for j in range(32):
returnMat[0,32*i+j]=int(line_str[i])
return returnMat




有了特征向量后,我们就能够进行预测啦!

类似之前的约会预测,话不多说直接上代码:



正确率高达98.9%

2.4 总结

KNN算法

给一个训练数据集和一个新的实例,在训练数据集中找出与这个新实例最近的k个训练实例,然后统计最近的k个训练实例中所属类别计数最多的那个类,就是新实例的类

三要素:

k值的选择

距离的度量(常见的距离度量有欧式距离,马氏距离等)

分类决策规则 (多数表决规则)

k值的选择

k值越小表明模型越复杂,更加容易过拟合

但是k值越大,模型越简单,如果k=N的时候就表明无论什么点都是训练集中类别最多的那个类

所以一般k会取一个较小的值,然后用过交叉验证来确定

这里所谓的交叉验证就是将样本划分一部分出来为预测样本,比如95%训练,5%预测,然后k分别取1,2,3,4,5之类的,进行预测,计算最后的分类误差,选择误差最小的k

KNN的回归

在找到最近的k个实例之后,可以计算这k个实例的平均值作为预测值。或者还可以给这k个实例添加一个权重再求平均值,这个权重与度量距离成反比(越近权重越大)。

优缺点:

KNN算法的优点:

思想简单,理论成熟,既可以用来做分类也可以用来做回归;

可用于非线性分类;

训练时间复杂度为O(n);

准确度高,对数据没有假设,对outlier不敏感;

缺点:

计算量大;

样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);

需要大量的内存;

有空再补充。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: