机器学习算法之K近邻法-Python实现

一、算法简介

        k近邻法(k-nearest neighbor,k-NN)是一种基本的分类方法,输入的是实例的特征向量,对应于特征空间的点,输出结果为实例的类别,可以取多类。对于训练集来说,每个实例的类别已定,当分类时,对于新的实例,根据其k个最近邻的训练实例的类别,通过多数表决等方式来进行预测。k近邻法分类过程不具有显式的学习过程,其实际上是利用训练数据集对特征向量空间进行划分,从而作为后面分类的模型。对于k近邻法来说,最重要的是k值的选择、距离的度量以及分类决策规则得确定三个基本要素。

算法输入:

                                                                  T= \left \{ \left ( x_{1},y_{1} \right ) \left ( x_{2},y_{2} \right ),\cdots ,\left ( x_{N},y_{N} \right )\right \}

其中,x_{i}\epsilon \chi \subseteq R^{n}为实例的特征向量,y_{i}\epsilon y= \left \{ c_{1}, c_{2},\cdots , c_{K}\right \}为实例的类别,i= 1,2,\cdots ,N;实力特征向量x;

算法输出:实例x所属的类y.

算法步骤:

1.根据给定的距离度量,在训练集T中找出与x最近邻的k个点,涵盖这k个点的x的邻域记作N_{k}\left ( x \right );

2.在N_{k}\left ( x \right )中根据分类决策规则(如多数表决)决定x的类别y:

                                               y= arg \max_{c _{j}}\sum _{x_{i}\epsilon N_{k}\left ( x \right ) }I\left ( y_{i} = c _{j}\right ), i=1,2,\cdots ,N;j=1,2,\cdots ,K

其中,I为指示函数,即当y_{i} = c _{j}时I为1,否则I为0.

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

缺点:计算复杂度高、空间复杂度高。

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

(1)、距离度量:L_{p}\left ( x_{i}, x_{j} \right )= \left ( \sum _{l=1}^{n}\left | \Delta \right | ^{p} \right)^{\frac{1}{p}},其中\Delta =x_{i}^{\left ( l \right )} -x_{j}^{\left ( l \right )},当p=2时为欧氏距离,当p=1时为曼哈顿距离以及当p=\infty时,它是各个坐标距离的最大值,即L_{\infty }\left ( x_{i}, x_{j} \right )= \max_{l}\left | \Delta \right |。下图给出了p取不同值时与远点的距离为1的点的图形。

图 不同p取值的距离之间关系

(2)、k值的选择:k值的减小就意味着整体模型变得复杂,容易发生过拟合,通常采用交叉验证法来选取最优的k值。

(3)、分类决策规则:常用的为多数表决。

k近邻法代码如下:(采用欧氏距离和python 3.7)

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]

二、算法示例(摘录自《机器学习实战》,python 3.7)

       我的朋友海伦一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人选,但她没有从中找到喜欢的人。经过一番总结,她发现曾交往过三种类型的人:

□ 不喜欢的人

□ 魅力一般的人

□ 极具魅力的人

        尽管发现了上述规律,但海伦依然无法将约会网站推荐的匹配对象归人恰当的分类。她觉得可以在周一到周五约会那些魅力一般的人,而周末则更喜欢与那些极具魅力的人为伴。海伦希望我们的分类软件可以更好地帮助她将匹配对象划分到确切的分类中。此外海伦还收集了一些约会网站未曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。

2.1 准备数据

       海伦收集约会数据巳经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占据一行,总共有1000行。海伦的样本主要包含以下3种特征:

□ 每年获得的飞行常客里程数

□ 玩视频游戏所耗时间百分比

□ 每周消费的冰淇淋公升数

       在将上述特征数据输人到分类器之前,必须将待处理数据的格式改变为分类器可以接受的格式。创建名为file2matrix的函数,以此来处理输人格式问题。该函数的输人为文件名字符串,输出为训练样本矩阵和类标签向量。代码如下:

