机器学习 K近邻算法(KNN算法)
2.K近邻算法(KNN算法)
2.1简单KNN模型实例
2.1.1问题描述
本次实例采用暴力搜索的方法,即最原始的 kNN 方法进行实例讲解。数据集为鸢尾花数据集,可以在机器学习函数库 sklearn 中调用。
2.1.2鸢尾花数据集
Iris 鸢尾花数据集是一个经典的数据集,在统计学习和机器学习领域中经常被用作示例。数据集内包含 3 类共 150 条记录,每类记录各有 50 个数据,每条记录都有 4 项特征:花萼长度;花萼宽度;花瓣长度;花瓣宽度。因此,可以通过这 4 个特征来预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种[129]。
2.1.3算法实现
实例分析:BFS 实现鸢尾花数据集分类
(1) 处理数据:使用 sklearn 数据集,加载鸢尾花数据集。然后划分为训练集和测试集。
(2) 构建 kNN 算法:由于 kNN 算法中不具有显式学习过程,所以该部分代码包含预测代码。
(3) 评估精度:评估对于测试数据集的预测精度作为预测正确率[19]。
(4) 完整代码:使用所有代码呈现一个完整的、独立的支持向量机算法的实现[18]。
(5) 实验结果:通过改变 k 的取值,观察实验结果。
2.2代码及其解释
2.2.1生成/导入数据
def create_data():
iris = load_iris()
data, target = iris.data, iris.target
name = ["花萼长度", "花萼宽度", "花瓣长度", "花瓣宽度"]
visualize_feature(data, target, name)
x_train,x_test,y_train,y_test=train_test_split(data, target, test_size=0.33, random_state=42)
return x_train, x_test, y_train, y_test
2.2.2数据可视化
下面通过可视化鸢尾花数据的 4 种特征,帮助我们更好了解数据分布,具体代码如下:
def visualize_feature(X, Y, name_data):
for i in range(len(name_data)):
plt.subplot(2, 2, i+1)
plt.scatter(y=X[:, i], x=Y[:, ], c=Y[:, ])
plt.xlabel("鸢尾花标签")
plt.ylabel(name_data[i])
plt.suptitle("鸢尾花数据的4种特征")
plt.tight_layout() # 使子图标题和全局标题与坐标轴不重叠
plt.show()

2.2.3构建KNN算法
(1)参数初始化
def __init__(self, K, X_train, Y_train):
self.k = K
self.x_train = np.array(X_train)
self.y_train = np.array(Y_train)
(2)对测试集进行预测
def predict(self, data):
labels = np.repeat(self.y_train.reshape(1, -1), len(data), axis=0)
dist = cdist(data, self.x_train)
for i in range(len(labels)):
Z = zip(dist[i], labels[i])
Z = sorted(Z, reverse=False)
dist[i], labels[i] = zip(*Z)
labels = labels[:, :self.k]
res = [np.argmax(np.bincount(label)) for label in labels]
return res
(3)评估精度
def evaluate(self, data, label, i):
"""
评估精度
"""
pred = self.predict(data)
acc = np.sum(pred == label) / len(label)
print("当 k = {} 时, 准确率为:{}%".format(i, acc * 100))
return acc
2.2.4完整代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
plt.rcParams['font.sans-serif'] = ['SimHei'] # 正常显示中文标签
class KNN(object):
def __init__(self, K, X_train, Y_train):
self.k = K
self.x_train = np.array(X_train)
self.y_train = np.array(Y_train)
def predict(self, data):
"""
对测试集进行预测
"""
labels = np.repeat(self.y_train.reshape(1, -1), len(data), axis=0)
dist = cdist(data, self.x_train)
for i in range(len(labels)):
Z = zip(dist[i], labels[i])
Z = sorted(Z, reverse=False)
dist[i], labels[i] = zip(*Z)
labels = labels[:, :self.k]
res = [np.argmax(np.bincount(label)) for label in labels]
return res
def evaluate(self, data, label, i):
"""
评估精度
"""
pred = self.predict(data)
acc = np.sum(pred == label) / len(label)
print("当 k = {} 时, 准确率为:{}%".format(i, acc * 100))
return acc
def create_data():
"""
生成数据
"""
iris = load_iris()
data, target = iris.data, iris.target
name = ["花萼长度", "花萼宽度", "花瓣长度", "花瓣宽度"]
visualize_feature(data, target, name)
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.33, random_state=42)
return x_train, x_test, y_train, y_test
def visualize_feature(X, Y, name_data):
"""
可视化鸢尾花的4个特征数据
"""
for i in range(len(name_data)):
plt.subplot(2, 2, i + 1)
plt.scatter(y=X[:, i], x=Y[:, ], c=Y[:, ])
plt.xlabel("鸢尾花标签")
plt.ylabel(name_data[i])
plt.suptitle("鸢尾花数据的4种特征")
plt.tight_layout() # 使子图标题和全局标题与坐标轴不重叠
plt.show()
def visualize_results(k, acc):
plt.plot(k, acc)
plt.xlabel("K 值")
plt.ylabel("准确率")
plt.title("暴力搜索算法")
plt.show()
if __name__ == '__main__':
x_train, x_test, y_train, y_test = create_data()
acc_list = []
k_list = []
for i in range(1, 102, 2):
k = KNN(i, x_train, y_train)
acc = k.evaluate(x_test, y_test, i)
acc_list.append(acc)
k_list.append(i)
visualize_results(k_list, acc_list)
# 根据 k_list 与 acc_list 选取k值11进行验证
knn = KNeighborsClassifier(n_neighbors=11)
knn.fit(x_train, y_train)
p_res = knn.predict(x_test)
print(f"预测结果:{p_res}")
score = knn.score(x_test, y_test)
print(f"预测准确率:{score * 100}%")
2.3实验结果
2.3.1观察K值变化对准确度的影响
for i in range(1, 102, 2):
k = KNN(i, x_train, y_train)
acc = k.evaluate(x_test, y_test, i)
acc_list.append(acc)
k_list.append(i)

从上面的实验结果可以证明,k 值的选择直接可以影响实验结果,当 k 取 1 时为最近邻算法,实际上,当 k 为 1 时,算法对于测试数据点将会非常敏感,但是由于鸢尾花数据集简单、异常数据少,所以能够呈现出较好的效果。随着 k 值的增大,算法的表现能力也开始变得更为优秀,但是当 k 值取得很大的时候,模型的表现迅速下降,所以 k 值的选择应该要小于数据集大小的开方。并且需要注意一点的就是 k 值的选取需为奇数。
2.3.2验证实验结果
# 根据 k_list 与 acc_list 选取k值11进行验证
knn = KNeighborsClassifier(n_neighbors=11)
knn.fit(x_train, y_train)
p_res = knn.predict(x_test)
print(f"预测结果:{p_res}")
score = knn.score(x_test, y_test)
print(f"预测准确率:{score * 100}%")
>>>
预测结果:[1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0 0 0 1 0 0 2 1 0 0 0 2 1 1 0 0 1 2 2 1 2]
预测准确率:100.0%

浙公网安备 33010602011771号