机器学习-- K-近邻算法(KNN)

一.问题提出

众所周知,电影可以按照题材分类,然而题材本身是如何定义的?怎么来判定某部电影属于哪个题材?动作片中也会存在接吻镜头,而爱情片中同样存在打斗场景,我们不能单纯依靠是否存在打斗或者亲吻来判断影片的类型。但是我们发现爱情片中的亲吻镜头更多,动作片中的打斗场景也更频繁,基于此类场景在某部电影中出现的次数,我们可以通过使用k-近邻算法构造程序,对电影进行分类。

二.K-近邻算法概述

简单来说,K-近邻算法采用不同特征值之间的距离方法进行分类。

优点:精度高、对异常值不敏感、无数据输入假定

缺点:计算量大,空间复杂度高,与决策树模型相比,KNN算法的可解释性不强

适用数据范围:数值型和标称型

三.K-近邻算法的工作原理

K-近邻算法(K-NearestNeighbor)的工作原理:对于一个新样本,K近邻算法的目的就是在已有数据中寻找与它最相似的K个数据,或者说是离它最近的K个邻居,并在这K个数据里进行统计,找到对应数据最多的类别,则该样本也属于这个类别。下面就以刚刚所提出的如何辨别动作片和爱情片为例,对KNN算法进行解释。

电影名称打斗镜头接吻镜头电影类型
California Man3104爱情片
He‘s Not Really into Dudes2100爱情片
Beautiful Woman181爱情片
Kevin Longblade10110动作片
Robo Slayer 3000995动作片
Amped ll982动作片
1890未知

在KNN算法中,我们采用欧式距离公式,计算出未知电影和已知电影的距离。

 \huge l=\sqrt{\left ( x_{A0}-x_{B0} \right )^{2}+\left (x _{A1}-x_{B1} \right )^{2}}

电影名称与未知电影的距离
California Man20.5
He‘s Not Really into Dudes18.7
Beautiful Woman19.2
Kevin Longblade115.3
Robo Slayer 3000117.4
Amped ll118.9

现在我们得到了样本集中所有电影与未知电影的距离,所以我们可以找到k个距离最近的电影。假设k=3,则最靠近的三部电影是California Man、He‘s Not Really into Dudes、Beautiful Woman,这三部电影的类别都是爱情片,故我们判定未知电影是爱情片。

四.K-近邻算法的一般流程

理解了KNN算法的基本原理后,还需要知道KNN算法的一般流程

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

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

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

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

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

(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分          别属于哪个分类,最后应用对计算出的分类执行后续的处理。

五.K-近邻算法的代码实现

  1.使用python导入数据

        导入4组数据,每组数据有两个我们已知的属性或者特征值。group矩阵每行包含一组不同的数据,向量label包含了每个数据点的标签信息,其中label包含的元素个数等于group矩阵行数。

from 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

2.K-近邻算法实现

def classify(inX,dataSet,labels,k):
    dataSetSize=dataSet.shape[0]
    diffMat=tile(inX,(dataSetSize,1))-dataSet#求差
    sqDiffMat=diffMat**2
    sqDistances=sqDiffMat.sum(axis=1)#求平方和
    distances=sqDistances**0.5
    sortedDistIndicies=distances.argsort()#argsort()函数得到从小到大的索引值
    classCount={}
    for i in range(k):         #选择距离最小的k个点
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1  #统计每个类别出现的次数
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #迭代对象是字典的键值对,根据value值进行降序排序
    return sortedClassCount[0][0]

3.测试结果

if __name__=='__main__':
    group,labels=createDataSet()
    print(classify([1,1],group,labels,3))

输入点的坐标是(1,1),最后得到结果为A

六.K-近邻算法的实际运用

1.项目简介

数据集来自集美大学acm校选,数据集包括三个特征值,一个分类值。其中特征值分别为得分,用时,年级。类别分为三类,1代表入选acm校队,2代表未入选但得到综测分,3代表未取得综测分。

数据集

 

2.编写knn算法

#knn算法
def classify(inX,dataSet,labels,k):
    dataSetSize=dataSet.shape[0]
    diffMat=tile(inX,(dataSetSize,1))-dataSet#求差
    sqDiffMat=diffMat**2
    sqDistances=sqDiffMat.sum(axis=1)#求平方和
    distances=sqDistances**0.5
    sortedDistIndicies=distances.argsort()#argsort()函数得到从小到大的索引值
    classCount={}
    for i in range(k):         #选择距离最小的k个点
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel]=classCount.get(voteIlabel,0)+1  #统计每个类别出现的次数
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #迭代对象是字典的键值对,根据value值进行降序排序
    return sortedClassCount[0][0]

