机器学习实战(第2章 k-近邻算法)

机器学习实战(第2章 k-近邻算法)

(代码下载地址:http://download.csdn.net/download/jichun4686/9910040

目录

这里写图片描述

简介:

K最近邻(k-Nearest Neighbor,KNN)分类算法,是最简单的机器学习算法之一。即是给定一个训练数据集,对于输入的未知标签的实例,在训练数据集中找到与该实例最邻近的K个实例,则将其分类到K个有大多数的相同标签的标签中。如在下边实线圈中绿色实例属于标签为红色的一类,而在虚线中其属于标签为蓝色的一类,这就要看K的取值大小。一般K<=20,且K为正整数。

这里写图片描述

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

这里写图片描述

2.1 基本k-近邻算法

代码1:

# -*- coding: utf-8 -*-   #中文注释记得加这句话
from numpy import *       #导入numpy包  用import numpy 也可以,但是之后的使用其中的函数时前边必须加上numpy.方法名
from operator import *

# 创建数据集
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                                        #返回数据集(数组)和标签(列表)


# k-近邻算法

""""
inX:    用于分类的输入向量
dataSet:训练样本集
labels: 标签向量
k:      选择的最近邻居数目
"""
def classify0(inX, dataSet, labels, k):
    #计算距离
    dataSetSize = dataSet.shape[0]  #dataSetSize为dataSet的行数
    diff = tile(inX,(dataSetSize,1)) - dataSet  #扩展输入向量,方便计算每一个数据到该向量的距离
    sqDiff = diff ** 2              #数组每个元素进行平方
    sqDistances = sqDiff.sum(axis=1)#求该数组的行和
    distances = sqDistances ** 0.5  #为每行元素进行开方 即求出了每个数据和输入数据的距离
    sortedDistIndicies = distances.argsort() #为该距离数组进行升序排序  返回排序结果的下标值

    #确定k个点并计算其频率
    classCount = {}  #声明一个统计K个数据中类别与相应个数的字典
    for i in range(k): #i从0到k-1进行循环
        votelabel = labels[sortedDistIndicies[i]] #找到i对应下标的类别
        classCount[votelabel] = classCount.get(votelabel,0)+1 #该类别如果在字典中存在,则取出其值后加1 如果不存在取默认值再+1
    sortedClassCount = sorted(classCount.items(),key=itemgetter(1),reverse=True)#将字典返回为元祖列表 并依据其第二个元素进行降序排序
    return sortedClassCount[0][0]  #返回频率最高元祖的第一个量即标签

输入

group,labels = createdataset() #得到数据集和标签向量
classify0([0.5,0.5],group,labels,3) #测试[0,0]向量的分类

测试结果1

这里写图片描述

此处我做了一些改动,将测试数据变为[0.5,0.5],因为书中提及,要测试的数据不能为分类器所知,但是[0,0]作为训练集中的样本数据已经提供给分类器,所以此处选取[0.5,0.5]。关于测试数据时,可以选取提前知道结果的数据进行输入,可以得到该分类器的错误率,进而可以对该分类器进行评估。

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

2.2.1将文本记录转换为Numpy的解析程序

代码2

创建文件DatingNet.py,程序如下。

from numpy import *

#程序1:将文本记录转换为Numpy的解析程序
#输入:文件名字符串
#输出:训练样本矩阵和类标签向量

def file2matrix(filename):
    fr = open(filename)
    arrayOfLines = fr.readlines()#得到一个元素为文件每一行的列表
    numberOfLines = len(arrayOfLines)#得到文件的行数
    numberOfCols = len(arrayOfLines[0].strip().split('\t')) #得到每一行的列数

    returnMat = zeros((numberOfLines,numberOfCols-1)) #返回的数据集
    classLabelVector = []                           #返回的标签向量
    index = 0                                       #为了给返回的数据集方便赋值的自增变量

    for line in arrayOfLines:
        listFromLine = line.strip().split('\t')     #将每一行去掉换行符并且以\t为分隔符分隔为列表

        returnMat[index,:] = listFromLine[0:3]      #将列表中的0、1、2号元素赋给returnMat的第一行
        classLabelVector.append(int(listFromLine[-1]))   #-1提取列表中的最后一个元素并将其标为int变量 必须明确的通知解释器 否则python语言会将这些元素做字符串处理
        index += 1

    return returnMat,classLabelVector

【ps】
1、书中我认为arrayOLines应该是arrayOfLines,故做修改,与书中不一样。
2、注意classLabelVector.append(int(listFromLine[-1])) 这一行代码不要少了int,必须明确告诉解释器,否则会作为字符串处理的。
3、另外列表取值时用[],而不是小括号,否则会产生TypeError: ‘list’ object is not callable的错误,但是百度还百度不到。

建立测试文件test2.py

import DatingNet

datingDataMat,datingLabels = DatingNet.file2matrix('datingTestSet2.txt')
print(datingDataMat)
print(datingLabels[0:20])

【ps】
1、书中输入的文件名为datingTestSet.txt,但是此处应输入datingTestSet2.txt.
2、书中得到的datingDataMat与文本文件中的也是不同。

运行结果2

这里写图片描述

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

建立figure_Matplotlib.py。
共绘制五幅图
图一:同书中,是第二三列数据所作图。但是没有色彩或者记号。
图二:同书中,是第二三列数据所作图。有色彩和记号。但是观测不出来数据间的关系。
图三:同书中,是一二列数据所作图。有色彩和记号,可以看出明显分类。
图四:增加,是一三列数据所作图。对比可知其分类也不是很明显。
图五:增加,是三维图,可以进行拖动、旋转,方便得到分类关系,更加便于观察。

【ps】 该代码画三维图时,有一个警告,但是限于自身能力,没有查出来,自己对于画图这块是刚刚接触,接来搞懂了会进行补充的。

代码3

from numpy import *                     #导入numpy包,这里要用到numpy中的array
from DatingNet import *                 #导入产生数据的包
import matplotlib                       #导入绘图的库
import matplotlib.pyplot as plt         #将绘图的函数重命名
from mpl_toolkits.mplot3d import Axes3D  #导入3维图像的包

datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
fig1 = plt.figure() #创建图形

#————————————————二维------------------------------------------

#第一个子图 是第2个特征和第3个特征的散点图  但是没有颜色标识  
ax = fig1.add_subplot(2,2,1)#代表创建1行1列从上到下的第一块的子图
ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
plt.xlabel(u'x  玩视频游戏所耗时间半分比')  
plt.ylabel(u'y  每周消费的冰淇淋公升数')
plt.title(u'图一(2&&3)')

#定义三个类别的空列表
type1_x = []
type1_y = []
type2_x = []
type2_y = []
type3_x = []
type3_y = []


#第二个子图 是第2个特征和第3个特征的散点图
ax = fig1.add_subplot(2,2,2)#代表创建1行1列从上到下的第二块的子图

#循环获得每个列表中的值
for i in range(len(datingLabels)):
    if datingLabels[i] == 1:  # 不喜欢
        type1_x.append(datingDataMat[i][1])
        type1_y.append(datingDataMat[i][2])

    if datingLabels[i] == 2:  # 魅力一般
        type2_x.append(datingDataMat[i][1])
        type2_y.append(datingDataMat[i][2])

    if datingLabels[i] == 3:  # 极具魅力
        type3_x.append(datingDataMat[i][1])
        type3_y.append(datingDataMat[i][2])

type1 = ax.scatter(type1_x, type1_y, s=20, c='red')
type2 = ax.scatter(type2_x, type2_y, s=40, c='green')
type3 = ax.scatter(type3_x, type3_y, s=50, c='blue')
ax.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2)#显示图例  1 右上  2左上 3左下 4 右下 逆时针
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
plt.xlabel(u'x  玩视频游戏所耗时间半分比')  
plt.ylabel(u'y  每周消费的冰淇淋公升数')
plt.title(u'图二(2&&3)')



