本文主要介绍KNN邻近规则算法的过程:
目录
1.KNN临近规则算法概述
KNN是机器学习领域一个简单又实用的算法。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。
kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。有点类似,“物以类聚,人以群分”的意味。
2.KNN算法的构建
2.1 KNN
KNN中用欧式距离来度量特征向量之间的距离。欧式距离,即对于两个n维向量x和y,两者的欧式距离定义为:
当然特征向量之间的距离求法有很多种,但是欧式距离的求法基本上能满足我们的日常需求。
2.2 KNN构建
为了判断未知实例的类别,以所有已知类别的实例作为参照,算法过程为:
- 选择参数K,人为设定即可,一般为奇数较佳;
- 计算未知实例与所有已知实例的距离;
- 选择最近K个已知实例;
- 根据少数服从多数的投票法则,让未知实例归类为K个最邻近样本中最多数的类别。
我这里杜撰了一批数据集,用于具体实现KNN算法:
构建数学模型,这里列举了前十个,映射到空间坐标点为:
于是,编写计算方法:
import math
def ComputeEuclideanDistance(x1, y1, x2, y2):
'''计算两点之间的欧式距离'''
d = math.sqrt(math.pow((x1-x2), 2) + math.pow((y1-y2), 2))
return d
## 数据集
data_set = []
## 坐标点
A=(3,104)
data_set.append(A)
B=(2,100)
data_set.append(B)
C=(1,81)
data_set.append(C)
D=(101,10)
data_set.append(D)
E=(99,5)
data_set.append(E)
F=(98,2)
data_set.append(F)
G=(0,5)
data_set.append(G)
H=(0,20)
data_set.append(H)
I=(0,3)
data_set.append(I)
J=(18,90)
data_set.append(J)
## 坐标名称
point_dict = {0:'A',1:'B',2:'C',3:'D',4:'E',5:'F',6:'G',7:'H',8:'I',9:'J'}
size = len(data_set)
for i in range(size):
for j in range(size):
if i <j:
x1,y1 = data_set[i][0],data_set[i][1]
x2,y2 = data_set[j][0],data_set[j][1]
d_ij = ComputeEuclideanDistance(x1, y1, x2, y2)
print("坐标点%s-%s之间的欧式距离为:%s"%(point_dict[i],point_dict[j],int(d_ij)))
算出每个点之间的欧式距离分别为:
按照实际应用需求,分为三类,故设定k=3,生成的分类图:
2.3 KNN优缺点
优点:易于理解,容易实现,通过对K的选择可具备丢噪音数据的健壮性。
缺点:需要大量空间储存所有已知实例;算法复杂度高(需要比较所有已知实例与要分类的实例);当其样本分布不平衡时,比如其中一类样本过大(实例数量过多)占主导的时候,新的未知实例容易被归类为这个主导样本,因为这类样本实例的数量过大,但这个新的未知实例实际并木接近目标样本。
3.KNN算法编码实现
3.1 编码过程
实现关键步骤在代码中做了注释,如下:
import numpy as np
from numpy import *
import operator
import matplotlib.pylab as plt
# 创建一个数据集,包含3个类别共40个样本
def createDataSet():
# 生成一个矩阵,每行表示一个样本
group = array([[3,104], [2,100], [1,81], [101,10],[99,5],[98,2],[0,5],[0,20],[0,3],[18,90],
[1,50],[2,6],[4,7],[5,21],[20,80],[10,30],[12,91],[2,103],[40,132],[34,131],
[21,101],[112,54],[92,65],[110,87],[20,30],[40,12],[50,1],[3,32],[43,2],[21,5],
[1,32],[2,45],[7,31],[121,92],[105,21],[91,32],[105,31],[32,87],[65,102],[10,91]])
# 4个样本分别所属的类别
labels = '''潜客,潜客,潜客,熟客,熟客,熟客,生客,生客,生客,潜客,潜客,生客,生客,生客,潜客,生客,潜客,潜客,潜客,潜客,潜客,熟客,熟客,熟客,生客,生客,生客,生客,生客,生客,生客,生客,生客,熟客,熟客,熟客,熟客,潜客,潜客,潜客'''.split(',')
return group, labels
def plot_figure(data_mat, labels):
"""
没有加入legend
有需要参考:https://www.zhihu.com/question/37146648
"""
# 生成一个新的图像
fig = plt.figure()
# 这里的111的意思就是,图像画成一行一列(其实就一个框),最后一个1就是放在从左到右,从上到下的第1个
# 想在一个画面里面放多几个子图和分配位置改下这个参数就好了
ax = fig.add_subplot(111)
# 前两个参数试一试data_mat[:,1],data_mat[:,2]
# 或者data_mat[:,1],data_mat[:,0] # 这种的区分度更高
# scatter是画散点图
# 10是点的大小,前后是颜色
l = []
for label in labels:
if label =='潜客':
l.append(1)
elif label =='熟客':
l.append(2)
elif label =='生客':
l.append(3)
else:print('ERR:',label)
labels = l
t = ax.scatter(data_mat[:,1],data_mat[:,0],
10 * np.array(labels),np.array(labels))
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
plt.title('Potential Customer Discovery')
plt.xlabel('Buy Times')
plt.ylabel('Click Times')
plt.show()
# KNN分类算法函数定义
def kNNClassify(newInput, dataSet, labels, k):
numSamples = dataSet.shape[0]
# shape[0]表示行数
diff = tile(newInput, (numSamples, 1)) - dataSet # 按元素求差值
squaredDiff = diff ** 2 # 将差值平方
squaredDist = sum(squaredDiff, axis = 1) # 按行累加
distance = squaredDist ** 0.5 # 将差值平方和求开方,即得距离
# step 2: 对距离排序
# argsort() 返回排序后的索引值
sortedDistIndices = argsort(distance)
classCount = {} # define a dictionary (can be append element)
for i in range(k):
# step 3: 选择k个最近邻
voteLabel = labels[sortedDistIndices[i]]
# step 4: 计算k个最近邻中各类别出现的次数
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
# step 5: 返回出现次数最多的类别标签
maxCount = 0
for key, value in classCount.items():
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex
# 生成数据集和类别标签
dataSet, labels = createDataSet()
# 定义一个未知类别的数据
testX = array([18, 90])
k = 3
# 调用分类函数对未知数据分类
outputLabel = kNNClassify(testX, dataSet, labels, 3)
plot_figure(dataSet, labels)
print ("输入测试集:", testX, "分类: ", outputLabel)