机器学习——K-近邻算法

来源:互联网 发布:js怎么使用正则表达式 编辑:程序博客网 时间:2024/05/22 22:21

简而言之,k-近邻算法采用测量不同特征值之间的距离来进行分类。

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

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

适用数据范围:数值型和标称型(目标变量的结果只在有限目标集中取值)。

算法的工作原理:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的关系。输入没有标签的新数据后,将数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法的出处。通常k是不大于20的整数,最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

k-近邻算法的一般流程:

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

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

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

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

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

下面是一个使用k-近邻算法改进约会网站配对效果的实例。

数据中主要包含了3个属性,分别是:(1)每年获得的飞行常客里程数(2)玩视频游戏所耗时间百分比(3)每周消费冰淇淋公升数。以及最后约会的满意程度,1:不喜欢;2:比较喜欢;3:非常喜欢。

部分数据的截图如下:


首先,我们需要创建一个函数,该函数用于解析数据文件中的信息,并最终返回我们所需的训练样本集和对应的类标签向量。函数的具体定义如下:

def file2matrix(filename):    fr = open(filename)    numberOfLines = len(fr.readlines())       #获取文件的总行数      returnMat = zeros((numberOfLines,3))        #初始化一个0矩阵    classLabelVector = []                           fr = open(filename)    index = 0    for line in fr.readlines():        line = line.strip()#删除前、后的空白符        listFromLine = line.split('\t')#将3个属性和分类放入列表listFromLine        returnMat[index,:] = listFromLine[0:3]#依次装入了属性值        classLabelVector.append(int(listFromLine[-1]))#将最后的分类情况添加到classLabelVector中        index += 1    return returnMat,classLabelVector#返回提取到的属性矩阵和分类向量

将数据解析之后,我们可以进行一些可视化的展示,主要代码如下:

#解析文件,获得数据datingDataMat, datingLables = kNN.file2matrix('E:/PythonItem/DateSet/datingTestSet2.txt')fig = plt.figure()ax = fig.add_subplot(111)#设置图片标题title = u'属性可视化展示'title.decode('utf-8')ax.set_title(title)#设置坐标轴文字plt.xlabel(u'玩视频游戏所耗时间百分比')plt.ylabel(u'每周消耗冰淇淋公升数')p = ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0*array(datingLables), 15.0*array(datingLables))plt.show()

运行后的情况如下:

从上图中我们已经可以大致看出三类不同人群的分布区域。

在进行进一步的分析之前,我们需要对数据做归一化处理。因为在三个属性中“飞行里程”这个属性的数值较大,从而也将产生相对于其它两个属性更大的影响。但我们认为这三种属性特征是同等重要的,因此作为三个等权重的特征之一,飞行常客里程数不应该如此严重地影响到结果的判断。

在这里,我们采用如下的公式将数据转化为0到1区间内的值:

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

其中,min和max分别是数据集中最小和最大的特征值。下面就是实现上述归一化方法的具体代码:

def autoNorm(dataSet):#分别取得每一列的最小值和最大值    minVals = dataSet.min(0)    maxVals = dataSet.max(0)#计算最值间的差值    ranges = maxVals – minVals#构造行列与输入矩阵相同的0矩阵    normDataSet = zeros(shape(dataSet))#获取输入矩阵的行数    m = dataSet.shape[0]#矩阵中每一列的元素减去该列的最小值    normDataSet = dataSet - tile(minVals, (m,1))#在上一步的基础上再除以对应列的最值差    normDataSet = normDataSet/tile(ranges, (m,1))   #返回归一化后的矩阵、每列的最值差以及每列的最小值    return normDataSet, ranges, minVals

对数据进行归一化后就可以输入分类器进行具体分类操作了,实现的分类器代码如下:

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={}      #根据前k个最小距离的值来判断目标向量所属的分类    for i in range(k):        voteIlabel = labels[sortedDistIndicies[i]]#统计每个分类在距离最小的k个例子中出现的次数        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#根据字典的值来对字典项进行从大到小的排序    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)#最后返回字典值最大的字段的键,即对应的分类    return sortedClassCount[0][0]

到这里为止,k-近邻算法的主体部分就全部实现了。可以通过如下的代码来测试我们编写的分类器的错误率:

def datingClassTest():    hoRatio = 0.10      #测试数据为10%    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')           normMat, ranges, minVals = autoNorm(datingDataMat)    m = normMat.shape[0]    numTestVecs = int(m*hoRatio)#接收测试的向量数    errorCount = 0.0    for i in range(numTestVecs):#数据集中前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

接下来可以构建对应的应用,根据用户输入的信息进行分类。具体代码如下:

def classifyPerson():    resultList = ['not at all', 'in small doses', 'in large doses']    percentTats = float(raw_input(\        "percentage of time spent playing video games?"))    ffMiles = float(raw_input("frequent flier miles earned per year?"))    iceCream = float(raw_input("liters of ice cream consumed per week?"))    datingDataMat, datingLabels = file2matrix('datingDataMat')    normMat, ranges, minVals = autoNorm(datingDataMat)    inArr = array([ffMiles, percentTats, iceCream])#先将输入向量做归一化,然后再进行测试    classifierResult = classify0((inArr-minVals)/ranges, normMat, datingLabels, 3)    print "You will probably like this person:", \        resultList[classifierResult-1]