#第三个子图 是第1个特征和第2个特征的散点图
ax = fig1.add_subplot(2,2,3)#代表创建1行1列从上到下的第三块的子图
#循环获得每个列表中的值
for i in range(len(datingLabels)):
    if datingLabels[i] == 1:  # 不喜欢
        type1_x.append(datingDataMat[i][0])
        type1_y.append(datingDataMat[i][1])

    if datingLabels[i] == 2:  # 魅力一般
        type2_x.append(datingDataMat[i][0])
        type2_y.append(datingDataMat[i][1])

    if datingLabels[i] == 3:  # 极具魅力
        type3_x.append(datingDataMat[i][0])
        type3_y.append(datingDataMat[i][1])

type1 = ax.scatter(type1_x, type1_y, s=20, c='red')
type2 = ax.scatter(type2_x, type2_y, s=40, c='green')
type3 = ax.scatter(type3_x, type3_y, s=50, c='blue')
ax.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2)#显示图例  1 右上  2左上 3左下 4 右下 逆时针
#ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15.0*array(datingLabels),15.0*array(datingLabels))
plt.xlabel(u'x  每年获取的飞行常客里程数')  
plt.ylabel(u'y  玩视频游戏所耗时间半分比')
plt.title(u'图三(1&&2)')

#第四个子图 是第1个特征和第3个特征的散点图
ax = fig1.add_subplot(2,2,4)#代表创建1行1列从上到下的第四块的子图
#循环获得每个列表中的值
for i in range(len(datingLabels)):
    if datingLabels[i] == 1:  # 不喜欢
        type1_x.append(datingDataMat[i][0])
        type1_y.append(datingDataMat[i][2])

    if datingLabels[i] == 2:  # 魅力一般
        type2_x.append(datingDataMat[i][0])
        type2_y.append(datingDataMat[i][2])

    if datingLabels[i] == 3:  # 极具魅力
        type3_x.append(datingDataMat[i][0])
        type3_y.append(datingDataMat[i][2])

