KNN算法

120 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

KNN算法

主要内容

  1. 什么是KNN,KNN用来解决哪类问题
  2. KNN算法的不足
  3. KNN实现的步骤
  4. KNN实战应用

KNN介绍

KNN(K-Nearest Neighbor)算法,意思是K个最近的邻居,从这个名字我们就能看出一些KNN算法的蛛丝马迹了。K个最近邻居,毫无疑问,K的取值肯定是至关重要的。那么最近的邻居又是怎么回事呢?
其实啊,KNN的原理就是当预测一个新的值x的时候,根据它距离最近的K个点是什么类别来判断x属于哪个类别preload  图中绿色的点就是我们要预测的那个点,假设K=3。那么KNN算法就会找到与它距离最近的三个点(这里用圆圈把它圈起来了),看看哪种类别多一些,比如这个例子中是蓝色三角形多一些,新来的绿色点就归类到蓝三角了。 img  但是,当K=5的时候,判定就变成不一样了。这次变成红圆多一些,所以新来的绿点被归类成红圆。从这个例子中,我们就能看得出K的取值是很重要的。

KNN算法的不足

 该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。
 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
 该方法的另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。

KNN实现步骤

  1. 计算距离(欧几里得距离或者马氏距离)

    欧几里得距离(二维):

    p=(x2x1)2+(y2y1)2p=\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}

    马氏距离:

    d=(xy)T1(xy)d=\sqrt{(x-y)^{T\sum^{-1}(x-y)}}

  2. 升序排列

  3. 取前K个

    K的取值

    • K太大:导致分类模糊
    • K太小:受个例影响,波动较大
  4. 加权平均

    • 经验

KNN实战应用

KNN算法求病人癌症检测的正确率

import csv
import random

# 读取数据
with open(".\Prostate_Cancer.csv","r") as f:
    render = csv.DictReader(f)
    datas = [row for row in render]

# 分组,打乱数据
random.shuffle(datas)
n = len(datas)//3

test_data = datas[0:n]
train_data = datas[n:]
# print (train_data[0])
# print (train_data[0]["id"])


# 计算对应的距离
def distance(x, y):
    res = 0
    for k in ("radius","texture","perimeter","area","smoothness","compactness","symmetry","fractal_dimension"):
        res += (float(x[k]) - float(y[k]))**2
    return res ** 0.5
    
# K=6
def knn(data,K):
    # 1. 计算距离
    res = [
        {"result":train["diagnosis_result"],"distance":distance(data,train)}
        for train in train_data
    ]
    # 2. 排序
    sorted(res,key=lambda x:x["distance"])
    # print(res)
    # 3. 取前K个
    res2 = res[0:K]
    # 4. 加权平均
    result = {"B":0,"M":0}
    # 4.1 总距离
    sum = 0
    for r in res2:
        sum += r["distance"]
    # 4.2 计算权重
    for r in res2 :
        result[r['result']] += 1-r["distance"]/sum
    
    # 4.3 得出结果
    if result['B'] > result['M']:
        return "B"
    else:
        return "M"

    
# print(distance(train_data[0],train_data[1]))
# 预测结果和真实结果对比,计算准确率
for k in range(1,10):
    correct = 0
    for test in test_data:
        result = test["diagnosis_result"]
        result2 = knn(test,k)
        if result == result2:
            correct += 1
    print("k="+str(k)+"时,准确率{:.2f}%".format(100*correct/len(test_data)))