KNN算法算是非常出名的一个算法了,KNN是K-Nearest Neighbors的简写,即K个最近的邻居的意思,因此该算法也被称为K近邻算法。它的核心思想就是物以群分,离它最近的那些跟它会有相似的属性。KNN算法通常用于分类问题,也可以用于回归问题,不过回归的话则要复杂一些,需要用到加权才可以处理连续值,本节只介绍KNN在分类问题上的使用。
KNN算法的两个核心问题是:
第一个就是怎么定义远近,通常有很多种定义方式,比如使用曼哈顿距离,比如两个点(x1,y1)和(x2,y2),那么它的曼哈顿距离就是|x1-x2|+|y1-y2|,也就是两个坐标x1和x2相减的绝对值加上y1和y2相减的绝对值,其他的距离还有欧式距离,闵可夫斯基距离,切比雪夫距离等。
第二个就是K值怎么取,如果K取的太小,很容易被恰巧出现的噪声干扰,比如取2,很容易出现选择的数据恰好是噪声数据。如果K取的太大,又很容易被整体数据分布干扰,比如取整个样本集的数据,那么无论对哪个点进行分类,都只会得到一个结果。
KNN算法的使用在sklearn这个框架中非常简单,只需要使用KNeighborsClassifier这个类就可以了,只需要指定n_neibors这个参数,比如我们看一个具体的例子:
from sklearn.neighbors import KNeighborsClassifier
# 初始化数据
x = [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
# 初始化 KNN 分类器,这里 k 值设为 3
knn = KNeighborsClassifier(n_neighbors=3)
# 训练模型
knn.fit(x, y)
# 预测新数据
new_data = [[2.5], [6.5], [7.5]]
# 预测新数据的类别
predictions = knn.predict(new_data)
print(predictions)
上面代码我们有10条数据,前5个是0,后5个是1,那么我们接下来预测2.5、6.5和7.5的值,这里我们会发现2.5周围的像1、2、3、4对应的标签值都是0,那么它的值也会是0,而7.5周围的像6、7、8、9对应的标签值都是1,那么它的预测值也会是1,来看下执行结果:
接下来我们看一下sklearn自带的鸢尾花的数据集,这个数据集只需要load_iris即可导入进来,然后我们根据它的花萼长度和宽度使用knn算法进行预测,如下所示:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
# 加载鸢尾花数据集
iris = load_iris()
X = iris.data[:, :2] # 为了方便可视化,仅选取前两个特征
y = iris.target
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 创建 KNN 模型
knn = KNeighborsClassifier(n_neighbors=5)
# 训练模型
knn.fit(X_train, y_train)
# 预测测试集
y_pred = knn.predict(X_test)
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
print(f"模型准确率: {accuracy:.2f}")
print("预测结果:", y_pred)
print("真实结果:", y_test)
print("预测结果与真实结果的比较:", y_pred == y_test)
# 可视化训练数据
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='viridis', edgecolor='k', s=50, label='train data')
plt.title('train data')
plt.xlabel('sepal length (cm)')
plt.ylabel('sepal width (cm)')
# 可视化测试数据和预测结果
plt.subplot(1, 2, 2)
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='viridis', edgecolor='k', s=50, label='test data')
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_pred, cmap='viridis', marker='x', s=100, label='predict data')
plt.title('test and predict')
plt.xlabel('sepal length (cm)')
plt.ylabel('sepal width (cm)')
plt.legend()
plt.tight_layout()
plt.show()
上面代码大部分都比较简单,而且还都有注释,所以就不过多解释了,需要说明的是,sepal length表示花萼长度,sepal width表示花萼宽度,这个是鸢尾花的数据特征。然后我们先看一下它的准确率吧:
可以看到准确率是78%,然后下面把预测结果和真实结果打印了出来,这里的0、1、2表示三个分类,分别代表花的三种类型,然后我们看一下预测的可视化的部分:
上面图的左边部分是训练数据,右边是真实数据和预测数据,需要特别说明的是,如果x和圆形标记的颜色一样则表示预测正确,如果x和原型标记的颜色不一样,则表示预测错误,比如下面的就是预测错误的:
当然经过我的多次测试,发现如果使用四个特征来使用knn进行预测,在knn的k取值为5的时候准确率可以接近100%,但是因为整体数据集有四个特征,可视化不太方便,所以就没有选择展示出来。