本章介绍第一个机器学习算法:A-近邻算法,它非常有效而且易于掌握。首先,我们将探讨女-近邻算法的基本理论,以及如何使用距离测量的方法分类物品;其次我们将使用?7««^从文本文件中导人并解析数据; 再次,本书讨论了当存在许多数据来源时,.如何避免计算距离时可能碰到的一些常见错误;最后,利用实际的例子讲解如何使用匕近邻算法改进约会网站和手写数字识别系统。

一、K-近邻算法概述--------->K-近邻算法采用测量不同特征值之间的距离方法进行分类。

工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输人没有标签的新数据后, 将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最 近 邻 )的分类标签。一般来说,我们
只选择样本数据集中前&个最相似的数据,这就是&-近邻算法中&的出处,通常*是不大于20的整数。最 后 ,选择K个最相似数据中出现次数最多的分类,作为新数据的分类。

1.准备:使用Python导入数据

在构造完整的丨-近邻算法之前,我们还需要编写一些基本的通用函数,kNN.py代码如下

 def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','']
return group, labels

在上述的代码中需要导入两个模块:

from numpy import*

import operator

然后进入Python开发环境之后,输人下列命令导人上面编辑的程序模块:
>>> import kNN
上 述 命 令 导 人 kNN 模 块 。为了确保输人相同的数据集,kNN模 块 中 定 义 了 函 数createDataSet,在Python命令提本符下输入下属命令
>>> group,labels = kNN.createDataSet()
上述命令创建了变量group 和labels ,在Python 命令提示符下, 输人变量的名字以检验是否正确
地定义变量:
>>> g r o u p
labels
2.从文本文件中解析数据

这里首先给出k-近邻算法的伪代码和实际的Python代码 ,然后详细地解释每行代码的含义。该函数的功能是使用k-近邻算法将每组数据划分到某个类中,其伪代码如下:

K-近邻算法代码:

 def classify0(inX, dataSet, labels, k):
#计算距离
dataSetSize = dataSet.shape[0] #求出样本集的行数,也就是labels标签的数目
diffMat = tile(inX, (dataSetSize,1)) - dataSet #构造输入值和样本集的差值矩阵
#计算欧氏距离
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort() #求距离从小到大排序的序号
#选择距离最小的K个点,对距离最小的K个点统计对应的样本标签
classCount={}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]] #取第i+1邻近的样本对应的类别标签
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #以标签为key,标签出现的次数为value将统计到的标签及出现次数写进字典
#排序
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #对字典按value从大到小排序
return sortedClassCount[0][0] #返回排序后字典中最大value对应的key

classify0 ()函数有4个输人参数:用于分类的输人向量是丨必, 输人的训练样本集为daaSet,标签向量为labels ,最后的参数义表示用于选择最近邻居的数目,其中标签向量的元素数目和矩阵dataSet 的行数相同。

计算完所有点之间的距离后,可以对数据按照从小到大的次序排序。然后,确定前k个距离最小元素所在的主要分类 , 输人k总是正整数;最 后 ,将classCount字典分解为元组列表,然后使用程序第二行导入运算符模块的itemgetter ,按照第二个元素的次序对元组进行排序©。此处的排序为逆序,即按照从最大到最小次序排序,最后返回发生频率最高的元素标签。

3.如何测试分类器

为了测试分类器的效果,我们可以使用已知答案的数据,当然答案不能告诉分类器,检验分类器给出的结果是否符合预期结果。通过大量的测试数据,我们可以得到分类器的错误率—分类器给出错误结果的次数除以测试执行的总数。错误率是常用的评估方法,主要用于评估分类器在某个数据集上的执行效果。

示例:使用K-近邻算法改进约会网站的配对效果

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

数据存放在文本文件datingTestSet.txt 中,每个样本数据占据一行,总共有1000行。海伦的样本主要包含以下3种特征:(1)每年获得的飞行常客里程数;(2)玩视频游戏所耗时间百分比;(3) 每周消费的冰淇淋公升数

