机器学习随笔2--k近邻

摘要

  本文主要介绍k近邻,以及k近邻在数据集上的应用。

目录

一、k-近邻算法原理

1.1 k-近邻算法介绍

  K近邻分类算法(k-Nearest Neighbor,KNN),是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。
  K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个实例的多数属于某个类,就把该输入实例分类到这个类中。

1.2 计算步骤

(1)计算测试对象与训练集中所有对象的距离,可以是欧式距离、余弦距离等,比较常用的是较为简单的欧式距离;
(2)找出上步计算的距离中最近的K个对象,作为测试对象的邻居;
(3)找出K个对象中出现频率最高的对象,其所属的类别就是该测试对象所属的类别。

1.3 数学表达:

  输入:输入训练集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) }
其中, x 是样本的特征向量, y 是样本的类别, T 是一个特征空间,包含输入的样本
  过程:
(1)根据给定的距离度量,在训练集 T 中找出与 x 最邻近的k个点,包含这k个点的x的邻域记作 N ( k ) ,这里不要纠结邻域是什么,简单来说,邻域就是x点组成的集合域
(2)在 N ( k ) 中根据分类决策规则(如多数表决)决定 x 的类别 y

  输出: x 所属的类别 y

1.4 算法的三要素:

1.4.1 k的选择:

  k的取值对结果会有很大的影响。
  k值设置过小会降低分类精度;若设置过大,且测试样本属于训练集中包含数据较少的类,则会增加噪声,降低分类效果。
  通常,k值的设定采用交叉检验的方式(以kK=1为基准)
  经验规则:k一般低于训练样本数的平方根。

1.4.2 距离度量

  在KNN算法中,常用的距离有三种,分别为曼哈顿距离、欧式距离和闵可夫斯基距离。

1.4.3 分类决策规则

  KNN做分类预测时,一般是选择多数表决法,即训练集里和预测的样本特征最近的K个样本,预测为里面有最多类别数的类别。而KNN做回归时,一般是选择平均法,即最近的K个样本的样本输出的平均值作为回归预测值。由于两者区别不大,虽然本文主要是讲解KNN的分类方法,但思想对KNN的回归方法也适用。由于scikit-learn里只使用了蛮力实现(brute-force),KD树实现(KDTree)和球树(BallTree)实现,本文只讨论这几种算法的实现原理。

二、数据集说明

2.1 数据的获取

  自2001 年以来,世界睡眠医学协会每年将3 月21 日定为世界睡眠日,以引起人们对睡眠的重要性以及睡眠质量的关注。一天的精神状态取决于前一晚的睡眠质量,高睡眠质量自然保证了人们第二天精力充沛。但据统计,中国成年人失眠率高达38.2%,青少年失眠率也在上升。总的来说,如果入睡时间超过30 分钟,就属于失眠的范畴,所以我们相信很多参与者也存在失眠的情况。长期失眠会使人整天感到疲劳,精力不足、注意力不集中、工作学习效率低下。严重的失眠甚至会导致植物神经功能紊乱,导致体内各种系统的不平衡以及各种问题。
影响失眠的因素很多,一般可以分为客观因素和主观因素两大类。客观因素是环境变化、在睡觉前喝茶或咖啡等;而主观因素一般是生活压力、情绪失落、精神兴奋等精神因素。然而,青年人在成长和发展的过程中,由于学习和工作压力极易疲劳。因此,他们必须特别注意卧床休息,以确保身体健康。
  本文针对睡眠质量进行了一些研究。本文数据数据集源自2017年Asia and Pacific Mathematical Contest in Modeling,并经过处理而来,数据含2732个样本,显示精神质Psychoticism、神经质Nervousness、可靠性Reliability和性格Character等指标与睡眠质量Sleep quality的关系(“1”代表睡眠好,“2”代表睡眠差)。
部分数据截图:



图1

2.2 基本假设与指标的选取

2.2.1 基本假设

假设1:数据真实可靠。
假设2:男女群体中发生睡眠质量的概率相等。
假设3:年龄对于睡眠质量的差异性可以忽略。

2.2.2 指标的选取

  可知,数据的序号Number、来源Source本身和睡眠质量并无关系。
  基于以上假设,年龄、性别对于睡眠质量影响可以忽略。于是,选取的指标为:精神质、神经质、可靠性和性格。

三、数据的训练与测试

  基于精神质、神经质、可靠性和性格这4个指标,我们应用k-近邻对数据集进行训练和测试。其中,训练数据与测试数据的比例为6:4,选取k=5,程序运行结果如下图所示:



图2

  从图2中看出,训练数据与测试数据的比例为6:4,选取k=5,测试的正确率为93.413%,错误率仅为6.587% 。
  这表明,对于用精神质、神经质、可靠性和性格这4个指标来分类睡眠质量的好坏,使用k-近邻算法是可行的,正确率超过90%。

四、进一步探索

  我们在不同的训练数据与测试数据的比例和k的不同选值下,求得测试结果的正确率如图所示:



图3

  从图中可以看出,在相同的训练数据与测试数据的比例下,k的取值越大,正确率一般也越大。
  同时可以看出,在k取定值时,训练数据与测试数据的比例在7:3时的正确率比6:4和8:2的低,但是,在这三个比例下,正确率均超过90%,表明k-近邻算法在区别睡眠质量好坏时是具有一定的可行性。
  本文只考虑精神质、神经质、可靠性和性格这4个指标,没考虑性别和年龄的影响。基于本文可以以此作为发展点,继续探索性别、年龄、精神质、神经质、可靠性和性格这6个指标对于睡眠质量的关系,并使用k-近邻算法或其改进算法进行进一步探索。

五、小结

优点:精度高,对异常值不敏感,无数据输入假定。
缺点:计算复杂度高,空间复杂度高。
适用数据范围:数值型和标称型。

六、参考文献

[1]周志华.机器学习[M].北京:清华大学出版社,2016.
[2]Peter Harrington.机器学习实战[M].北京:人民邮电出版社,2013.
[3]韩家炜等.数据挖掘概念与技术[M].北京:机械工业出版社,2012.

七、附录

《机器学习实战》的代码,其代码的资源网址为:
  https://www.manning.com/books/machine-learning-in-action

  其中,kNN.py文件为:


from numpy import *
import operator
from os import listdir

def classify0(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()     
    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]


def file2matrix(filename):
    w=4
    fr = open(filename)
    numberOfLines = len(fr.readlines())         #get the number of lines in the file
    returnMat = zeros((numberOfLines,w))        #prepare matrix to return
    classLabelVector = []                       #prepare labels return   
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:w]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

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

def datingClassTest( hoRatio, filename, k ):
    #hoRatio =      #测试数据的比例
    datingDataMat,datingLabels = file2matrix( filename )       #load data setfrom file
    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],  k )
             #print("the classifier came back with: %d, the real answer is: %d"  % (classifierResult, datingLabels[i]) )       
        if (classifierResult != datingLabels[i]): 
            errorCount += 1.0
    #print(numTestVecs)#"测试数据:"+str(m)
    print("正确率 : %3.4f %%" % (100-100.0*errorCount/float(numTestVecs)) )
    print("错误率 : %3.4f %%" % (100.0*errorCount/float(numTestVecs)) )

猜你喜欢

转载自blog.csdn.net/d_i_k_y/article/details/80948091