3.导入数据

为了使数据更有代表性,我们在导入数据这一步将数据集打乱

#导入数据
def file2matrix(filename):
    fr = open(filename) #打开文件
    arrayOLines = fr.readlines()#读取所有行
    random.shuffle(arrayOLines)#将数据打乱
    numberOfLines = len(arrayOLines)
    returnMat = zeros((numberOfLines,3))
    classLabelVector=[]
    index=0
    for line in arrayOLines:
        line =line.strip() #删除一行中首尾的空白符
        listFromLine=line.split(' ')#字符按照空格截取
        returnMat[index,:] = listFromLine[0:3] #将数据的前三列提取出来,存放在特征矩阵中
        classLabelVector.append(listFromLine[-1])
        index += 1
    return returnMat,classLabelVector

4.数据归一化

三个特征是同等重要的,为了使三个特征等权重,采用数据归一化是一种常用的方法。

         newValue = \left (\frac{oldValue-min}{max-min} \right )

oldValue是需要归一化的数据,max是对应列的最大值,min是对应列的最小值

#数据归一化
def autoNorm(dataSet):
    minVals=dataSet.min(0)
    maxVals=dataSet.max(0)
    ranges=maxVals-minVals
    normDataSet=zeros(shape(dataSet)) #生成与dataset同类型的零矩阵
    m = dataSet.shape[0] #读取行数
    normDataSet=dataSet-tile(minVals,(m,1)) #分子
    normDataSet=normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals

5.计算错误率

在knn算法里,最为重要的是如何确定k的值。在这一步中,我将数据集(已打乱)的%20作为测试集,使用其余的%80作为特征样本。通过计算错误率,我们可以进一步确定k值得范围,从而找到最优解。

#计算错误率
def datingClassTest():
    hoRatio=0.20
    datingDataMat,datingLabels = file2matrix("data.txt")
    normMat, ranges, minVales = autoNorm(datingDataMat)
    m=normMat.shape[0]
    numTestVecs = int(m*hoRatio)#取出测试行数
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)  #numTestVecs个为输入的数据,剩下的作为原数据
        print ("the classifier came back with: %s, the real answer is: %s"%(classifierResult, datingLabels[i]))
        if(classifierResult != datingLabels[i]):
            errorCount +=1.0
    print("the total error rate is",{errorCount/float(numTestVecs)})

6.绘画三维图

if __name__=='__main__':
    datingClassTest()
    datingDataMat,datingLabels=file2matrix("data.txt")
    normMat,ranges,minVals=autoNorm(datingDataMat)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.set_title('3d_image_show')
    ax.set_xlabel('score')
    ax.set_ylabel('time')
    ax.set_zlabel('grade')
    index=0
    color={1:'blue',2:'red',3:'green'}#分配颜色
    shape={1:'o',2:'^',3:'*'}
    for x,y,z in normMat:
        ax.scatter(x,y,z,c=color[int(datingLabels[index])],marker=shape[int(datingLabels[index])])#获取每一个点的标签值
        index=index+1
    plt.show()

7.结果显示

 8.穷举k

 为了寻找最优的k值,我采用的是穷举的方法。

def datingClassTest():
    hoRatio=0.20
    datingDataMat,datingLabels = file2matrix("data.txt")
    normMat, ranges, minVales = autoNorm(datingDataMat)
    m=normMat.shape[0]
    numTestVecs = int(m*hoRatio)#取出测试行数
    numSample = int(m*(1-hoRatio))
    errorCount = 0.0
    for k in range(1,numSample+1):
        errorCount=0.0
        for i in range(numTestVecs):
            classifierResult = classify(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],k)  #numTestVecs个为输入的数据,剩下的作为原数据
            print ("the classifier came back with: %s, the real answer is: %s"%(classifierResult, datingLabels[i]))
            if(classifierResult != datingLabels[i]):
             errorCount +=1.0
        print("the total error rate is",{errorCount/float(numTestVecs)})

 

...

 

通过运行,我发现在这个数据集中,随着k值的增大,错误率会慢慢变大。

9.实验总结与反思

总结:通过knn算法,我们可以实现基本的分类问题。k值较大,比较稳定,但过于平滑。k值较小,分类结果容易受噪声影响,出现过拟合。

反思:由于数据较少,在计算错误率时发现,无法更加细致地算出错误率的多少,对k值的判断不够准确。

10.参考教材

机器学习实战

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值