在kNN.py中创建名为file2matrix的函数,以此来处理输人格式问题。该函数的输人为文件名字符串,输出为训练样本矩阵和类标签向量

下面代码是将文本记录到转换Numpy的解析程序

 def file2matrix(filename):
fr = open(filename)
#得到文件行数
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
#创建返回的NumPy矩阵
returnMat = zeros((numberOfLines,3))
classLabelVector = []
#解析文件数据到列表
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t') #截取所有的回车字符
returnMat[index,:] = listFromLine[0:3]#选取前三个元素,存储在特征矩阵中
classLabelVector.append(int(listFromLine[-1])) #将列表的最后一列存储到向量classLabelVector中
index +=1
return returnMat,classLabelVector

从上面的代码可以看到,Python处理文本文件非常容易。首先我们需要知道文本文件包含多少行。打开文件,得到文件的行数 。然后创建以零填充的矩阵Numpy ( 实际上,NumPy是一个二维数组,这里暂时不用考虑其用途)。为了简化处理,我们将该矩阵的另一维度设置为固定值3 , 你可以按照自己的实际需求增加相应的代码以适应变化的输人值。循环处理文件中的每行数据 , 首先使用函数line.strip()截取掉所有的回车字符,然后使用tab字符\t将上一步得到的整行数据分割成一个元素列表。 接着,我们选取前3个元素, 将它们存储到特征矩阵中。Python语言可以使用索引值-1表示列表中的最后一列元素,利用这种负索引,我们可以很方便地将列表的最后一列存储到向量classLabelVector中。需要注意的是,我们必须明确地通知解释器, 告诉它列表中存储的元素值为整型,否则Python语言会将这些元素当作字符串处理。

在Python命令提示符下输入下面命令:

>>>datingDataMat,datingLabels = kNN.file2matrix('datingTestSet.txt')

现在已经从文本文件中导人了数据, 并将其格式化为想要的格式,接着我们需要了解数据的真实含义。当然我们可以直接浏览文本文件,但是这种方法非常不友好,一般来说,我们会采用图形化的方式直观地展示数据。下面就用?^1!(瓜工具来图形化展示数据内容,以便辨识出一些数据模式。

2.分析数据:使用Matplotlib创建散点图

首先我们使用Matplotlib制作原始数据的散点图,在Python 命令行环境中,输人下列命令:

 >>>import matplotlib
>>>import matplotlib.pyplot as plt
>>>fig = plt.figure()
>>>ax = fig.add_subplot(111)
>>>ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
>>>plt.show()

Matplotlib库提供的scatter函数支持个性化标记散点图上的点。重新输入上面的代码,调用scatter函数时使用下列参数:

ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))

3.准备数据:归一化数值

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

newValue = (oldValue - min)/(max - min)

其中min和max分别是数据集中的最小特征值和最大特征值。虽然改变数值取值范围增加了分类器的复杂度,但为了得到准确结果,我们必须这样做。我们需要在文件kNN.py 中增加一个新函数autoNorm(), 该函数可以自动将数字特征值转化为0到 1的区间

归一化特征值的代码如下:

 ###    特征值归一化程序
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1)) #element wise divide
return normDataSet, ranges, minVals

在函数autoNorm()中 ,我们将每列的最小值放在变量minVals中 ,将最大值放在变量maxVals中 , 其 中 dataSet.min(0)中的参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值。然 后 ,函数计算可能的取值范围,并创建新的返回矩阵.

为了归一化特征值,我们必须使用当前值减去最小值,然后除以取值范围。需要注意的是,特征值矩阵有1000x3个 值 , 而minVals和range的值都为1x3。为了解决这个冋题,我们使用Numpy库中tile()函数将变量内容复制成输人矩阵同样大小的矩阵,注意这是具体特征值相除,而对于某些数值处理软件包,/可能意味着矩阵除法,但在Numpy 库 中 ,矩阵除法需要使用函数linalg.solve(matA,matB)