def file2matrix(filename):

    fr = open(filename)

    numberOfLines = len(fr.readlines())  # get the number of lines in the file

    returnMat = zeros((numberOfLines, 3))  # 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:3]

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

        index += 1

    return returnMat, classLabelVector

       从上面的代码可以看到,Python处理文本文件非常容易。首先我们需要知道文本文件包含多少行。打开文件,得到文件的行数。然后创建以零填充的矩阵NumPy(实际上,NumPy是一个二维数组,这里暂时不用考虑其用途)。为了简化处理,我们将该矩阵的另一维度设置为固定值3 , 你可以按照自己的实际需求增加相应的代码以适应变化的输人值。循环处理文件中的每行数据 , 首先使用函数line.strip()截取掉所有的回车字符,然后使用tab字符\t将上一步得到的整行数据分割成一个元素列表。接着,我们选取前3个元素,将它们存储到特征矩阵中。Python语言可以使用索引值-1表示列表中的最后一列元素,利用这种负索引,我们可以很方便地将列表的最后一列存储到向量classLabelVector中。需要注意的是,我们必须明确地通知解释器,告诉它列表中存储的元素值为整型,否则?”如0语言会将这些元素当作字符串处理。以前我们必须自己处理这些变量值类型问题,现在这些细节问题完全可以交给NumPy函数库来处理。

2.2 分析数据:利用Matplotlib创建散点图分析数据的具体情况

2.3 准备数据:归一化处理

        在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:

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

       在函数autoNorm中,我们将每列的最小值放在变量minVals中,将最大值放在变量maxVals中 ,其中dataSet.min(0)中的参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值。然后,函数计算可能的取值范围,并创建新的返回矩阵。

       为了测试分类器效果,创建函数datingClassTest,

def datingClassTest():

    hoRatio = 0.10  # hold out 10%

    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')  # 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], 3)

        print('the classifier came back with: {0}, the real answer is:{1}'.format(classifierResult, datingLabels[i]))

        if (classifierResult != datingLabels[i]):

            errorCount += 1.0

    print('the total error rate is:{}'.format(errorCount/float(numTestVecs)))

    print(errorCount)

       函数datingClassTest首先使用了file2matrix和autoNorm函数从文件中读取数据并将其转换为归一化特征值。接着计算测试向量的数量,此步决定了normMat向量中哪些数据用于测试,哪些数据用于分类器的训练样本;然后将这两部分数据输人到原始kNN分类器函数classify ()。最后,函数计算错误率并输出结果。

       数据和源码:链接:https://pan.baidu.com/s/1rqDZK-xb5y0cFV4knzIpyg 提取码:6br6 (其中还包括手写识别系统的代码和数据以及其他资源)

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

推荐阅读更多精彩内容

  • 收保护费最后一天,创了新高。最爱车后座伸出的一个个小脑袋,每次我用眼神调戏小精灵们,各种反应真的可爱到死。...
    芙蕖沉香阅读 347评论 0 1
  • 在《红楼梦》第四十五回《金兰契互剖金兰语 风雨夕闷制风雨词》中,黛玉因为看了“乐府杂稿”,有“秋闺怨”、“别离怨”...
    小闲云阅读 4,146评论 5 24
  • 一句话总结整个过程是:触摸或者点击一个控件,然后这个事件会从上向下(从父->子)找最合适的view处理,找到这个v...
    Lefe阅读 189评论 0 1
  • 有朋友点评笔者的博文《用中国话讲道理·第一章.求真之路(19)》http://blog.sina.com.cn/s...
    徐红钢阅读 790评论 0 2
  • 听了贾老师《让深度学习真实发生的课堂》,感触:在教学中,我们不单单是教学任务的完成,更为重要的是,教师也要把这些任...
    富宁156杨会仙阅读 593评论 0 5