机器学习之K-近邻算法

声明:本篇博文是学习《机器学习实战》一书的方式路程,系原创,若转载请标明来源。

1. K-近邻算法的一般流程:

(1)收集数据:可以使用任何方法(如爬虫)。

(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式。

(3)分析数据:可以使用任何方法。

(4)测试算法:计算误差率。

(5)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行K-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

2. K-近邻算法的伪代码和Python代码

对未知类别属性的数据集中的每个点依次执行以下操作:

(1)计算已知类别数据集中的点与当前点之间的距离。

(2)按照距离递增次序排序。

(3)选取与当前点距离最小的k个点。

(4)确定前k个点所在类别的出现频率。

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

程序代码:

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.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

代码解释:

(1)classify0()函数的四个输入参数:inX 用于分类的输入向量,dataSet是输入的训练样本集,labels是标签向量,最后k是用于选择最近邻的数目,其中标签向量的元素数目和矩阵dataSet的行数相同。

(2)代码中测量两个对象距离的方法是欧几里得公式:

                                                                                         

(3)shape函数是numpy.core.fromnumeric中的函数,它的功能是读取矩阵的长度,比如shape[0]就是读取矩阵第一维度的长度。它的输入参数可以使一个整数表示维度,也可以是一个矩阵。

(4)tile函数是模板numpy.lib.shape_base中的函数。函数的形式是tile(A,reps)

        A的类型几乎所有类型都可以:array, list, tuple, dict, matrix以及基本数据类型int, string, float以及bool类型。

        reps的类型也很多,可以是tuple,list, dict, array, int,bool.但不可以是float, string, matrix类型。行列重复copy的次数。

(5)sum函数中加入参数。sum(a,axis=0)或者是.sum(axis=1) ,axis=0 就是普通的相加 ;加入axis=1以后就是将一个矩阵的每一行向量相加。

(6)argsort函数返回的是数组值从小到大的索引值。

 (7)   get() 函数返回字典指定键的值,如果值不在字典中返回默认值。

(8)sorted进行排序,sorted(iterable, cmp=None, key=None, reverse=False)

         iterable:是可迭代类型;
         cmp:用于比较的函数,比较什么由key决定;
         key:用列表元素的某个属性或函数进行作为关键字,有默认值,迭代集合中的一项;
         reverse:排序规则. reverse = True  降序 或者 reverse = False 升序,有默认值。
         返回值:是一个经过排序的可迭代类型,与iterable一样。

3 示例-使用K-近邻算法改进约会网站的配对效果

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

将待处理数据的格式改变为分类器可以接收的格式。

代码实现:

 1 def file2matrix(filename):
 2     fr = open(filename)
 3     numberOfLines = len(fr.readlines())         #get the number of lines in the file
 4     returnMat = zeros((numberOfLines,3))        #prepare matrix to return
 5     classLabelVector = []                       #prepare labels return   
 6     fr = open(filename)
 7     index = 0
 8     for line in fr.readlines():
 9         line = line.strip()
10         listFromLine = line.split('\t')
11         returnMat[index,:] = listFromLine[0:3]
12         classLabelVector.append(int(listFromLine[-1]))
13         index += 1
14     return returnMat,classLabelVector

代码解释:

(1)file2matrix ()函数的参数,filename接收文件名。

(2)open(路径+文件名,读写模式),读写模式:r只读,r+读写,w新建(会覆盖原有文件),a追加,b二进制文件。

(3)readlines() 自动将文件内容分析成一个行的列表,该列表可以由 Python 的 for ... in ... 结构进行处理。

(4)len()获得列表中的元素个数。

(5)zeros(shape, dtype=float, order='C'),返回来一个给定形状和类型的用0填充的数组;

         参数:shape:形状

                   dtype:数据类型,可选参数,默认numpy.float64

                   dtype类型:t ,位域,如t4代表4位

                                     b,布尔值,true or false

                                     i,整数,如i8(64位)

                                     u,无符号整数,u8(64位)

                                    f,浮点数,f8(64位)

                                   c,浮点负数,

                                   o,对象,

                                   s,a,字符串,s24

                                   u,unicode,u24

                order:可选参数,c代表与c语言类似,行优先;F代表列优先

(6)strip() 方法用于移除字符串头尾指定的字符,默认删除空白符(包括'\n', '\r',  '\t',  ' ')。

 (7)split()就是将一个字符串分裂成多个字符串组成的列表。

3.2 准备数据:归一化数值

不同的属性,其数值的取值范围不同,则计算距离时属性的权重就会无法掌控。因此,通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。其通用公式:

newValue = (oldValue-min) /(max-min)

其中min和max分别是数据集中的最小特征值和最大特征值。

代码实现:

1 def autoNorm(dataSet):
2     minVals = dataSet.min(0)
3     maxVals = dataSet.max(0)
4     ranges = maxVals - minVals
5     normDataSet = zeros(shape(dataSet))
6     m = dataSet.shape[0]
7     normDataSet = dataSet - tile(minVals, (m,1))
8     normDataSet = normDataSet/tile(ranges, (m,1))   #element wise divide
9     return normDataSet, ranges, minVals

3.3 测试算法:作为完整程序验证分类器

通常把已有的数据90%作为训练样本分类器,其余10%数据去测试分类器,检验分类器的正确率。

代码实现:

 1 def datingClassTest():
 2     hoRatio = 0.10      #hold out 10%
 3     datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')       #load data setfrom file
 4     normMat, ranges, minVals = autoNorm(datingDataMat)
 5     m = normMat.shape[0]
 6     numTestVecs = int(m*hoRatio)
 7     errorCount = 0.0
 8     for i in range(numTestVecs):
 9         classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
10         print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i])
11         if (classifierResult != datingLabels[i]): errorCount += 1.0
12     print "the total error rate is: %f" % (errorCount/float(numTestVecs))
13     print errorCount

测试结果:

误差在5%,基本可以实现对结果的预测。

4.使用算法:构建完整可用系统

代码实现:

 1 def classifierPerson():
 2    resultList = [u'不喜欢',u'比较喜欢',u'很喜欢']
 3    percentTats = float(raw_input(u"玩视频游戏所耗时间百分百:"))
 4    ffmiles = float(raw_input(u"每年获得的飞行常客里程数:"))
 5    iceCream = float(raw_input(u"每周消耗的冰淇淋公升数:"))
 6    datingDataMat, datingLables = kNN.file2matrix('datingTestSet2.txt')
 7    normMat,ranges,minVals = kNN.autoNorm(datingDataMat )
 8    inArr = array([ffmiles ,percentTats ,iceCream ])
 9    classifierResult = kNN.classify0((inArr-minVals )/ranges,normMat ,datingLables ,3)
10    print u"你对这个人喜欢度: %s" % resultList [classifierResult -1]

运行结果:

5 附-对数据分析的数据可视化

 代码实现:

1 def show_data():
2    dataSet,lables = kNN .file2matrix('datingTestSet2.txt')
3    jieguo = kNN .classify0([2674,8.54,8.0],dataSet ,lables ,10)
4    fig= plt.figure()
5    ax = fig.add_subplot(111)
6    ax.scatter(dataSet [:, 1], dataSet [:, 2],
7               15.0*array(lables ),15.0*array(lables))
8    fig.show()
9    raw_input()

运行结果:

 

posted @ 2017-10-25 22:19  追寻的鹿  阅读(570)  评论(0)    收藏  举报