美文网首页
kNN算法实例之约会网站

kNN算法实例之约会网站

作者: 故梦_三笙 | 来源:发表于2019-06-01 14:58 被阅读0次

    好久没更新了,最近在忙着写论文,昨天我的新书机器实战到了,于是就连夜学了第一个实例。

    1、k-近邻法简介

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

    举个简单的例子,我们可以使用k-近邻算法分类一个电影是爱情片还是动作片。

    机器学习实战教程(一):K-近邻算法(史诗级干货长文)

    表1.1 每部电影的打斗镜头数、接吻镜头数以及电影类型

    表1.1 就是我们已有的数据集合,也就是训练样本集。这个数据集有两个特征,即打斗镜头数和接吻镜头数。除此之外,我们也知道每个电影的所属类型,即分类标签。用肉眼粗略地观察,接吻镜头多的,是爱情片。打斗镜头多的,是动作片。以我们多年的看片经验,这个分类还算合理。如果现在给我一部电影,你告诉我这个电影打斗镜头数和接吻镜头数。不告诉我这个电影类型,我可以根据你给我的信息进行判断,这个电影是属于爱情片还是动作片。而k-近邻算法也可以像我们人一样做到这一点,不同的地方在于,我们的经验更"牛逼",而k-近邻算法是靠已有的数据。比如,你告诉我这个电影打斗镜头数为2,接吻镜头数为102,我的经验会告诉你这个是爱情片,k-近邻算法也会告诉你这个是爱情片。你又告诉我另一个电影打斗镜头数为49,接吻镜头数为51,我"邪恶"的经验可能会告诉你,这有可能是个"爱情动作片",画面太美,我不敢想象。 (如果说,你不知道"爱情动作片"是什么?请评论留言与我联系,我需要你这样像我一样纯洁的朋友。) 但是k-近邻算法不会告诉你这些,因为在它的眼里,电影类型只有爱情片和动作片,它会提取样本集中特征最相似数据(最邻近)的分类标签,得到的结果可能是爱情片,也可能是动作片,但绝不会是"爱情动作片"。当然,这些取决于数据集的大小以及最近邻的判断标准等因素。

    2、距离度量

    我们已经知道k-近邻算法根据特征比较,然后提取样本集中特征最相似数据(最邻近)的分类标签。那么,如何进行比较呢?比如,我们还是以表1.1为例,怎么判断红色圆点标记的电影所属的类别呢? 如下图所示。

    机器学习实战教程(一):K-近邻算法(史诗级干货长文)

    我们可以从散点图大致推断,这个红色圆点标记的电影可能属于动作片,因为距离已知的那两个动作片的圆点更近。k-近邻算法用什么方法进行判断呢?没错,就是距离度量。这个电影分类的例子有2个特征,也就是在2维实数向量空间,可以使用我们高中学过的两点距离公式计算距离,如图1.2所示。

    机器学习实战教程(一):K-近邻算法(史诗级干货长文)

    通过计算,我们可以得到如下结果:

    • (101,20)->动作片(108,5)的距离约为16.55
    • (101,20)->动作片(115,8)的距离约为18.44
    • (101,20)->爱情片(5,89)的距离约为118.22
    • (101,20)->爱情片(1,101)的距离约为128.69

    通过计算可知,红色圆点标记的电影到动作片 (108,5)的距离最近,为16.55。如果算法直接根据这个结果,判断该红色圆点标记的电影为动作片,这个算法就是最近邻算法,而非k-近邻算法。那么k-近邻算法是什么呢?k-近邻算法步骤如下:

    1. 计算已知类别数据集中的点与当前点之间的距离;
    2. 按照距离递增次序排序;
    3. 选取与当前点距离最小的k个点;
    4. 确定前k个点所在类别的出现频率;
    5. 返回前k个点所出现频率最高的类别作为当前点的预测分类。

    比如,现在我这个k值取3,那么在电影例子中,按距离依次排序的三个点分别是动作片(108,5)、动作片(115,8)、爱情片(5,89)。在这三个点中,动作片出现的频率为三分之二,爱情片出现的频率为三分之一,所以该红色圆点标记的电影为动作片。这个判别过程就是k-近邻算法。

    代码实现步骤

    准备数据:从文本文件中解析数据
    分析数据:使用Matplotlib创建散点图
    准备数据:归一化数值
    测试算法:作为完整程序验证分类器
    使用算法:构建完整可用系统

    from numpy import *
    from matplotlib.font_manager import FontProperties
    import matplotlib.lines as mlines
    import  matplotlib.pyplot as plt
    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
    ''''
    函数说明:knn算法,分类器,inx是输入的向量,dataset是数据集,labels是标签
    '''''
    def classify0(inX, dataSet, labels, k):
        dataSetSize = dataSet.shape[0]
        diffMat = tile(inX, (dataSetSize,1))-dataSet
        sqDiffMat = diffMat**2
        sqDistance = sqDiffMat.sum(axis=1)
        distances = sqDistance**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)
        return sortedClassCount[0][0]
    '''
    函数说明:打开解析文件
    filename:文件名
    returnmat:特征矩阵
    classlabelvector;标签列表
    '''
    def file2matrix(filename):
        fr = open(filename)
        arrayOLines = fr.readlines()
        numberOfLines = len(arrayOLines)
        returnMat = zeros((numberOfLines,3))#创建一个行*3的0矩阵
        classLabelVector = []
        index = 0
        for line in arrayOLines:
            line = line.strip() #去除每一行的空格或换行符
            listFromLine = line.split("\t") #按照回车分割每一行为每一个数组
            returnMat[index, :] = listFromLine[0:3]#把每一行数据添加到之前创建的0矩阵之中
            #print(listFromLine)
            #classLabelVector.append(str(listFromLine[-1]))
            """"
            if listFromLine[-1] == "didntLike":
                classLabelVector.append(1)
            elif listFromLine[-1] == "smallDoses":
                classLabelVector.append(2)
            elif listFromLine[-1] == "largeDoses":
                classLabelVector.append(3)
                """
            classLabelVector.append(int(listFromLine[-1]))
            index += 1
        return returnMat, classLabelVector
    """
    函数说明:分析数据,数据可视化
    
    """
    def showdatas(datingDataMat,datingLabels):
        # 设置汉字格式
        font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
        # 将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8)
        # 当nrow=2,nclos=2时,代表fig画布被分为四个区域,axs[0][0]表示第一行第一个区域
        fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 8))
    
        numberOfLabels = len(datingLabels)
        LabelsColors = []
        for i in datingLabels:
            if i == 1:
                LabelsColors.append('black')
            if i == 2:
                LabelsColors.append('orange')
            if i == 3:
                LabelsColors.append('red')
        # 画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第二列(玩游戏)数据画散点数据,散点大小为15,透明度为0.5
        axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelsColors, s=15, alpha=.5)
        # 设置标题,x轴label,y轴label
        axs0_title_text = axs[0][0].set_title(u'每年获得的飞行常客里程数与玩视频游戏所消耗时间占比', FontProperties=font)
        axs0_xlabel_text = axs[0][0].set_xlabel(u'每年获得的飞行常客里程数', FontProperties=font)
        axs0_ylabel_text = axs[0][0].set_ylabel(u'玩视频游戏所消耗时间占', FontProperties=font)
        plt.setp(axs0_title_text, size=9, weight='bold', color='red')
        plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')
        plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')
    
        # 画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
        axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
        # 设置标题,x轴label,y轴label
        axs1_title_text = axs[0][1].set_title(u'每年获得的飞行常客里程数与每周消费的冰激淋公升数', FontProperties=font)
        axs1_xlabel_text = axs[0][1].set_xlabel(u'每年获得的飞行常客里程数', FontProperties=font)
        axs1_ylabel_text = axs[0][1].set_ylabel(u'每周消费的冰激淋公升数', FontProperties=font)
        plt.setp(axs1_title_text, size=9, weight='bold', color='red')
        plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')
        plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')
    
        # 画出散点图,以datingDataMat矩阵的第二(玩游戏)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
        axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
        # 设置标题,x轴label,y轴label
        axs2_title_text = axs[1][0].set_title(u'玩视频游戏所消耗时间占比与每周消费的冰激淋公升数', FontProperties=font)
        axs2_xlabel_text = axs[1][0].set_xlabel(u'玩视频游戏所消耗时间占比', FontProperties=font)
        axs2_ylabel_text = axs[1][0].set_ylabel(u'每周消费的冰激淋公升数', FontProperties=font)
        plt.setp(axs2_title_text, size=9, weight='bold', color='red')
        plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')
        plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')
        # 设置图例
        didntLike = mlines.Line2D([], [], color='black', marker='.',
                                  markersize=6, label='didntLike')
        smallDoses = mlines.Line2D([], [], color='orange', marker='.',
                                   markersize=6, label='smallDoses')
        largeDoses = mlines.Line2D([], [], color='red', marker='.',
                                   markersize=6, label='largeDoses')
        # 添加图例
        axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses])
        axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses])
        axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses])
        # 显示图片
        plt.show()
        """""
        函数说明:dataSet是数据集,将数据集转化为0-1之间的数,公式为 
        newvalue = (oldvalue - min)/(max - min)
        """
    def autoNorm(dataSet):
        #min(0)是取当前列最小值,min(1)是当前列最大值,max同理
        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))
        return normDataSet, ranges, minVals
    def datingClassTest():
        hoRatio = 0.10
        datingDataMat, datingLabels = file2matrix("datingTestSet.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( "分类器结果:%d, 真实结果:%d" %(classifierResult,datingLabels[i]))
            if(classifierResult!=datingLabels[i]) :
                errorCount += 1.0
        print( "分类器的错误率是%f" %(errorCount/float(numTestVecs)))
    def classifyPerson():
        resultList = ["讨厌", "有点喜欢", "非常喜欢"]
        percentTats = float(input("玩视频游戏所占百分比:"))
        ffMiles = float(input("每年飞行里程数:"))
        iceCream = float(input("每周吃的冰淇淋公升数:"))
        datingDataMat, datingLabels = file2matrix("datingTestSet2.txt")
        normMat, ranges, minVals = autoNorm(datingDataMat)
        inArr = array([ffMiles, percentTats, iceCream])
        norminArr = (inArr - minVals) / ranges
        classifierResult = classify0(norminArr, normMat, datingLabels, 3)
        print("这个人你应该%s"%(resultList[classifierResult-1]))
    if __name__ == '__main__':
        classifyPerson()
    

    相关文章

      网友评论

          本文标题:kNN算法实例之约会网站

          本文链接:https://www.haomeiwen.com/subject/wzfatctx.html