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

一、介绍

KNN算法称为邻近算法,或者说K邻近算法(kNN,k-NearestNeighbor),分类算法。

KNN核心思想:一个样本在特征空间中的k个最相邻的样本中的大多数属于的某一个类别,则认为该样本也属于这一类别,并具有这个类别上的样本特性。

KNN算法优点:

  1. 简单,易于理解,易于实现,无需估计参数,无需训练;

  2. 适合对稀有事件进行分类;

  3. 特别适合于多分类问题(multi-modal,对象具有多个类别标签), kNN比SVM的表现要好。

KNN算法缺点:

  1. K邻近算法必须保存全部数据集,存储空间需求大。

  2. 必须对数据集中的每个数据计算距离值,计算量大,时间开销大。

  3. 无法给出任何数据的基础结构信息,无法知晓平均实例样本和典型实例样本 具有什么特征。

二、原理

2.1 欧式距离

欧式距离又称欧几里得度量(euclidean metric),是一个通常采用的距离定义,指在m维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离。

二维空间下,欧式距离计算公式,如图1-1所示:


图1-1 二维空间欧式距离

d表示坐标(x1,y1)到坐标(x2,y2)之间的直线距离。

三维空间下,欧式距离计算公式,如图1-2所示:


图1-2 三维空间欧式距离

n维空间下,欧式距离计算公式,如图1-3所示:


图1-3 n维空间欧式距离
2.2 KNN算法

KNN是通过测量不同特征值之间的距离进行分类。它的的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

例如:如下图2-1所示,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,因此绿色圆被赋予红色三角形那个类;如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。

图2-1 KNN算法示例图

我们要使k-近邻算法将每组数据划分某个类中,首先需要准备好已知类别数据集,我们需要对未知类别属性的数据集中的每个点都依次执行以下操作:

  1. 计算已知类别数据集中的点到当前未知点之间的距离;

  2. 按照距离递增次序排序;

  3. 选取与当前点距离最小的k个点;

  4. 确定前k个点所在类别出现的概率;

  5. 返回前k个点出现频率最高的类别作为当前未知点的预测分类。

三、代码实现

参考《机器学习实战》第二章K-近邻算法样例-约会对象分类

首先导入已分类好的数据作为判断依据,代码如下:

#提取数据
def file2matrix(filename):
    #导出文件中数据,初始化returnMat为Mx3,M为数据数量
    with open(filename,'r')as r:
        arrayOLines = r.readlines()
    numberOLines = len(arrayOLines)
    returnMat = np.zeros((numberOLines,3))
    classLabelVector = []
    index =0
    #分别获取数据和数据标签
    for line in arrayOLines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1 
    return returnMat,classLabelVector

已分类好数据集格式如图 3-1 所示:

图3-1 已分类好数据示例图

第一列是每年获得的飞行常客里程数的特征值,第二列是玩游戏所耗时间百分比,第三列是每周消费冰淇淋公升数。最后一列是分类好的标签,1~3表示喜欢程度,1表示不喜欢,2表示一点喜欢,3表示很喜欢。

首先实现KNN算法,代码如下所示:

#计算距离,排序,选取K个最近的点,以K个点中频率最高的为预测类别
def classify0(test_point,dataSet,dataSetlabels,k):
    dataSetSize = dataSet.shape[0]  #得到已知数据集点个数
    #计算已知类别数据集中点和当前点之间的距离
    diffMat = np.tile(test_point,(dataSetSize,1)) - dataSet
    sqDiffMat = diffMat **2
    sqDistances = sqDiffMat.sum(axis=1) 
    distances = sqDistances ** 0.5
    sortedDistIndicies = distances.argsort() #对距离按照递增顺序排序
    classCount ={}
    #确定k个点所在类别出现频率
    for i in range(k):
        voteIlabel = dataSetlabels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0)+1  #dict.get(key,def) 查找key值value 没有返回def
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #按照k个点中类别出现次数排序
    return sortedClassCount[0][0]

classify0函数传入四个参数,test_point是需要预测的点坐标,dataSet是已分类好的数据点,dataSetlabels分类好的数据标签,k指以几个点来作为预测类别的依据。返回KNN算法预测类别。

但是由于我们通过欧式距离公式来计算样本点之间的距离发现,数字差值最大的属性对计算结果影响很大。比如(20000, 0, 0.1)和(32000, 67, 0.1)这两组数据,可以发现第一列对结果影响很大,但我们认为这三种特征对结果是同等重要的。所以我们要对样本数据进行“数值归一化”处理。

数值归一化做法就是将任意取值范围的特征值转化为0到1区间内的值,公式:newValue = (oldVlue - min) / (max - min)

代码如下所示:

#归一化特征值
def autoNorm(dataSet):
    minVals = dataSet.min(0)#每列最小值
    maxVals = dataSet.max(0)#每列最大值
    ranges = maxVals-minVals #得到取值范围
    normDataSet = np.zeros(np.shape(dataSet))  #初始化为dataSet大小全为0的矩阵
    m = dataSet.shape[0]  #得到数据行数
    normDataSet = dataSet - np.tile(minVals,(m,1))  #tile将minvals复制成m行1列矩阵
    normDataSet = normDataSet/np.tile(ranges,(m,1))
    return normDataSet

归一化后我们得到的数据集如图3-2所示:


图3-2 数值归一化示例图

接下来我们讲测试分类效果,代码如下所示:

#约会网站测试代码
def datingClassTest():
    hoRatio = 0.10  #用来测试数据占比
    datingDataMat,datingLabels = file2matrix('C:/Users/hyt/Desktop/machinelearninginaction/Ch02/datingTestSet2.txt')
    normMat = 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('the classifier came back with: %d,the real answer is : %d '%(classifierResult,datingLabels[i]))
        if (classifierResult != datingLabels[i]):  #比较预测结果和正确值
            errorcount += 1.0
    print('the total error rate is : %f'%(errorcount/float(numTestVecs)))

hoRatio表示测试数据占源数据比重,这里选用数据集中的10%用来做测试数据。

normMat是特征值归一化后的数据

normMat[i,:]表示第i行的数据作为测试数据,normMat[numTestVecs:m,]表示用除去测试集后的所有数据作为用来预测分类的点。参考numTestVecs=int(m*hoRatio)。

运行结果如图3-3所示:

图3-3 约会分类预测结果

分类处理约会数据集错误率是5%,我们可以尝试改变datingClassTest内变量hoRatio和变量k的值,检测错误率是否随着变量值变化而变化。

四、总结

综上,了解了KNN算法原理以及使用实例:

  • 首先,我们知道kNN算法就是计算预测点到所有已知点的直线距离,计算距离方法是欧式距离法。
  • 接着,我们需要对数据预处理,为了避免较大数据差值的干扰,我们需要将数据进行归一化处理,newValue = (oldVlue - min) / (max - min)。
  • 最后,我们只需要选择合适的k值,便很容易得到计算结果。
如果您喜欢我的文章,请关注或点击喜欢,您的支持是我最大的动力 ^ ^~!
欢迎指出问题,一起讨论,共同进步
转载请注明作者及其出处

黑羊的皇冠 简书主页

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350