机器学习 - K近临法(KNN)

博客内容源于《统计机器学习》一书的阅读笔记。Python的源码实现源于《机器学习实战》部分内容。

1. K近临算法

【算法描述】给定一个训练数据集,对于新的输入实例,在训练数据集中找到与该实例最近临的K个实例,这个k个实例的多数属于某个类,就把该输入实例分为这个类。

【数学描述】
输入:训练数据集

T={(x1,y1),(x2,y2),...,(xN,yN)}

其中, xiXRn 为实例的特征向量, yiY={c1,c2,...,cK} 为实例类别, i=1,2,...,N ;实例特征向量 x ;
输出:实例x 所属的类 y

【算法求解过程】
1. 根据给定的距离度量,在训练集T 中找出与 x 最近邻的 k 个点,涵盖这个 k 个点的领域记作Nk(x);
2. 在 Nk(x) 中根据分类决策规则(比如多数表决)决定 x 的类别y:

y=argmaxcjxiNk(x)I(yi=cj)i=1,2,...,N;j=1,2,...,K

上式中,函数 I 为指示函数,当yi=cj I 为1,否则I 为0。

2. K近临模型

K近临模型由三个参数决定:距离度量k值分类决策规则

2.1 距离度量

设特征空间 X n 维实数向量空间Rn xi,xjX,xi=(x(1)i,x(2)i,...,x(n)i)xj=(x(1)j,x(2)j,...,x(n)j)xi,xj Lp 距离定义为:

Lp(xi,yj)=(l=1n|x(l)ix(l)j|p)1pp>=1

2.1.1 欧氏距离(Euclidean Distance)

欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式。上述公式中 p=2 时,称为欧式距离:

L2(xi,yj)=(l=1n|x(l)ix(l)j|2)12

2.1.2 曼哈顿距离(Manhattan Distance)

p=1 时,称为曼哈顿距离:

L1(xi,yj)=(l=1n|x(l)ix(l)j|)

2.1.3 夹角余弦(Cosine)

几何中夹角余弦可用来衡量两个向量方向的差异,机器学习常常中用这一概念来衡量样本向量之间的差异。

cosθ=x⃗ ix⃗ j|x⃗ i||x⃗ j|

2.2 K值选取

K值的选取会对k近临算法的结果产生很大的影响。

  • K值很小时,预测结果会对噪声特别敏感。假设临近的实例点恰好是噪声,预测的结果就会出错。
  • K值很大,与输入实例距离较远的实例点也会起作用,导致预测的结果出错。

2.3 分类决策

k近临中的分类决策规则往往使用多数表决,即由输入实例的k个邻近的训练实例中的多数类决定输入实例的类。
【多数表决】
如果分类的损失函数为0-1损失函数,分类函数为

f:Rn{c1,c2,...,cK}

那么误分的概率是
P(Yf(X))=1P(Y=f(X))

对于给定的实例 xX , 其最近邻的k个训练实例点构成集合 Nk(x) 。如果涵盖 Nk(x) 的区域的类别是 cj , 那么误分率是:
1kxiNk(x)I(yicj)=11kxiNk(x)I(yi=cj)

要使误分类率最小(经验风险最小),就要使 xiNk(x)I(yi=cj) 最大,所以多数表决规则等价于经验风险最小化。

3. K近临举例

数据来自于《机器学习实战》一书中,数据下载链接:
https://github.com/bzhou830/ML-python/blob/master/ch1-KNN/datingTestSet2.txt
1.从文件中导入元数据

def file2matrix(filename):
    fr = open(filename)                                             #打开文件
    arrayOfLines = fr.readlines()                                   #读取文件行数
    numberOfLines = len(arrayOfLines)                               #文件行数
    returnMat=zeros((numberOfLines,3))                              #矩阵
    classLabelVector = []                                           #标签
    index = 0                           
    for line in arrayOfLines:           
        line = line.strip() 
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

2.使用matplotlib绘制数据集合图像

def PrintFigure(datingDataMat,datingLabels):
    fig = plt.figure()
    ax=fig.add_subplot(111, projection='3d')
    #绘制三维图
    num = len(datingDataMat)
    for i in range(num):
        if datingLabels[i] == 1:
            ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], c='b', marker='x')
        elif datingLabels[i] == 2:
             ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], c='r', marker='o')
        elif datingLabels[i] == 3:
             ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], c='g',marker='*')
        elif datingLabels[i] == 0:
             ax.scatter(datingDataMat[i][0],datingDataMat[i][1],datingDataMat[i][2], marker='1')

    plt.show()


数据集合

可以直观的看到元数据有三类。

3.完整程序

'''
inx         输入向量
dataSet     训练数据集
labels      训练数据集标签
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]                                   #返回数量最多的标签


def file2matrix(filename):
    fr = open(filename)                                             #打开文件
    arrayOfLines = fr.readlines()                                   #读取文件行数
    numberOfLines = len(arrayOfLines)                               #文件行数
    returnMat=zeros((numberOfLines,3))                              #矩阵
    classLabelVector = []                                           #标签
    index = 0                           
    for line in arrayOfLines:           
        line = line.strip() 
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat,classLabelVector

'''
dataSet 传入矩阵数据集
normDataSet 返回归一化后数据集
ranges 
minVals 
'''
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.20                                                  #测试数据集所占比例
    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 "Rt is %d, Real is %d" % (classifierResult,datingLabels[i])
        if(classifierResult != datingLabels[i]):
            errorcount += 1.0

    print " %f " % (errorcount/float(numTestVecs))

程序文件可以可以到github上下载:https://github.com/bzhou830/ML-python/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空空的司马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值