从0到1,用python实现K-NN算法

1.我所理解的KNN(k-Nearest Neighbor)

k-Nearest Neighbor,即k个相对距离最小的临近点。

举个栗子,对于下图的这个绿色的圆来说,它到底是属于红色的三角一方,还是属于蓝色的矩形一方。面对如此虎视眈眈的两边,小圆需要立刻表明自己的立场。这次,它使用了KNN算法,它在心里盘算:

1. 就看距离我最近的这三个家伙的来历吧(取k=3),两个是红方(2/3),一个是蓝方(1/3)。嗯,我应该当红方的人。

2. 如果我看距离我最近的五个人呢(k=5),两个是红方(2/5),三个是蓝方(3/5)。嗯,我应该是蓝人。

好了,说人话

如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,其中K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。

由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。 

KNN算法不仅可以用于分类,还可以用于回归。通过找出一个样本的k个最近邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。更有用的方法是将不同距离的邻居对该样本产生的影响给予不同的权值(weight),如权值与距离成反比。

  在KNN中,通过计算对象间距离来作为各个对象之间的非相似性指标,避免了对象之间的匹配问题,在这里距离一般使用欧氏距离或曼哈顿距离:

                      

 我们从上面那个例子也可以看出,KNN算法的结果很大程度取决于K的选择。那么我们怎么得到一个合理的k值呢?

如果k太小,分类结果易受噪声点影响。而如果k太大,近邻中又可能包含太多的其它类别的点。因此如果条件允许,我们可以对距离进行加权,来降低k值设定的影响。通常情况下,k值是采用交叉检验来确定(以k=1为基准,k一般低于训练样本数的平方根,通常是不大于20的整数)。

 2.python实现

首先,来总结一下KNN的精华:

1)计算测试数据与各个训练数据之间的距离(欧式或曼哈顿);

2)按照距离的递增关系进行排序;

3)选取距离最小的前K个点;

4)确定前K个点所在类别的出现频率;

5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。

先做一个mini的雏形

# 给出我们的mini训练数据

from numpy import *
import operator
def createDataSet():
    group = array([[0.1, 0.1], [0, 0], [1, 0.9]])
    lables = ['B', 'B', 'A']
    return group, lables

# 计算欧氏距离,返回在k个点中出现次数最多的label(得出预测结果)

def classify0(inX, dataSet, labels, k):
    # k means how many you need to get your P(x)
    dataSetSize = dataSet.shape[0]
    # tile(x,(a,b)) 把数组x在行上重复a次,列上重复b次
    DiffMat = tile(inX, (dataSetSize, 1)) - dataSet  
    sqDiffMat = DiffMat**2
    sqDistances = sqDiffMat.sum(axis=1) # 求出每行的sum
    distances = sqDistances**0.5  # 开根 到这里求解了欧式距离(并构成了一个ndarray)

    # argsort() 将x中的元素从小到大排列,输出对应的index
    sortedDistances = distances.argsort() 

    # 选出距离最小的k个点,并统计
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistances[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    
    #  operator.itemgetter(a)  项拿到器 拿到第a项 相当于 key = lambda x: x[1]
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse=True) #按照第一个(从0开始数)进行排序
    return sortedClassCount[0][0]   # 返回的出现次数最多的那个标签

# 开跑

if __name__ == "__main__":
    group, labels = createDataSet()
    x = [1, 1]
    print(classify0(x, group, labels, 2))

# 输出的结果

A

使用KNN改进约会网站的效果

使用的数据样子如下,命名为KNN_Test.txt

 附完整代码

from numpy import *
import operator

# 通过计算欧氏距离,返回在k个点中出现次数最多的label(得出预测结果)
def classify0(inX, dataSet, labels, k):
    # k means how many you need to get your P(x)
    dataSetSize = dataSet.shape[0]
    # tile(x,(a,b)) 把数组x在行上重复a次,列上重复b次
    DiffMat = tile(inX, (dataSetSize, 1)) - dataSet 
    sqDiffMat = DiffMat**2
    sqDistances = sqDiffMat.sum(axis=1) # 求出每行的sum
    distances = sqDistances**0.5  # 开根 到这里求解了欧式距离(并构成了一个ndarray)

    # argsort() 将x中的元素从小到大排列,输出对应的index
    sortedDistances = distances.argsort() 

    # 选出距离最小的k个点,并统计
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistances[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    
    #  operator.itemgetter(a)  项拿到器 拿到第a项 相当于 key = lambda x: x[1]
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse=True) #按照第一个(从0开始数)进行排序
    return sortedClassCount[0][0]   # 返回的出现次数最多的那个标签

# 将file转换为合适的数据结构
def file2Matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines) # 1000行
    returnMat = zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        line = line.strip()  # 去除首末的空格
        # 把每一行切片  ['40920', '8.326976', '0.953952', 'largeDoses']
        listFromLine = line.split() 
        # 将整个行直接粘贴过来,这里自动完成了类型转换的问题, 前三列
        returnMat[index, :] = listFromLine[0:3]  
        index += 1
        # 最后一列的标签放到此列表中
        classLabelVector.append(listFromLine[-1])  
    return returnMat, classLabelVector

if __name__ == "__main__":
    group, labels = file2Matrix('data/KNN_Test.txt')
    x = [3000, 5, 0.9]
    print(classify0(x, group, labels, 5))

预测结果如下 :

smallDoses

猜你喜欢

转载自blog.csdn.net/weixin_42280517/article/details/81289600