【机器学习实战】笔记1-K近邻算法

最近想学python,因此开始看《机器学习实战》,笔记主要侧重代码的理解方面。


1 原理

首先讲述一下K近邻算法的原理


准备阶段:给定训练数据

训练阶段:K近邻不需要训练过程

预测阶段:对于任何一个新的数据x,在训练数据中找到离x最近的k个样本点。在regression(回归)任务中,返回这k个样本点的平均值作为预测结果。在classification(分类)任务中,返回这k个样本点的中出现次数最多的那个类别。


2 样例--使用k近邻算法改进约会网站的配对效果

    某个大型交友网站会推荐不同的人选,大龄女青年淋淋把这些人分成三大类:老娘并不care的人,老娘觉得还行的人,老娘爱到不行的人。那么淋淋是怎么将这些人分类的呢,她有她自己的特别的技巧:主要从这三个特征进行分析

□每年坐飞机的里程数

□玩视频游戏所占的时间比例

□每周吃掉的冰激凌公升数

    至于为什么选择这些特征,在这里我不探究。总之淋淋把这些数据都记到了她的日记本里(文本文件datingTestSet.txt中)。淋淋收集了1000个男人的数据,每个样本数据占据一行,总共有1000行。(也是吸了好多人厚,淋淋只是想收集更多芭比娃娃)。


2.1 准备数据:从文本文件中解析数据

    第一步工作就是要将这些记在文本中的数据提取出来,转换为算法可以处理的格式。下面这个函数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') #使用字符'\t'把行分割成一个元素列表
        returnMat[index,:] = listFromLine[0:3] #前三个数是特征,存入returnMat中
        classLabelVector.append(int(listFromLine[-1])) #最后一个数是label,存入classLabelVector中
        index += 1
    return returnMat,classLabelVector



其中关键性的几步单步调试结果如下,有助于理解。某一行最初的line数据如下

 经过代码line = line.strip()后,去掉了这一行数据里的回车符

经过代码listFromLine = line.split('\t')后,利用制表符把这一行数据变成了如下元素列表

    这段代码有个需要注意的点。1、python可以用索引值-1表示列表中的最后一个元素。2、在存储label的时候告诉interpreter列表中存储的元素值为整型。否则python语言会将这些元素当做字符串处理。从上面的代码可以看到,Python处理文本文件非常容易。


2.2 准备数据:归一化数据

    数据归一化是机器学习中常用的预处理手段。它的主要功能是避免数据因为量纲不同而结果带来影响。例如本例中,每年坐飞机的里程数这个数据的值是很大,所以在计算的时候,该特征对计算结果的影响会远远大于其他两个特征。所以我们需要把所有的输入特征都归一化到[0,1]这个区间内。归一化的公式如下:


    归一化的代码段如下:输入参数为原始数据集,输出为归一化后的数据集

def autoNorm(dataSet):
    minVals = dataSet.min(0) #min(0)表示从选取每一列的最小值。
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))# 创建一个根dataSet一样大小的矩阵
    m = dataSet.shape[0] # shape[0]表示求dataSet的行数
    normDataSet = dataSet - tile(minVals, (m,1))
    normDataSet = normDataSet/tile(ranges, (m,1))   #element wise divide
    return normDataSet, ranges, minVals

    这个函数比较简单,主要有两点注意的地方。1、tile函数的功能:重复某个数组,比如tile(A,n)就是将数组A重复n次,构成一个新的数组。同样用单步调试增加对该函数的理解


    可以看出minVals这个矩阵,经过tile之后,在行数上重复了3次。在本代码中,特征矩阵有1000X3个值,而minVals和range的值都是1X3。实用tile函数将变量内容复制成与特征矩阵同样大小的矩阵。


2.3 测试算法:作为完成程序验证分类器

    机器学习一个很重要的工作就是评估算法的正确率,通常我们用已有数据的90%作为训练样本来训练分类器,使用剩下的10%的数据去测试分类器,检测分类器的正确率。分类器针对约会网站的测试代码如下:

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: %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))
    print errorCount


    代码比较简单。它首先使用了file2matrix和autoNorm函数从文件中读取数据并将数据进行归一化。接着计算用于测试的数据的数量,这一步决定了norMat向量中那些数据用于测试,哪些数据用于当分类器的测试样本。最后调用函数classify0,也就是K近邻的核心算法部分。该函数的代码如下:

#函数输入:inX待分类的数据 dataSet给定的数据集 labels数据集中数据的类别标签 k选择最靠近的k个样本中的k的值
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0] #得到dataSet的行数
    # 距离计算 欧式距离
    diffMat = tile(inX, (dataSetSize,1)) - dataSet #tile用于重复某个数组,例如这里就是将inX重复dataseSize行,1列
    sqDiffMat = diffMat**2 
    sqDistances = sqDiffMat.sum(axis=1)# 将sqDiffMat里面的数据按行相加,每行输出一个sum
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()  #从小到大排序,返回的是下标   
    classCount={} #classCount是字典 
    # 选择距离最小的k个点       
    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]

classify0函数一共有4个输入参数。算法主要包括
1、距离计算,欧式距离。
2、选择距离最小的k个点,确定前k个距离最小元素所在的主要分类。
3、最后将classCount字典分解为元组列表(利用iteritems,但因为字典是无序的,所以得到的元组列表里面所有的项也是无序的),(由于我们需要返回发生频率最高的元素的标签,因此需要)使用程序第二行导入运算符模块的itemgetter方法,按照第二个元素的次序对元组进行从大到小的排序,最后返回发生频率最高的元素标签。
    在Python命令提示符中重新kNN模块,并输入kNN.datingClassTest(),执行分类测试程序,我们的得到下面的输入结果。错误率为6.4%。


以下是关于字典iteritems和sorted的一些题外话

    字典items()方法和iteritems()方法,是python字典的内建函数,分别会返回列表和迭代器,下面一起来看下字典items()和iteritems()的具体操作方法。

python字典的items方法作用:是可以将字典中的所有项,以列表方式返回。因为字典是无序的,所以用items方法返回字典的所有项,也是没有顺序的。
python字典的iteritems方法作用:与items方法相比作用大致相同,只是它的返回值不是列表,而是一个迭代器。


字典items()操作方法:

    从结果中可以看到,items()方法是将字典中的每个项分别做为元组,添加到一个列表中,形成了一个新的列表容器。如果有需要也可以将返回的结果赋值给新变量,这个新的变量就会是一个列表数据类型。

字典 iteritems()操作方法:

字典.iteritems()方法在需要迭代结果的时候使用最适合,而且它的工作效率非常的高


参考文章
Python 字典items返回列表,iteritems返回迭代器  http://www.iplaypython.com/jinjie/items-iteritems.html

 Python中的sorted函数以及operator.itemgetter函数 http://blog.csdn.net/dongtingzhizi/article/details/12068205


3 总结

    k近邻算法是分类数据最简单最有效的方法,是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据。k近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间,此外,由于必须对数据集中的每一个数据计算距离值,实际使用时可能非常耗时。

    k近邻算法的另一个缺陷是无法给出任何数据的基础结构信息,因此我们也无法知道平均实例样本和典型实例样本具有什么特征。但是下一个算法:决策树 可以解决这个问题。





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值