在Python命令提示符下, 重 新 加 载 kNN.py 模 块 ,执行autoNorm函数,检测函数的执行结果:

normMat,range,minVals = kNN.atuoNorm(datingDataMat)

4.测试算法:作为完整程序验证分类器

上节我们巳经将数据按照需求做了处理,本节我们将测试分类器的效果,机器学习算法一个很重要的工作就是评估算法的正确率,通常我们只提供已有数据的90%作为训练样本来训练分类器 ,而使用其余的10%数据去测试分类器,检测分类器的正确率。本书后续章节还会介绍一些高级方法完成同样的任务,这里我们还是采用最原始的做法。

前面我们巳经提到可以使用错误率来检测分类器的性能。对于分类器来说, 错误率就是分类器给出错误结果的次数除以测试数据的总数,完美分类器的错误率为0 ,而错误率为1.0的分类器不会给出任何正确的分类结果。代码里我们定义一个计数器变量,每次分类器错误地分类数据,计数器就加1, 程序执行完成之后计数器的结果除以数据点总数即是错误率。

为了测试分类器效果,在kNN.py文件中创建函数datingClassTest,该函数是自包含的,你可以在任何时候在?)也^ 运行环境中使用该函数测试分类器效果。

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

 def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = 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("the classifier came back with :%d,the real answer id:%d" %(classifierResult,datingLabels[i]))
if(classifierResult != datingLabels[i]): errorCount+=1.0
print("the total error rate is :%f" %(errorCount/float(numTestVecs)))

首先使用了file2matrix和autoNorm()函数从文件中读取数据并将其转换为归一化特征值。接着计算测试向量的数量,此步决定了normMat向量中哪些数据用于测试,哪些数据用于分类器的训练样本;然后将这两部分数据输人到原始kNN分类器函数classify0。最后,函数计算错误率并输出结果。注意此处我们使用原始分类器,本章花费了大量的篇幅在讲解如何处理数据,如何将数据改造为分类器可以使用的特征值。

在Python 命令提本符下重新加载看NN 模块,并输人kNN.datingClassTest( ) , 执行分类器测试程序。

5.使用算法:构建完整可用系统

约会网站预测函数:

 def classifyPerson():
resultList = ['not at all','in small doses','in large doses']
percentTats = float(input("percentage of time spent playing video game?"))
ffMiles = float(input("frequent flier miles earned per year?"))
iceCream = float(input("liters of ice cream consumed per year?"))
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = autoNorm(datingDataMat)
inArr = array([ffMiles,percentTats,iceCream])
classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
print("You will probably like this person :",resultList[classifierResult - 1])

在Python命令提示符下输入kNN.classifyPerson()

示例二:手写识别系统

本节我们一步步地构造使用k-近邻分类器的手写识别系统。为了简单起见,这里构造的系统只能识别数字0到9 。需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小: 宽髙是32像 素 x32像素的黑白图像。尽管采用文本格式存储图像不能有效地利用内存空间,但是为了方便理解,我们还是将图像转换为文本格式。

1.准备数据:将图像转换为测试向量

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

为了使用前面两个例子的分类器,我们必须将图像格式化处理为一个向量。我们将把一个32x32的二进制图像矩阵转换为1 x 1024的向量,这样前两节使用的分类器就可以处理数字图像信息了。

首先编写一段函数img2vector,将图像转换为向量:该函数创建1x 1024的Numpy 数组 ,然后打开给定的文件,循环读出文件的前32行 ,并将每行的头32个字符值存储在Numpy 数 组中,最后返回数组。

     ##将图像转换为测试向量
def img2vector(filename):
returnVect = zeros((1,1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0,32*i+j] = int(lineStr[j])
return returnVect

在Python命令提示符下输入:testVector = kNN.img2vector('testDigits/0_13.txt')

2.测试算法:使用k-近邻算法识别手写数字

上 节 我 们 已 经 将 数 据 处 理 成 分 类 器 可 以 识 别 的 格 式 ,本 节 我 们 将 这 些 数 据 输 人 到 分 类 器 ,检测 分 类 器 的 执 行 效 果。 程 序 清 单 2-6所 示 的 自 包 含 函 数handwritingClassTest()是 测 试 分 类 器的 代 码 , 将 其 写入kNN.py 文 件 中 。 在 写 人 这 些 代 码 之 前 , 我 们 必 须 确 保 将from os import listdir 写 人 文 件 的 起 始 部 分 ,这 段 代 码 的 主 要 功 能 是 从os模 块 中 导 人 函 数listdir, 它 可 以 列出 给 定 目 录 的 文 件 名 。

手写数字识别系统的测试代码:

 ##手写数字识别系统的测试代码
def handwritingClassTest():
hwLabels = []
trainingFileList = listdir('trainingDigits') # 获取目录内容
m = len(trainingFileList)
trainingMat = zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i] #从文件名解析分类数字
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('trainingDigits/%s' %fileNameStr)
testFileList = listdir('testDigits')
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector('testDigits/%s' %fileNameStr)
classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,7)
print("the classifier came back with :%d,the real answer is:%d" %(classifierResult,classNumStr))
if(classifierResult != classNumStr):errorCount +=1.0
print("\n the totle number of errors is:%d" %errorCount)
print("\n the totle error rate is: %f" %(errorCount/float(mTest)))

将trainingDigits目录中的文件内容存储在列表中,然后可以得到目录中有多少文件,并将其存储在变量m中。接 着 ,代码创建一个m行 1024列的训练矩阵,该矩阵的每行数据存储一个图像。我们可以从文件名中解析出分类数字 。该目录下的文件按照规则命名, 如文件9_45加的分类是9,它是数字9的第45个实例。然后我们可以将类代码存储在hwLabels向量中,使用前面讨论的img2vextor函数载入图像。在下一步中, 我们对testDigits目_录中的文件执行相似的操 作,不同之处是我们并不将这个目录下的文件载人矩阵中,而是使用classify0()函数测试该目录下的每个文件。

在Python命令提示符中输入:kNN.handwritingClassTest()

k -近邻算法识别手写数字数据集, 错误率为1. 2%。改变变量k的值、 修改函数handwritingClassTest随机选取训练样本、改变训练样本的数目,都会对女-近邻算法的错误率产生影响,感兴趣的话可以改变这些变量值,观察错误率的变化。实际使用这个算法时,算法的执行效率并不高。因为算法需要为每个测试向量做2000次距离计 算 ,每个距离计算包括了1024个维度浮点运算,总计要执行900次 ,此外,我们还需要为测试向量准备2 M B 的存储空间。