type1 = ax.scatter(type1_x, type1_y, s=20, c='red')
type2 = ax.scatter(type2_x, type2_y, s=40, c='green')
type3 = ax.scatter(type3_x, type3_y, s=50, c='blue')
ax.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2)#显示图例  1 右上  2左上 3左下 4 右下 逆时针
#ax.scatter(datingDataMat[:,0],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
plt.xlabel(u'x  每年获取的飞行常客里程数')  
plt.ylabel(u'y  每周消费的冰淇淋公升数')
plt.title(u'图四(1&&3)')

#————————————————三维
#第二个图 是第1个特征和第2个特征和第3个特征的散点图
fig2 = plt.figure() #创建图形
ax = fig2.add_subplot(111)
ax = Axes3D(fig2)
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels),15.0*array(datingLabels))
ax.set_xlabel(u'x  每年获取的飞行常客里程数')
ax.set_ylabel(u'y  玩视频游戏所耗时间半分比')
ax.set_zlabel(u'z  每周消费的冰淇淋公升数')

plt.title(u'图四(1&&2&&3)')
plt.show()

运行结果3

图一 – 图四

这里写图片描述

图五(初始)

这里写图片描述

图五(旋转后)

这里写图片描述
由上可见,图像化工具给我们展示数据内容,更加方便去辨识一些数据模型。

2.2.3 准备数据:归一化数值
归一化数值是为了不让某个特殊的属性对计算结果影响比较大,而应该每一个特征都是等权重的。

转换公式: newValue = (oldValue - min)/(max - min)

代码4

import DatingNet

datingDataMat,datingLabels = DatingNet.file2matrix('datingTestSet2.txt')
normMat,ranges,minVals = DatingNet.autoNorm(datingDataMat)
print(normMat)
print(ranges)
print(minVals)

运行结果4

这里写图片描述

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

机器学习算法一个重要的工作就是评估算法的正确性,通常我们只提供90%的数据作为训练样本来训练分类器,而使用其余的10%进行测试分类器,以检测分类器的正确性。 在DatingNet.py中创建函数datingClassTest()函数,计算该分类器的错误率。 用到了DatingNet.py中的准备数据file2matrix()、归一化数值autoNorm()函数以及myKNN.py中的classify0()函数。

代码5:

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

    for i in range(numTestVecs):
        classifierResult = myKNN.classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) 
        print('the classifier came with back with:%d,the real answer is:%d'% (classifierResult,datingLabels[i]))
        if(classifierResult != datingLabels[i]):
            errorCount += 1  #统计错误量
    print('错误率是:%f'%(errorCount/float(numTestVecs)))  #输出错误率

