K最近邻(KNN,K-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。所谓K最近邻,就是K个最近的邻居的意思,说的是每个样本都可以用它最接近的K个邻近值来代表。 K最近邻算法就是将数据集合中每一个记录进行分类的方法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。
优点:
缺点:
如上图所示,图中的数据可以分为蓝色正方形和红色三角形两类,图中心的绿色圆点是待分类数据,下面我们通过K最近领法对绿色圆点进行分类:
1.当 k = 3 k=3 k=3时,由图中实线圆内的数据可知:绿色圆点最近领的三个邻居中,一共有一个蓝色正方形和两个红色三角形,那么就可以将绿色圆点和红色三角形判定为一类。
2.当 k = 5 k=5 k=5时,由图中虚线圆内的数据可知:绿色圆点最近领的五个邻居中,一共有三个蓝色正方形和两个红色三角形,那么就可以将绿色圆点和蓝色正方形判定为一类。
至此,我们对KNN算法已经有了大概的了解。
初始化训练集和测试集。训练集一般为两类或者多种类别的数据;测试集一般为一个数据。
计算测试数据到其他所有数据的距离,并记录下来。
常用到的距离计算公式如下:
①欧几里得距离(欧氏距离): d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 d=\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}} d=(x1−x2)2+(y1−y2)2
②曼哈顿距离
③闵可夫斯基距离
④切比雪夫距离
⑤马氏距离
⑥余弦相似度
⑦皮尔逊相关系数
⑧汉明距离
⑨杰卡德相似系数
⑩编辑距离
⑪DTW 距离
⑫KL 散度
将所有距离进行升序排序,确定K值,最近的K个邻居即距离最短的K个数据。
关于K值得选择问题:
三种交叉验证方法:
明确K个邻居中所有数据类别的个数,将测试数据划分给个数最多的那一类。即由输入实例的 K 个最临近的训练实例中的多数类决定输入实例的类别。
最常用的两种决策规则:
from operator import attrgetter
import matplotlib.pyplot as plt
import matplotlib
from math import sqrt
class point:
def __init__(self, kind, dis):
self.kind = kind
self.dis = dis
#####初始化数据集#####
data_A = [[1,2],[3.2,4],[4,7],[5.2,3],[7,4.1]]#数据集A
data_B = [[2.2,5.5],[4.2,2],[5,5],[6.3,7]]#数据集B
test_data = [[4.5,4.5], [1, 2]]#测试集
num_A = len(data_A)
num_B = len(data_B)
num_T = len(test_data)
def getDis(p1, p2):
return sqrt(pow(p1[0] - p2[0], 2) + pow(p1[1] - p2[1], 2))
# calc dist
def calcDist():
lsDist = [[], []]
ls = []
for i in range(len(test_data)):
for j in range(num_A):
lsDist[i].append(point(0, getDis(data_A[j], test_data[i])))
ls.append(getDis(data_A[j], test_data[i]))
for j in range(num_B):
lsDist[i].append(point(1, getDis(data_B[j], test_data[i])))
ls.append(getDis(data_B[j], test_data[i]))
return lsDist, ls
def judge(k, lsDist):
num0 = 0
num1 = 0
for j in range(len(test_data)):
for i in range(k):
if(lsDist[j][i].kind == 0):
num0 += 1
else:
num1 += 1
if(num0 > num1):
print('A类')
else:
print('B类')
def draw():
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
for i in range(num_A-1):
plt.plot(data_A[i][0], data_A[i][1], 'r^')
plt.plot(data_A[num_A-1][0], data_A[num_A-1][1], 'r^', label='A')
for i in range(num_B-1):
plt.plot(data_B[i][0], data_B[i][1], 'bo')
plt.plot(data_B[num_B-1][0], data_A[num_B-1][1], 'bo', label='B')
for i in range(num_T-1):
plt.plot(test_data[i][0], test_data[i][1], 'k+')
plt.plot(test_data[num_T-1][0], test_data[num_T-1][1], 'k+', label = '未标识')
plt.legend()
plt.xlim(0, 10)
plt.ylim(0, 10)
plt.show()
def printf(lsDist):
for j in range(len(lsDist)):
for i in range(len(lsDist[j])):
print('({},{})'.format(lsDist[j][i].dis, lsDist[j][i].kind))
if __name__ == '__main__':
lsDist, ls = calcDist()
print('距离列表')
printf(lsDist)
for i in range(len(lsDist)):
lsDist[i] = sorted(lsDist[i], key=attrgetter('dis'))
print('排序后的距离列表')
printf(lsDist)
k = int(input('请输入k:'))
judge(k, lsDist)
draw()