机器学习实战书-第二章K-近邻算法笔记的更多相关文章

  1. 《机器学习实战》---第二章 k近邻算法 kNN

    下面的代码是在python3中运行, # -*- coding: utf-8 -*- """ Created on Tue Jul 3 17:29:27 2018 @au ...

  2. 《机器学习实战之第二章k-近邻算法》

    入坑<机器学习实战>: 本书的第一个机器学习算法是k-近邻算法(kNN),它的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据 ...

  3. 《机实战》第2章 K近邻算法实战(KNN)

    1.准备:使用Python导入数据 1.创建kNN.py文件,并在其中增加下面的代码: from numpy import * #导入科学计算包 import operator #运算符模块,k近邻算 ...

  4. KNN K~近邻算法笔记

    K~近邻算法是最简单的机器学习算法.工作原理就是:将新数据的每一个特征与样本集中数据相应的特征进行比較.然后算法提取样本集中特征最相似的数据的分类标签.一般来说.仅仅提取样本数据集中前K个最相似的数据 ...

  5. 第2章 K近邻算法

    numpy中的tile函数: 遇到numpy.tile(A,(b,c))函数,重复复制A,按照行方向b次,列方向c次. >>> import numpy >>> n ...

  6. 《Java并发编程实战》第二章 线程安全性 读书笔记

    一.什么是线程安全性 编写线程安全的代码 核心在于要对状态訪问操作进行管理. 共享,可变的状态的訪问 - 前者表示多个线程訪问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与 ...

  7. 1.K近邻算法

    (一)K近邻算法基础 K近邻(KNN)算法优点 思想极度简单 应用数学知识少(近乎为0) 效果好 可以解释机器学习算法使用过程中的很多细节问题 更完整的刻画机器学习应用的流程 图解K近邻算法 上图是以 ...

  8. 02机器学习实战之K近邻算法

    第2章 k-近邻算法 KNN 概述 k-近邻(kNN, k-NearestNeighbor)算法是一种基本分类与回归方法,我们这里只讨论分类问题中的 k-近邻算法. 一句话总结:近朱者赤近墨者黑! k ...

  9. 机器学习实战笔记--k近邻算法

    #encoding:utf-8 from numpy import * import operator import matplotlib import matplotlib.pyplot as pl ...

随机推荐

  1. 在虚拟机上的关于FTP FTP访问模式(虚拟用户模式)

    首先你要有vsftpd服务 可以先去yum中下载(当然你要有本地yum仓库) 输入命令: yum  install  vsftpd 下载完成之后打开vsftpd服务 输入命令:systemctl   ...

  2. CVE-2019-16097:Harbor任意管理员注册漏洞复现

    0x00 Harbor简介 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distri ...

  3. C语言作业007

    问题 答案 这个作业属于那个课程 C语言程序设计1 这个作业要求在哪里 我在这个课程的目的是 学习并掌握C语言 这个作业在那个具体方面帮助我实现目标 参考文献 四 作业格式 1PTA作业贴图 1.1题 ...

  4. CAT客户端如何从Apollo中读取配置?

    运行环境 以下就是这个示例的运行环境,如果版本号不一样,区别也应该不会很大,可以根据实际情况做相应调整. JDK 8 spring boot 2.0.7.RELEASE cat-client 3.0. ...

  5. Java 实现一个 能够 进行简单的增删改查的 超市管理系统

    1. 首先编写一个 Fruitltem 的商品类, 描述 商品的基本信息. 代码如下: 保证详细, 运行的起来, 有什么 问题也可以评论留言. /* * 自定义类, 描述商品信息 * * 商品的属性: ...

  6. Numpy 中的比较和 Fancy Indexing

    # 导包 import numpy as np Fancy Indexing 应用在一维数组 x = np.arange(16) x[3] x[3:9] # array([3, 4, 5, 6, 7, ...

  7. [考试反思]1024csp-s模拟测试85:以为

    愈发垃圾. T1基本全场切(除了RP<-inf的zkt和把人擦) 然后T2想了半天逐渐趋近于正解,但是因为数据有问题锅了25分,没什么好说的.T3连题意转化都没有完成.括号匹配转为+1/-1做法 ...

  8. 单点登录 - API 认证系统 Passport(二)

    安装 composer require laravel/passport=~4.0 notes: 1)确保系统安装unzip.zip等命令. 2)composer 安装出现 Authenticatio ...

  9. ie浏览器兼容性的入门解决方案

    IE浏览器的兼容性素来是令人头疼的问题,大名鼎鼎的FUCK-IE不是浪得虚名的. 这里使用的解决方案是HACK,具体原理就是针对不同的浏览器写不同的HTML.CSS样式,从而使各种浏览器达到一致的渲染 ...

  10. Eclipse添加自定义注释

    首先介绍几个常用的注解: @author 作者名 @date 日期 @version 版本标识 @parameter 参数及其意义 @return 返回值 @throws 异常类及抛出条件 @depr ...