运行结果5

运行datingNet.py,输入datingClassTest()得到结果如下(与书中不同,与网上多数网友结果相同):

这里写图片描述

分类器结果是5%,算是一个还不错的结果。所以基本上可以满足约会人对于某一对象的可交往程度的判定。

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

测试通过后,可以进行应用。在datingNet.py中建立函数classifyPerson()函数,通过在约会网站找到某个人并输入他的信息,程序给对对方喜欢程度的预测值。 因python之后就没有raw_input,而是用input代替,所以代码中与书中不同,如果环境为python3还用raw_input就会产生NameError: name ‘raw_input’ is not defined的错误。

代码6

#约会网站预测函数

def classifyPerson():
    resultList = ['nont at all','in small doses','in large doses']

    #用户输入的三个特征值
    percentTats=float(input('percentage of time spent playing video games?'))    
    ffMiles=float(input('frequent flier miles earned per year?'))
    iceCream=float(input('liters of ice cream consumed per year?'))
    inArr = array([ffMiles,percentTats,iceCream])

    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
    normMat,ranges,minVals = autoNorm(datingDataMat)
    classifierResult = myKNN.classify0((inArr-minVals)/ranges,normMat,datingLabels,3)

    print('you will probably like this person:',resultList[classifierResult-1])

运行结果6

这里写图片描述

2.3 实例2:手写识别系统

该系统只能识别数字0-9,图像转换成为了文本的若干文本数据。

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

建立文件hardWritingSystem.py,在其中写入img2vector()函数。

代码7

#将一个图像文件转换为一维向量
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

运行结果7

运行该文件,输入testVector = img2vector(‘digits/trainingDigits/0_0.txt’) 然后输出前32个testVector[0,0:31]。

这里写图片描述

2.3.2 使用k-近邻算法识别手写数字

代码8

def handWrithingClassTest():
    hwLabels = [] #定义一个空列表   记录标签向量
    trainingFileList = listdir('digits/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('digits/trainingDigits/%s' % fileNameStr) #创建训练集

    #创建测试集和标签

    errorCount = 0.0 #统计错误个数
    testFileList = listdir('digits/testDigits') #得到存放测试数据的文件列表
    mTest = len(testFileList)

    for i in range(mTest):
        fileNameStr = testFileList[i]  #得到文件名和扩展名
        fileStr = fileNameStr.split('.')[0] #得到文件名
        classNumStr = int(fileStr.split('_')[0])#得到该文件的存储的数字

        vectorUnderTest = img2vector('digits/testDigits/%s' % fileNameStr)  #得到测试集

        classifierResult = myKNN.classify0(vectorUnderTest,trainingMat,hwLabels,3)  #计算结果

        #print ("the classifier came back with: %d, the read answer is:%d" %(classifierResult,classNumStr) )

        if (classifierResult !=classNumStr):#计算错误率
            errorCount += 1.0
    print ("the total number of errors is %d" % errorCount)  #输出错误个数
    print ("the total error rate is: %f" % (errorCount/float(mTest)))#输出错误率

运行结果8

这里写图片描述

错误率为1.06%,改变k值、训练、测试样本错误率会有所变化。

实际使用这个算法时,执行效率并不是很高,因为数据量太大会产生额外的时间、空间开销。而决策树就是k-近邻的优化版,可以节省大量的计算开销。

总述

k-近邻算法花了好一段时间终于搞定了,其中的一些python知识也详细的去查过,虽然假期的效率并没有太高,但是觉得每一步都走精也是一种收获,下面对k-近邻进行一下总结。

本章通过基础的k-近邻算法和两个例子进行了讲解和说明,主要就是将测试数据或者训练数据转换为要输入矩阵的格式,可能用到了归一化,然后使用k-近邻进行解析数据从而返回预测结果。其中还设计数据的绘图、错误率的计算以及大量的python矩阵知识。关于本章中python的知识,由于内容量太大,我放到了单独的博客中,供以没学过python的人参考。

k-近邻有简单有效的优点、但是时空开销比较大,所以引进下一章可以节省大量的计算开销的决策树。

(代码下载地址:http://download.csdn.net/download/jichun4686/9910040
【2017.7.25决策树!!!!】

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值