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

Python机器学习实战之K-近邻算法概述

2018-03-26 10:26 351 查看

一. k-近邻算法的概述

       简单的说,k-近邻算法采用测量不同特征值之间的距离方法进行分类,其工作原理:存在一个样本数据集,样本集中每个数据均有标签,即知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中的数据对应的特征进行比较,然后算法提取样本集中特征最相似数据的分类标签。通产只选择前k个最相似的数据。
k-近邻算法的一般流程:

收集数据:可以使用任何方法
准备数据:距离计算所需要的数值,最好是结构化的数据格式
分析数据:可以使用任何方法
训练算法:此步骤不适用与k-近邻算法
测试算法:计算错误率
使用算法:首先需要输入样本数据和结构化的输出成果,然后运行k-近邻算法判定输入数据属于哪个分类,最后应用对计算出的分类执行后续的处理

二. k-近邻算法的简单分类算法实现

2.1.1from numpy import *
import operator

def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group,labels #py允许返回多个不同类型的值在上面的代码中,我们导入了两个模块:科学计算包和运算符模块,此外creatDataSet函数创建了数据集和标签,两者一一对应,即A(1.0,1.1) A(1,1) B(0,0) B(0,0.1)
2.1.2 实施kNN分类算法
该分类算法的伪代码如下,对未知类别属性的数据集中每个点依次执行以下操作:

计算已知类别的数据集中的点与当前点之间的距离
按照距离递增次序排序
选取与当前点距离最小的k个点
确定前k个点所在类别出现的频率
返回前k个点出现的频率最高的点作为当前点的预测分类
#描述k近邻算法
def classify0(inX,dataSet,labels,k): #用于分类的输入向量是inX(需要判别的坐标),输入的训练样本集是dataset,标签向量为labels k为近邻算法的k
dataSetSize = dataSet.shape[0] #返回矩阵的行数
diffMat = tile(inX,(dataSetSize,1)) - dataSet
#tile构造的结果是[[0,0],[0,0],[0,0],[0,0]] diffmat为坐标差的矩阵
sqDiffMat = diffMat**2 #将列表中各元素二次方
sqDistance = sqDiffMat.sum(axis=1) #将矩阵各行元素相加
distances = sqDistance**0.5
sortedDis = distances.argsort() #将对应的索引排序
classCount = {}
for i in range(k): #统计距离最近的k个点的类别
label = labels[sortedDis[i]]
classCount[label] = classCount.get(label,0) + 1
sortedClass = sorted(classCount.items(),key = lambda item:item[1],reverse = True)
return sortedClass[0][0]测试数据[0,0]得到结果B,测试数据[1,1.1]得到结果A

2.2  示例:使用k-近邻算法改进约会网站的配对效果
     单身女士一直使用在线约会网站寻找适合自己的约会对象,尽管网站会推荐不同的人选,但她并不是喜欢每一个人。经过总结,她发现她曾经交往过的三种类型的人:不喜欢的人,魅力一般的人,极具魅力的人。因而网站需要收集各男士信息以划分类别,从而有助于女士们筛选。
2.2.1 准备数据:从文本文件中解析数据40920 8.326976 0.953952 3
14488 7.153469 1.673904 2
26052 1.441871 0.805124 1
75136 13.147394 0.428964 1
38344 1.669788 0.134296 1
72993 10.141740 1.032955 1
35948 6.830792 1.213192 3每个样本数据占据一行,样本数据包含以下三种特征:每年飞行里程数,玩视频游戏所耗时间百分比,每周消费的冰淇淋公升数,下面的代码将文本文档中的数据变为可分析的格式
#文本文件保留了样本的特征值
def file2Matrix(filename):
file = open(filename)
fileLines = file.readlines()
numberOfLine = len(fileLines)
returnMat = zeros((numberOfLine,3))  #用0填充矩阵 指定行号与列号
classLabelVector = []
index = 0
for line in fileLines:
line = line.strip() #截取掉所有回车字符
listFormLine = line.split('\t')  #分割成一个元素列表‘
returnMat[index,:] = listFormLine[0:3] #将元素列表赋值给矩阵
classLabelVector.append(int(listFormLine[-1]))  #提取类别
index = index + 1
return returnMat,classLabelVector
2.2.2   分析数据:使用Matplotlib创建散点图import kNN
import matplotlib
import matplotlib.pyplot as plt
from numpy import *

if __name__ == '__main__':
datingDataMat,datingLabels = kNN.file2Matrix('C:/Users/lpw007/Desktop/datingTestSet2.txt')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()结果如下:



2.2.3  准备数据:归一化数值
从样本数据的特征值中可以发现,数值差值最大的属性对计算结果的影响最大,如飞行里程数对于计算结果的影响远远大于其他两个特征值。但女士们认为这三个特征同等重要,因此我们在处理这种不同取值范围的特征值时,通常采用的方法是将数值归一化,即newValue = (oldValue - minVal)/(maxVal - minVal)#归一化特征值 new = (old - min)/(max - min)
def autoNorm(dataSet):
minvals = dataSet.min(0) #参数0使函数从列中寻找最小值
maxvals = dataSet.max(0)
ranges = maxvals - minvals
normDataSet = zeros(shape(dataSet)) #创建0填充的矩阵
m = dataSet.shape[0] #矩阵的行数
normDataSet = dataSet - tile(minvals,(m,1)) #计算old - min
normDataSet = normDataSet/tile(ranges,(m,1)) #得出(old - min)/(max - min)
return normDataSet,ranges,minvals #返回归一化处理的特征值矩阵 差值 最小值在函数autoNorm()中,我们将每一列的最小值放在minvals中,将最大值放在maxvals中,其中min(0)的参数0使得函数可以从列中选取最小值,而不是当前行的最小值。为了归一化特征值,我们必须使用当前值减去最小值,然后除以取值范围。需要注意的是,特征值矩阵有1000*3个值,而minVals和range的值都为1*3。为了解决这个问题,使用numpy库中tile( )函数将变量内容复制成输入矩阵同样大小的矩阵。
下面给出分类器的测试代码,测试该算法的错误率datingDataMat,datingLabels = kNN.file2Matrix('C:/Users/lpw007/Desktop/datingTestSet2.txt')
normMat,ranges,minvals = kNN.autoNorm(datingDataMat)
hoRatio = 0.50
m = normMat.shape[0]
numTest = int(m*hoRatio)
errorCount = 0.0
for i in range(numTest):
classifierResult = kNN.classify0(normMat[i,:],normMat[numTest:m,:],datingLabels[numTest:m],20)
print("result:%d,the real:%d" %(classifierResult,datingLabels[i]))
if(classifierResult != datingLabels[i]): errorCount += 1.0
print("error rate:%f" % (errorCount/float(numTest)))通过调整hoRatio和k的值来检测错误率是否随着变量值的增加而变化。通过测试之后,若错误率在可期范围内,则可以正式投入使用。下面代码对输入的男士信息进行分类,从而判断是否为喜欢的类型#输入某个人的参数 判断类别
def classfiPerson():
resultList = ['not at all','small doge','large doge']
gamePercent = float(input("percentage of playing game:"))
4000
flightMiles = float(input('fly miles:'))
iceCream = float(input('icecream:'))
datingDataMat, datingLabels = file2Matrix('C:/Users/lpw007/Desktop/datingTestSet2.txt')
normMat, ranges, minvals = kNN.autoNorm(datingDataMat)
inArr = array([flightMiles,gamePercent,iceCream])
classifyResult = classify0((inArr-minvals)/ranges,normMat,datingLabels,3)
print('you will like this person:',resultList[classifyResult - 1])2.3  示例:手写识别系统






如上两图所示,图2的txt文件0_1.txt表示描述数字0的图像一,图一为txt文件的内容,其通过32*32的矩阵表示数字图像,为了使用之前写好的分类算法classify0函数,这里将32*32转化为1*1024的NumPy数组,如下函数做到这点#将图像转化成测试向量 我们将32*32的0和1组成的图像转化成1*1024的向量(为了使用之前定义好的分词器)
def imgVerctor(filename):
returnVect = zeros((1,1024))
fr = open(filename)
for i in range(32):
line = fr.readline()
for j in range(32):
returnVect[0,i*32+j] = int(line[j])
return returnVect上面我们已经将数据转化成分类器可处理的格式,接下来我们将trainingDigits文件夹下对应的数据转化成数据集,将testDigits文件夹下的txt文件中的数据转化为测试数据,下面代码实现这些功能#测试算法
def handWritingTest():
hwLabels = []
trainingFileList = listdir('trainingDigits')
m = len(trainingFileList) #返回目录数
trainingMat = zeros((m,1024)) #将所有训练数据用trainingMat存储
for i in range(m):
fileNameStr = trainingFileList[i] #txt文件名
fileStr = fileNameStr.split('.')[0]
classNum = int(fileStr.split('_')[0])
hwLabels.append(classNum) #记录对应的数字标签列表
trainingMat[i,:] = imgVerctor('trainingDigits/%s' %fileNameStr)
testFileList = listdir('testDigits')
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNum = int(fileStr.split('_')[0])
verctorTest = imgVerctor('testDigits/%s' %fileNameStr)
classResult = classify0(verctorTest,trainingMat,hwLabels,3)
print('result:%d,real:%d'%(classResult,classNum))
if(classResult != classNum):errorCount += 1.0
print('errorRate:%f' %(errorCount/float(mTest)))得出的错误率为1.0%,修改k值,修改训练样本等会影响错误率。值得注意的是,使用该算法执行时间过长,执行效率并不高。因为算法需要对每个测试向量进行2000次距离计算,每次距离计算包括1024个维度,总共有900条测试数据。是否存在一种算法能减少存储空间和计算时间的开销呢?k-决策树就是k-近邻算法的改进版,可以节省大量的计算开销。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: