K近邻(K-nearst neighbors, KNN)
所谓k近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表。
KNN算法既可以应用于分类应用中,也可以应用在回归应用中。KNN在做回归和分类的主要区别在于最后做预测的时候的决策方式不同。 KNN在分类预测时,一般采用多数表决法;而在做回归预测时,一般采用平均值法。
基本步骤:
1.从训练集合中获取K个离待预测样本距离最近的样本数据;
2.根据获取得到的K个样本数据来预测当前待预测样本的目标属性值。
1.K值的选择:对于K值的选择,一般根据样本分布选择一个较小的值,然后通过交叉验证来选择一个比较合适的最终值;当选择比较小的K值的时候,表示使用较小领域中的样本进行预测,训练误差会减小,但是会导致模型变得复杂容易过拟合;当选择较大的K值的时候,表示使用较大领域中的样本进行预测,训练误差会增大,同时会使模型变得简单,容易导致欠拟合;
K的取值尽量要取奇数,以保证在计算结果最后会产生一个较多的类别,如果取偶数可能会产生相等的情况,不利于预测。
2.距离的度量:一般使用欧氏距离(欧几里得距离);
3.决策规则:在分类模型中,主要使用多数表决法或者加权多数表决法;在回归模型中,主要使用平均值法或者加权平均值法。
多数表决法:每个邻近样本的权重是一样的,也就是说最终预测的结果为出现类别最多的那个类。
加权多数表决法:每个邻近样本的权重是不一样的,一般情况下采用权重和距离成反比的方式来计算,也就是说最终预测结果是出现权重最大的那个类别。
平均值法:每个邻近样本的权重是一样的,也就是说最终预测的结果为所有邻近样本的目标属性值的均值。
加权平均值法:每个邻近样本的权重是不一样的,一般情况下采用权重和距离成反比的方式来计算,也就是说在计算均值的时候进行加权操作。
蛮力实现(brute):计算预测样本到所有训练集样本的距离,然后选择最小的k个距离即可得到K个最邻近点。缺点在于当特征数比较多、样本数比较多的时候,算法的执行效率比较低;
KD树(kd tree): KD树算法中,首先是对训练数据进行建模,构建KD树,然后再根据建好的模型来获取邻近样本数据。,除此之外,还有一些从KD-Tree修改后的求解最邻近点的算法,比如: Ball Tree, BBF Tree, MVP Tree等。
(代码分析写在注释里了)
"""
步骤
1.数据准备
2.计算距离
3.寻找邻居
4.决策分类
"""
import numpy as np
from matplotlib import pyplot as plt
#计算距离公式
def d_man(x, y):#曼哈顿
d = np.sum(np.abs(x - y))
return d
def d_euc(x, y):#欧式距离
d = np.sqrt(np.sum(np.square(x - y)))
return d
#返回字典
#key函数(itemgetter)获得对象的第一个域的值,定义reverse=True时将按降序排列
#items返回字典dict(key,value)元组对的列表
import operator
def majority_voting(class_count):
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)#itemgetter(a)获得第_a_个值,默认从小到大
return sorted_class_count
def create_data():
features = np.array(
[[2.88, 3.05], [3.1, 2.45], [3.05, 2.8], [2.9, 2.7], [2.75, 3.4],
[3.23, 2.9], [3.2, 3.75], [3.5, 2.9], [3.65, 3.6], [3.35, 3.3]]) #np.array构建二维数组且无需指针
labels = ['A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B']
return features, labels
def knn_classify(test_data, train_data, labels, k):
distances = np.array([]) # 创建一个空数组用于存放距离
for each_data in train_data: # 使用欧式距离计算数据相似度
d = d_euc(test_data, each_data)
distances = np.append(distances, d) #为原始array添加values
print(f"距离相似度distances={distances}")
sorted_distance_index = distances.argsort() # 获取按距离大小排序后的索引(argsort提取索引)
sorted_distance = np.sort(distances) #复制distances数组(对原数组无影响)
print(f"查看sorted_distance_index{sorted_distance_index}")
print(f"查看sorted_distance{sorted_distance}")
r = (sorted_distance[k]+sorted_distance[k-1])/2 # 计算圆的半径
#计算 'A','B'两类别在范围内的数量
class_count = {} #定义一个空字典
for i in range(k): #在排好序的数组中挑出距离最短的五个
vote_label = labels[sorted_distance_index[i]] #根据索引找到距离对应的类别
class_count[vote_label] = class_count.get(vote_label, 0) + 1 #!!!!!这步简直了!!我咋就想不到!佩服.jpg!
final_label = majority_voting(class_count)
return final_label, r
features, labels =create_data()
test_data = np.array([3.18, 3.15])
final_label, r = knn_classify(test_data, features, labels, 5) #k=5
print(final_label)
print(r)
#可视化
#极坐标方式表示圆
def circle(r, a, b): # 为了画出圆,这里采用极坐标的方式对圆进行表示 :x=r*cosθ,y=r*sinθ。
theta = np.arange(0, 2*np.pi, 0.01)
x = a+r * np.cos(theta)
y = b+r * np.sin(theta)
return x, y
k_circle_x, k_circle_y = circle(r, 3.18, 3.15)
plt.figure(figsize=(5, 5))
plt.xlim((2.4, 3.8))
plt.ylim((2.4, 3.8))
x_feature = list(map(lambda x: x[0], features)) # 返回每个数据的x特征值
y_feature = list(map(lambda y: y[1], features))
plt.scatter(x_feature[:5], y_feature[:5], c="b") # 在画布上绘画出"A"类标签的数据点
plt.scatter(x_feature[5:], y_feature[5:], c="g") # 在画布上绘画出"B"类标签的数据点
plt.scatter([3.18], [3.15], c="r", marker="x") # 待测试点的坐标为 [3.18,3.15]
plt.plot(k_circle_x, k_circle_y)
下面是学习的时候顺手写的笔记,和上面内容差不多
距离:https://www.cnblogs.com/soyo/p/6893551.html