机器学习之k-近邻算法简单实现

k-近邻算法属于一种分类算法。假设每一个数据都有其唯一对应的类别,k近邻算法实际就是在数据集里面确定与当前值最近的前k个点,然后确定前k个点所在的分类出现的频率,将前k个点出现频率最高的类别作为当前值的预测分类。下面以简单的程序来实现算法。

from numpy import*
import operator

#定义数据集
def createDataSet():
    group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=['A','A','B','B']
    return group,labels

#k-近邻算法
#计算输入点与数据集所有点的欧氏距离,确定排序后的前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]

group,labels=createDataSet()
print classify0([0,0],group,labels,3)

运行的结果是:B

以上就是k-近邻算法的简单实现。

However,我们并不满足于分类的数据集只有上述例子那么少!!!如果我们用文档输入更多数据的话,我们就要将文档里面的数据读取,并将它们转化成我们需要的格式!!!下面就将k-近邻算法延伸出去。

举个实际例子。小丽是个黄花大闺女,在相亲网站上面相亲。给出的对象选择标准是“每年飞行里程数”,“玩游戏时间的百分比”,“每周吃冰激凌公斤数”。自己的喜欢程度指标有“很喜欢”,“一般喜欢”,“你是个好人”。为了能够输入数据之后就能够自动分类,因此k-近邻算法派上大用场了。首先,小丽在相亲网站上面收集了1000个数据样本,并给出了自己的喜欢程度。

为了更好表示喜欢的程度,我将“很喜欢”,“一般喜欢”,“你是个好人”转化成“3”,“2”,“1”

文档传送门:https://download.csdn.net/download/kou_ching/10855212

接下来就是数据输入转换:

#数据输入
def file2matrix(filename):
    fr = open(filename)
    arrayOLines=fr.readlines()                  #得到所有行的字符串,并生成列表
    numberOfLines = len(arrayOLines)            #得到文件的行数
    returnMat = zeros((numberOfLines,3))        #得到 numberOfLines x 3 数组
    classLabelVector = []                       #p创建返回的数组  
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip()                     #截取这一行所有的回车字符
        listFromLine = line.split('\t')         #将一行数据分割成一个元素列表
        returnMat[index,:] = listFromLine[0:3]  #将列表的前两列放到矩阵returnMat中
        classLabelVector.append(int(listFromLine[-1]))  #将这一列表的最后一列放到classLabelVector中
        index += 1
    return returnMat,classLabelVector

但是计算两个样本的欧氏距离的时候,数据集中数值大的会影响计算结果,因此我们需要归一化数据集。

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

#归一化数据集
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))   
    return normDataSet, ranges, minVals

现在万事俱备,接下来就是测试我们算法的精确度。一般来说,训练算法是使用数据集的90%数量,而测试算法就是使用数据集的10%数量,由于小丽是无规律收集数据的,因此就可以看作是随机抽取样本来训练以及测试。

#测试算法
def datingClassTest():
    hoRatio = 0.50      #hold out 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):
        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   #错误数量

将上面代码整合一下即可得到:

# -*- coding: cp936 -*-

from numpy import*
import operator


def createDataSet():
    group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=['A','A','B','B']
    return group,labels

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]


#数据输入
def file2matrix(filename):
    fr = open(filename)
    arrayOLines=fr.readlines()                  #得到所有行的字符串,并生成列表
    numberOfLines = len(arrayOLines)            #得到文件的行数
    returnMat = zeros((numberOfLines,3))        #得到 numberOfLines x 3 矩阵
    classLabelVector = []                       #p创建返回的numpy矩阵  
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip()                     #截取这一行所有的回车字符
        listFromLine = line.split('\t')         #将一行数据分割成一个元素列表
        returnMat[index,:] = listFromLine[0:3]  #将列表的前两列放到矩阵returnMat中
        classLabelVector.append(int(listFromLine[-1]))  #将这一列表的最后一列放到classLabelVector中
        index += 1
    return returnMat,classLabelVector

#归一化数据集
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))   
    return normDataSet, ranges, minVals


#测试算法
def datingClassTest():
    hoRatio = 0.50      #hold out 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):
        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   #错误数量

运行一次测试算法函数,得到

误差率是6.4%,相当不错的结果,可见k-近邻算法可以顺利帮小丽解决筛选对象的问题!!!

接下来就是调用整个算法框架了!!!

下面简单地使用:

# -*- coding: cp936 -*-

from numpy import*
import operator


def createDataSet():
    group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=['A','A','B','B']
    return group,labels

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]


#数据输入
def file2matrix(filename):
    fr = open(filename)
    arrayOLines=fr.readlines()                  #得到所有行的字符串,并生成列表
    numberOfLines = len(arrayOLines)            #得到文件的行数
    returnMat = zeros((numberOfLines,3))        #得到 numberOfLines x 3 矩阵
    classLabelVector = []                       #p创建返回的numpy矩阵  
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip()                     #截取这一行所有的回车字符
        listFromLine = line.split('\t')         #将一行数据分割成一个元素列表
        returnMat[index,:] = listFromLine[0:3]  #将列表的前两列放到矩阵returnMat中
        classLabelVector.append(int(listFromLine[-1]))  #将这一列表的最后一列放到classLabelVector中
        index += 1
    return returnMat,classLabelVector

#归一化数据集
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))   
    return normDataSet, ranges, minVals


#测试算法
def datingClassTest():
    hoRatio = 0.50      #hold out 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):
        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   #错误数量

#应用k-近邻算法
def classifyPerson():
    resultList=['He is a good man','Xiao Li likes him','Xiao Li likes him so much']
    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 year"))
    datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr=array([ffMiles,percentTats,iceCream])
    classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
    print "Xiao Li probably like this person: ",resultList[classifierResult-1]

classifyPerson()    #应用k-近邻算法

结果:

 

猜你喜欢

转载自blog.csdn.net/kou_ching/article/details/85042758