机器学习实战之KNN算法

640?wx_fmt=jpeg

本系列教程为《机器学习实战》的读书笔记。首先,讲讲写本系列教程的原因:第一,《机器学习实战》的代码由Python2编写,有些代码在Python3上运行已会报错,本教程基于Python3进行代码的修订;第二:之前看了一些机器学习的书籍,没有进行记录,很快就忘记掉了,通过编写教程也是一种复习的过程;第三,机器学习相对于爬虫和数据分析而言,学习难度更大,希望通过本系列文字教程,让读者在学习机器学习的路上少走弯路。

本系列教程特点:

  • 基于《机器学习实战》

  • 尽量避免讲太多数学公式,通过简单直白的方式讲解各算法的原理

  • 对于算法实现的代码进行详细讲解

哪些读者可以食用:

  • 了解机器学习的基本术语

  • 会Python语言

  • 会numpy和pandas库的使用

k-近邻算法(KNN)原理

KNN算法为分类算法。一句老话来描述KNN算法:“近朱者赤,近墨者黑”。 算法原理:计算测试样本与每个训练样本的距离(距离计算方法见下文),取前k个距离最小的训练样本,最后选择这k个样本中出现最多的分类,作为测试样本的分类。 如图所示,绿色的为测试样本,当k取3时,该样本就属于红色类;当k取5时,就属于蓝色类了。所以k值的选择很大程度影响着该算法的结果,通常k的取值不大于20。

640?wx_fmt=png

KNN算法原理

介绍完原理后,看看KNN算法的伪代码流程:

 
 
  1. 计算测试样本与所有训练样本的距离

  2. 对距离进行升序排序,取前k个

  3. 计算k个样本中最多的分类

KNN之约会对象分类

问题描述与数据情况

海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人:

  • 不喜欢的人

  • 魅力一般的人

  • 极具魅力的人

这里海伦收集了1000行数据,有三个特征:每年获得的飞行常客里程数;玩视频游戏所耗时间百分比;每周消费的冰淇淋公升数。以及对象的类型标签,如图所示。

640?wx_fmt=jpeg

数据情况

解析数据

 
 
  1. import numpy as np

  2. import operator

  3. def file2matrix(filename):

  4.    fr = open(filename)

  5.    arrayOLines = fr.readlines()

  6.    numberOflines = len(arrayOLines)

  7.    returnMat = np.zeros((numberOflines, 3))

  8.    classLabelVector = []

  9.    index = 0

  10.    for line in arrayOLines:

  11.        line = line.strip()

  12.        listFromLine = line.split('\t')

  13.        returnMat[index, :] = listFromLine[0:3]

  14.        classLabelVector.append(int(listFromLine[-1]))

  15.        index = index + 1

  16.    return returnMat, classLabelVector

定义解析数据的函数:4-9行:读取文件,并获取文件行数,创建一个文件行数(1000行)和3列的Numpy全0数组,创建用于存放类标签的classLabelVector列表。 10-17行:对文件进行循环遍历,对前三列数据存放到returnMat数组中,最后一列存放到classLabelVector列表中。结果如图所示。

640?wx_fmt=jpeg

解析数据

上面的代码为书中所写,其实用pandas读取数据后再出来是很方便了,代码如下:

 
 
  1. import numpy as np

  2. import operator

  3. import pandas as pd

  4. def file2matrix(filename):

  5.    data = pd.read_table(open(filename), sep='\t', header=None)

  6.    returnMat = data[[0,1,2]].values

  7.    classLabelVector = data[3].values

  8.    return returnMat, classLabelVector

归一化

由于特征间的数值差别太大,在计算距离时,数值大的属性会对结果产生更大的影响,这里需要对数据进行归一化:new = (old-min)/(max-min)。代码如下:

 
 
  1. def autoNorm(dataSet):

  2.    minval = dataSet.min(0)

  3.    maxval = dataSet.max(0)

  4.    ranges = maxval - minval

  5.    normDataSet = np.zeros(np.shape(dataSet))

  6.    m = dataSet.shape[0]

  7.    normDataSet = dataSet - np.tile(minval, (m,1))

  8.    normDataSet = normDataSet/np.tile(ranges, (m,1))

  9.    return normDataSet, ranges, minval

