算法简介
KNN(K Near Neighbor)意为k个最近邻居,对于一个未知样本,通过计算训练集中最相似(距离最近)的k个样本,利用它们的标签进行分类或者回归预测。该算法简单易懂并且在解决一些问题表现出了出色的性能。
KNN的工作原理
对于两个不同的类别,首先提取它们不同的特征并能在坐标轴上表示出来,例如两种电影的不同尽头的出现次数,先暂且分为A类和B类。这样我们就得到了一个数据集用于训练,接下来进行数据归一化。
归一化
不同的数据之间的差值可能很大,归一化数值就是将x轴和y轴的数据化为0~1之间的数,将原本像长方形的数据化为正方形。
这里随便举一些数据:
A:(1,2)、(2,5)、(3,9) 测试数据(2,8)、(5,2)
B:(4,1)、(5,6)、(5,4)
先设归一化后的数据为G,待计算的数据为D,min为数据中的最小值,同理max为最大值。
对于x轴,要处理的数据依次为(1、2、3、2、5、4、5、5),显然min为1,max为5。y轴的数据同理。
根据公式:
处理后的数据:
A:(0,0.125)、(0.25,0.5)、(0.5,1)
B:(0.75,0)、(1,0.625)、(1,0.375)
归一化前的数据
归一化后的数据
可以看到归一化后的数据更加匀称,对于KNN来说,计算点与点的距离是它的核心算法,因此数据之间的差值会影响结果的预测,这就是为什么我们需要归一化。
计算距离
对于每个未知样本,计算它与训练集中所有的样本的距离,在二维平面中计算点之间的距离的方式有很多,本文就使用大家比较熟悉的欧式距离。
确定最近邻
即选取距离未知样本最近的k个训练样本,本文中设k为3。
投票决策
对于分类问题,将这k个最近邻的标签进行投票,选择票数最多的类别作为未知样本的类别。对于回归问题,可以取k个最近邻的平均值作为预测值。
代码实现
示例数据
train_data = np.array([[1, 2], [2, 5], [3, 9], [4, 1], [5, 6], [5, 4]])
train_labels = ['A', 'A', 'A', 'B', 'B', 'B']
test_data = np.array([[2, 8], [5, 2]])
归一化处理
def normalize_data(data):
"""
对输入的数据进行归一化处理
参数:
data: 需要归一化的numpy数组,形状为(m, n),其中m是样本数,n是特征数
返回:
normalized_data: 归一化后的数据,形状与原数据相同
"""
# 计算每列特征的最小值和最大值
column_min = np.min(data, axis=0)
column_max = np.max(data, axis=0)
# 对每行数据的每个特征进行归一化
normalized_data = (data - column_min) / (column_max - column_min)
return normalized_data
# 归一化示例数据
normalized_train_data = normalize_data(train_data)
normalized_test_data = normalize_data(test_data)
计算欧式距离
def euclidean_distance(x1, x2):
return np.sqrt(np.sum((x1 - x2) ** 2))
KNN分类器
def knn(train_data, train_labels, test_data, k):
"""
参数:
train_data: 训练数据的numpy数组,形状为(m, n),其中m是样本数,n是特征数
train_labels: 训练标签的列表或numpy数组,长度为m
test_data: 测试数据的numpy数组,形状为(p, n),其中p是测试样本数
k: 最近邻居的数量
返回:
test_labels: 测试数据的预测标签列表
"""
test_labels = []
for i in range(test_data.shape[0]):
# 初始化距离和标签列表
distances = [euclidean_distance(test_data[i], train_data[j]) for j in range(train_data.shape[0])]
labels = [train_labels[j] for j in range(train_data.shape[0])]
# 获取最近的k个邻居的索引
k_indices = np.argsort(distances)[:k]
# 提取最近的k个邻居的标签
k_nearest_labels = [labels[i] for i in k_indices]
# 使用最常见的标签作为预测标签
most_common = Counter(k_nearest_labels).most_common(1)
test_labels.append(most_common[0][0])
return test_labels
运行结果
KNN的优缺点
KNN算法有着诸多优点,如简单易懂、无需训练过程等。但同时也存在一些缺点,例如对于大规模数据集计算开销大、需要存储全部训练数据等。
结语
KNN算法作为一种简单而有效的分类方法,为我们提供了解决问题的一种思路。通过本文的介绍,相信读者对KNN算法有了更深入的理解,并能够灵活运用到实际问题中。在实际应用中,我们可以根据具体问题的需求对KNN进行改进和优化,使其发挥更大的作用。若有错误,请在评论区指出,谢谢。