为了账号安全,请及时绑定邮箱和手机立即绑定

呐你们要的算法(一)

标签:
Java Python 算法

今天跟大家分享一下,机器学习中最简单的算法之一,KNN近邻算法,这是一个有监督的分类学习算法。

啥玩意叫有监督呢?就是作为训练的每个样本都是有标记的,比如样本1是A类,样本2是B类,这样的有标记的样本。而什么叫分类呢?输出结果是离散的,可穷尽的,就叫分类。

KNN近邻算法的核心思想是:

近朱者赤,近墨者黑,选K而取其最大可能。

从哪开始聊呢?先从算法的核心过程开始吧。

准备基本数据集 -> 计算待测试样本整个数据集所有数据的距离 -> 根据距离进行排序 ->获取前K个样本,将他们的标签进行统计 -> 获取频次最大的标签作为结果输出

举个栗子:我想知道我现在最可能处于哪个小区,我们假设小区都一样大。

我得到了一批小区的GPS定位经纬度 -> 我得到了我自己的GPS经纬度 -> 计算出我跟所有这些小区的距离,然后排序一下  -> 拿到前K个距离的样本,对他们的小区名称进行统计 -> bingo,频次最大的那个小区就可能是我的小区了。

听起来蛮简单的,但是在实际操作过程中会遇到几个问题。如果基础样本数太多,该如何计算出所有的距离?如果不是空间距离,比如是两个文本,如何衡量两个实体之间的距离?取K个样本,那么K取多少对于结果比较好?

这些是我们作为使用者需要去考虑的事情,模型本身不考虑这个东西。就开放性地把问题抛出来吧,大家都思考思考。

用一个识别手写数字的例子来分享一下这个算法吧,代码自己写的,但是例子是书上的,轻喷。首先我们的数据长下面这样子,32*32的1024个像素的图片,都是手写的。

这是数字1

https://img2.sycdn.imooc.com/5e649c13000157ab02300484.jpg

这是数字0

https://img2.sycdn.imooc.com/5e649bcf0001d2f102330476.jpg

先定义一个函数,参数inX是要用来计算的向量,dataSet是测试数据集,labels是与dataSet一一对应的标签值,k是我们想获取的前k个样本的数值。
def classify0(inX, dataSet, labels, k):
  #获得数据集的行数
    dataSetSize = dataSet.shape[0]
    
    #渲染出一个矩阵,行数跟dataSet行数一样,每一行都是要计算的图片
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    
    #然后计算出(Xn - Xi)^2 每个维度的坐标的相见的值
    #这里使用的是欧拉距离来衡量两个图片之间的距离
    sqDiffMat = diffMat**2

    #每一个值相加,再进行开方,得到N维空间的距离
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5

    #根据距离进行降序排序,获取前K个标签进行频次统计,然后取最大的那个
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

嘛,核心的分类算法就这样实现完了,不难,好好看看应该能看懂,最好自己写一遍。算法的主要思想就是把我们要计算的问题转变成能衡量距离的向量,然后计算向量之间的距离,用近朱者赤的观点来进行标签选择。

下面是一点工具类。这个函数是将一个文件转变成向量的工具,因为我们这里是文本所以直接用遍历的方式,这里比较原始,可以使用numpy等工具来进行矩阵的渲染。

def img2Vector(fileName):
    returnVect = zeros((1,1024))
    fr = open(fileName)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect
   
如果是真正的图片,我们可以用img库来进行转换,转换完的矩阵会有三个通道,RGB,而且每个通道的每个像素都0-255。有几百万像素就有几百万的像素点。

我们有一堆文本来训练和测试这个模型。下面是测试方法,我就不细说了。
[object Object]

[object Object]
def handWriterClassTest():
   hwLabels = []
   traningFileList = listdir('trainingDigits')
   m = len(traningFileList)
   traningMat = zeros((m,1024))
   for i in range(m):
      fileNameStr = traningFileList[i]
      fileStr = fileNameStr.split('.')[0]
      classNumStr = int(fileStr.split('_')[0])
      hwLabels.append(classNumStr)
      traningMat[i,:] = img2Vector('trainingDigits/%s' % fileNameStr)
   testFileList = listdir('testDigits')
   errorCount = 0.0
   mTest = len(testFileList)
   for i in range(mTest):
      fileNameStr = testFileList[i]
      fileStr = fileNameStr.split('.')[0]
      classNumStr = int(fileStr.split('_')[0])
      vectorUnderTest= img2Vector('testDigits/%s' % fileNameStr)
      classiFierResult = classify0(vectorUnderTest, traningMat, hwLabels, 3)
      print "the classifier came back with:%d , the real answer is: %d" % (classiFierResult,classNumStr)
      if(classiFierResult != classNumStr):
         errorCount += 1.0
   print "\n the total numer of Error is: % d ,the error rate is: %f" % (errorCount,(errorCount/float(mTest)))
         
         测试的结果准确度大概在98.8%这样子,在这个数据集上效果还是不错的,作为一个入门级算法。好了有什么好玩的大家再一次分享吧。什么你问欧拉距离是什么??是他是他就是他。

https://img4.sycdn.imooc.com/5e649bd00001e0f602220087.jpg





点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消