机器学习(九):KNN算法全解析与项目实践
声明:未经允许,禁止转载
算法原理
kkk近邻算法是一种常见的懒惰监督学习算法,懒惰指的是该算法不需要训练,只需要计算测试样本到各个训练集样本的距离,然后找出距离测试样本最近的kkk个训练样本:
- 对于分类任务,使用投票法,将kkk个训练样本的大多数样本所属的类别作为该预测样本的类别;
- 对于回归任务,将kkk个样本实值的均值作为预测结果。
在kkk近邻算法中,kkk和距离度量的选择都十分重要,kkk取不同值时,分类结果可能会差异很大(见下图),而距离度量的不同会影响kkk个近邻的选择。
算法实践
本文在鸢尾花数据集上进行kkk近邻算法的实践。数据集按训练集和测试集7:37:37:3的比例划分,具体实现代码如下:
import numpy as np
from scipy.spatial.distance import cdist
from scipy.stats import mode
from process import read_data
from sklearn.metrics import accuracy_score
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as pltclass KNNClassifier:def __init__(self, k, metric='euclidean'):self.k = kself.train_x = Noneself.train_y = Noneself.metric = metricdef fit(self, x, y):self.train_x = xself.train_y = ydef euclidean_distance(self, x1, x2):return cdist(x1, x2, metric=self.metric)def predict(self, test_x):# 距离计算dis_x = self.euclidean_distance(test_x, self.train_x)# 获取测试样本对应的k个最近邻的索引k_indices = np.argsort(dis_x, axis=1)[:, :self.k]# 根据索引获取对应的标签k_labels = self.train_y[k_indices]# 对每个样本的k个最近邻的标签进行投票选出频率最高的作为预测概率y_hat, _ = mode(k_labels, axis=1)return y_hatif __name__ == "__main__":data_path = "../datasets/iris.csv"metric = 'euclidean'train_x, train_y, test_x, test_y = read_data(data_path)k_max = 20accs = []for k in range(1, k_max + 1):# 自建模型cknn = KNNClassifier(k, metric)cknn.fit(train_x, train_y)cy_hat = cknn.predict(test_x)custom_acc = accuracy_score(test_y.reshape(-1), cy_hat.reshape(-1))# 调库sknn = KNeighborsClassifier(n_neighbors=k, metric=metric)sknn.fit(train_x, train_y)sy_hat = sknn.predict(test_x)sklearn_acc = accuracy_score(test_y.reshape(-1), sy_hat.reshape(-1))accs.append(custom_acc)print("k[{}]\tcustom acc[{}]\tsklearn acc[{}]".format(k, custom_acc, sklearn_acc))x = np.arange(1, k_max + 1)plt.figure()plt.xlabel("k")plt.ylabel("accuracy")plt.xticks(x)plt.plot(x, accs, marker="<")plt.savefig("accuracy.png")
实践结果中计算了k=[1,2,3,...,20]k=[1,2,3,...,20]k=[1,2,3,...,20]时的准确率,下图为对应的结果,横轴为kkk的取值,纵轴为对应在测试集上的准确率:
结语
参考资料:《机器学习》周志华
源码地址:KNN
以上便是本文的全部内容,若有任何错误敬请批评指正,觉得不错的话可以支持一下,不胜感激!!!。