传入的参数为测试数据(就是returnMat);首先按0轴(也就是按列)进行min和max的计算,如图所示进行简单的示例;然后构造和数据(normDataSet)一样大小的0矩阵; tile函数的用法读者可以自行百度,这里看下使用后的案例,作用就是让一维数组重复m行,如图所示,这样就可以进行数据归一化的计算。

640?wx_fmt=jpeg

640?wx_fmt=jpeg

示例

640?wx_fmt=jpeg

示例

640?wx_fmt=jpeg

结果

KNN算法

这里使用的距离为欧式距离,公式为:

640?wx_fmt=jpeg

欧式距离

 
 
  1. def classify(inX, dataSet, labels, k):

  2.    dataSize = dataSet.shape[0]

  3.    diffMat = np.tile(inX, (dataSize,1)) -dataSet

  4.    sqdiffMat = diffMat ** 2

  5.    sqDistance = sqdiffMat.sum(axis = 1)

  6.    distances = sqDistance ** 0.5

  7.    sortedDist = distances.argsort()

  8.    classCount ={}

  9.    for i in range(k):

  10.        voteIlable = labels[sortedDist[i]]

  11.        classCount[voteIlable] = classCount.get(voteIlable, 0) + 1

  12.    sortedClassCount = sorted(classCount.items(),

  13.                             key=operator.itemgetter(1), reverse=True)

  14.    return sortedClassCount[0][0]

inX为训练数据;dataSet为测试数据,labels为类别标签;k为取值; 2-6行:计算欧式距离; 7-最后:对计算的距离进行索引排序(argsort),然后对字典进行排序,获取值最多的分类。

对分类器进行测试

这里选择前10%数据做为测试样本,进行分类器的测试。

 
 
  1. def test():

  2.    r = 0.1

  3.    X, y = file2matrix('数据/datingTestSet2.txt')

  4.    new_X, ranges, minval = autoNorm(X)

  5.    m = new_X.shape[0]

  6.    numTestVecs = int(m*r)

  7.    error = 0.0

  8.    for i in range(numTestVecs):

  9.        result = classify(new_X[i, :],new_X[numTestVecs:m, :], y[numTestVecs:m], 3)

  10.        print('分类结果: %d, 真实数据: %d' %(result, y[i]))

  11.        if (result != y[i]):

  12.            error = error + 1.0

  13.    print('错误率: %f' % (error/float(numTestVecs)))

640?wx_fmt=jpeg

结果

测试系统

最后,编写一个简单的测试系统,该代码通过人为的输入三个属性特征,可以自动得到该约会对象的分类标签。

 
 
  1. def system():

  2.    style = ['不喜欢', '一般', '喜欢']

  3.    ffmile = float(input('飞行里程'))

  4.    game = float(input('游戏'))

  5.    ice = float(input('冰淇淋'))

  6.    X, y = file2matrix('数据/datingTestSet2.txt')

  7.    new_X, ranges, minval = autoNorm(X)

  8.    inArr = np.array([ffmile, game, ice])

  9.    result = classify((inArr - minval)/ranges, new_X, y, 3)

  10.    print('这个人', style[result - 1])

640?wx_fmt=jpeg

结果

算法优缺点

  • 优点:精度高,对异常值不敏感

  • 缺点:计算复杂(想想每个测试样本都要与训练样本继续距离计算)

作者罗罗攀,Python中文社区专栏作者,《从零开始学网络爬虫》图书作者。专栏地址: http://www.jianshu.com/u/9104ebf5e177



640?wx_fmt=gif


Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以公安部、工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

640?wx_fmt=jpeg

▼ 点击下方阅读原文免费成为社区会员

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值