机器学习算法原理实现——k近邻算法 KNN

K近邻算法是一种基于距离度量的数据分类模型,其基本做法是首先确定输入实例的[插图]个最近邻实例,然后利用这[插图]个训练实例的多数所属的类别来预测新的输入实例所属类别。

 

k最近邻(k-nearest neighbors,KNN)算法是一种基本的分类和回归算法。其基本原理如下:
1. 训练阶段:将训练样本集中的数据和对应的标签存储起来,构建一个训练模型。
2. 预测阶段:对于一个新的测试样本,计算它与训练样本集中所有样本的距离(通常使用欧氏距离或其他距离度量方法)。
3. 选择k个最近邻:根据距离计算结果,选择与测试样本距离最近的k个训练样本
4. 进行投票或计算平均值:对于分类任务,根据k个最近邻的标签进行投票,将得票最多的标签作为测试样本的预测标签。对于回归任务,根据k个最近邻的标签或值进行平均计算,将计算结果作为测试样本的预测值
KNN算法的关键是选择合适的k值和距离度量方法。k值的选择会影响算法的性能,较小的k值可能会导致过拟合,较大的k值可能会导致欠拟合。距离度量方法的选择应根据具体问题和数据特点进行合理选择。

KNN算法的优点包括简单易懂、易于实现、适用于多分类和回归问题。然而,KNN算法的缺点是计算复杂度高,对于大规模数据集的预测速度较慢。此外,KNN算法对于特征空间的维度敏感,需要进行特征选择或降维等预处理操作。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# 导入相关模块
import numpy as np
from collections import Counter
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.utils import shuffle
 
### 定义欧氏距离
def compute_distances(X, X_train):
    '''
    输入:
    X:测试样本实例矩阵
    X_train:训练样本实例矩阵
    输出:
    dists:欧式距离
    '''
    # 测试实例样本量
    num_test = X.shape[0]
    # 训练实例样本量
    num_train = X_train.shape[0]
    # 基于训练和测试维度的欧氏距离初始化
    dists = np.zeros((num_test, num_train))
    # 测试样本与训练样本的矩阵点乘
    M = np.dot(X, X_train.T)
    # 测试样本矩阵平方
    te = np.square(X).sum(axis=1)
    """
    >>> import numpy as np
    >>> a = np.array([[1,2,3],[4,5,6]])
    >>> np.sum(a)
    21
    >>> np.sum(a, axis=0)
    array([5, 7, 9])
    >>> np.sum(a, axis=1)
    array([ 6, 15])
    X = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])
    ==>np.square(X).sum(axis=1) 结果为 [14 77 194]   
    """
    # 训练样本矩阵平方
    tr = np.square(X_train).sum(axis=1)
 
    # 计算欧式距离
    dists = np.sqrt(-2 * M + tr + np.matrix(te).T) # 自己可以简单推导下 的确如此
    return dists
 
### 定义预测函数
def predict_labels(y_train, dists, k=1):
    '''
    输入:
    y_train:训练集标签
    dists:测试集与训练集之间的欧氏距离矩阵
    k:k值
    输出:
    y_pred:测试集预测结果
    '''
    # 测试样本量
    num_test = dists.shape[0]
    # 初始化测试集预测结果
    y_pred = np.zeros(num_test)
    # 遍历
    for i in range(num_test):
        # 初始化最近邻列表
        closest_y = []
        # 按欧氏距离矩阵排序后取索引,并用训练集标签按排序后的索引取值
        # 最后展平列表
        # 注意np.argsort函数的用法
        labels = y_train[np.argsort(dists[i, :])].flatten()
        # 取最近的k个值
        closest_y = labels[0:k]
        # 对最近的k个值进行计数统计
        # 这里注意collections模块中的计数器Counter的用法
        c = Counter(closest_y)
        # 取计数最多的那个类别
        y_pred[i] = c.most_common(1)[0][0]
    return y_pred
 
 
"""
>>> x = np.array([3, 1, 2])
>>> np.argsort(x)
array([1, 2, 0])
# 创建计数对象
>>> c = Counter('abcdeabcdabcaba')
# 取计数前三的元素
>>> c.most_common(3)
[('a', 5), ('b', 4), ('c', 3)]
"""
 
# 导入sklearn iris数据集
iris = datasets.load_iris()
# 打乱数据后的数据与标签
X, y = shuffle(iris.data, iris.target, random_state=13)
# 数据转换为float32格式
X = X.astype(np.float32)
# 简单划分训练集与测试集,训练样本-测试样本比例为7:3
offset = int(X.shape[0] * 0.7)
X_train, y_train = X[:offset], y[:offset]
X_test, y_test = X[offset:], y[offset:]
# 将标签转换为竖向量
y_train = y_train.reshape((-1,1))
y_test = y_test.reshape((-1,1))
# 打印训练集和测试集大小
print('X_train=', X_train.shape)
print('X_test=', X_test.shape)
print('y_train=', y_train.shape)
print('y_test=', y_test.shape)
 
dists = compute_distances(X_test, X_train)
# 测试集预测结果
y_test_pred = predict_labels(y_train, dists, k=10)
y_test_pred = y_test_pred.reshape((-1, 1))
# 找出预测正确的实例
num_correct = np.sum(y_test_pred == y_test)
# 计算分类准确率
accuracy = float(num_correct) / X_test.shape[0]
print('KNN Accuracy based on NumPy: ', accuracy)
 
# 导入KNeighborsClassifier模块
from sklearn.neighbors import KNeighborsClassifier
# 创建k近邻实例
neigh = KNeighborsClassifier(n_neighbors=10)
# k近邻模型拟合
neigh.fit(X_train, y_train)
# k近邻模型预测
y_pred = neigh.predict(X_test)
# 预测结果数组重塑
y_pred = y_pred.reshape((-1, 1))
# 统计预测正确的个数
num_correct = np.sum(y_pred == y_test)
# 计算分类准确率
accuracy = float(num_correct) / X_test.shape[0]
print('KNN Accuracy based on sklearn: ', accuracy)

 

运行结果:

X_train= (105, 4)
X_test= (45, 4)
y_train= (105, 1)
y_test= (45, 1)
KNN Accuracy based on NumPy:  0.9777777777777777
KNN Accuracy based on sklearn:  0.9777777777777777

 

其中,计算举例的可以使用下面更加简洁的函数:

1
2
3
4
5
6
7
8
def compute_distances(X, X_train):
    num_test = X.shape[0]
    num_train = X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in range(num_test):
        for j in range(num_train):
            dists[i, j] = np.sqrt(np.sum(np.square(X[i] - X_train[j])))
    return dists

  

predict我自己写的话,是下面这样:

1
2
3
4
5
6
7
8
def predict_labels(y_train, dists, k=1):
    num_test = dists.shape[0]
    y_pred = np.zeros(num_test)
    for i in range(num_test):
        sorted_indices = np.argsort(dists[i])
        closest_y = y_train[sorted_indices[:k]].flatten()
        y_pred[i] =  Counter(closest_y).most_common()[0][0]
    return y_pred

  

posted @   bonelee  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-09-10 metersploit msf 常用命令
2022-09-10 windows 7 iso下载地址
2020-09-10 小样本学习文献
2019-09-10 联邦学习
2019-09-10 数据库索引数据结构总结——ART树就是前缀树
点击右上角即可分享
微信分享提示