KNN算法简介
KNN 即 最近邻算法(K-NearestNeighbor),这个算法的核心思想就是物以类聚,近朱者赤近墨者黑,
直接计算K个距离未知点的样本距离,距离最近的样本点取最多的一个类别作为新数据的类别
例如图中,若我们设立K值为1,那么他就会被归纳到Class2中,若k值为5,那么Class1的数量要多余class2了,那么它就会被归纳到class1中,K值决定了距离辐射的范围从而影响着分类结果的准确性
KNN优缺点
KNN优点
- KNN是简单易实现的,KNN算法没有建立任何模型,而是把过往历史数据当作模型本身,将新数据跟历史每一条数据进行对比,不需要任何的训练,得到一条新的未知数据就直接可以进行预测
- KNN对于边界不规则的数据效果好,相较于线性算法用一条直线进行区分,遇到图上这种边界不规则的数据很难做到有效区分
KNN缺点
- 它只适合小数据集,因为他实现简单每次进行新数据的预测只需要比对所有的数据集,所以当数据集非常大时,预测就会消耗非常长的时间,占用非常大的的数据集存储空间
- 数据不平衡的预测效果不好,例如当某种数据样本数量非常多,那么它的竞争优势会远大于其他数据,导致KNN算法失效
- 数据必须标准化,因为使用距离来计算,如果数据字段的量度不同,数值较大的字段影响就会变大,所以需要对数据进行标准化,比如都转化为0-1的区间内
- 不适合太多特征维度的数据,如果数据维度太多,那么数据样本在每个维度上的分布就会很少,例如我们有3个样本,每个样本只有一个维度,这比有1个样本且样本有三个维度,前者特征要明显很多。
K值的选取会影响到模型的效果
在图中我们可以看到左侧是富人区,右侧是普通民众,普通民众的数量是会远大于富人的,若我们设置K值为1,此时若是某富人的房子盖的恰好在河边,离普通民众很近,他就会被判别为普通民众,那么富人区所判定的可能性会越来越小,所有新进来的人都会被判定为普通民众,导致整个模型失去作用。
K值越小越容易过拟合,因为这会导致结果与某一样本强相关
K越大越容易欠拟合,因为考虑的越少就等于什么都不考虑
尝试动手编写代码
我们先导入基础依赖,sklearn是一个Python第三方提供的非常强力的机器学习库,它包含了从数据预处理到训练模型的各个方面,还拥有许多基础的数据集供我们学习使用
# sklearn的数据集
from sklearn import datasets
# sklearn模块的KNN模型
from sklearn.neighbors import KNeighborsClassifier
# 矩阵运算工具库 numpy
import numpy as np
np.random.seed(0)
# 设置随机种子,不设置的话是按照系统时间作参数,有随机种子可以保证我们每次运行结果是一样的
我们选择鸢尾花数据库作为第一个实验,导入查看下结构
# sklearn的数据集
from sklearn import datasets
# sklearn模块的KNN模型
from sklearn.neighbors import KNeighborsClassifier
# 矩阵运算工具库 numpy
import numpy as np
np.random.seed(0)
# 设置随机种子,不设置的话是按照系统时间作参数,有随机种子可以保证我们每次运行结果是一样的
# 获取鸢尾花数据集
iris = datasets.load_iris()
# 查看结构
print(iris)
可以看到特征数据 data 和 标签数据 target 以及标签对应的名称
{'data': array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
.............
[4.6, 3.1, 1.5, 0.2]]), 'target': array([0, 0, 0..... 2, 2, 2, 2]),
'target_names': array(['setosa', 'versicolor', 'virginica']
例如第一条数据有4个特征[5.1, 3.5, 1.4, 0.2],咱暂不去了解这4个特征分别记录鸢尾花哪些属性,对应的标签是0,也就是setosa (山鸢尾花)
那我们的思路就是,通过计算一个新的鸢尾花每个特征 距离 各个样本鸢尾花特征的差值,累计起来,距离最近的K个,例如距离最近的5个都是 标签0 的山鸢尾花,那我们就可以认为他是山鸢尾花
# sklearn的数据集
from sklearn import datasets
# sklearn模块的KNN模型
from sklearn.neighbors import KNeighborsClassifier
# 矩阵运算工具库 numpy
import numpy as np
np.random.seed(0)
# 设置随机种子,不设置的话是按照系统时间作参数,有随机种子可以保证我们每次运行结果是一样的
# 获取鸢尾花数据集
iris = datasets.load_iris()
# 特征数据
iris_datas = iris.data
# 标签数据
iris_targets = iris.target
# 标签名称
iris_names = iris.target_names
# 查看特征数据数量
print("查看特征数据数量:", len(iris_datas))
# 查看标签名称
print("查看标签名称:", iris_names)
# 从150条数据中选择140条作为训练集,10条作为测试集。
# permutation方法接收一个数作为参数【这里我就直接使用特征数据的数量】,产生一个0-149乱序的一维数组
randomArr = np.random.permutation(len(iris_datas))
# 切割出训练集的特征数据 0~倒数第10个截止,也就是切割140个
iris_train_datas = iris_datas[randomArr[:-10]]
# 切割出训练集标签数据
iris_train_target = iris_targets[randomArr[:-10]]
# 切割出测试集的特征数据 倒数第十个开始 往后获取全部,也就是最后10个数据
iris_test_datas = iris_datas[randomArr[-10:]]
# 切割出测试集的标签数据
iris_test_target = iris_targets[randomArr[-10:]]
# 用Sklearn训练好的KNN模型 定义一个KNN分类器 在这里我们设置超参数K值为5
knn = KNeighborsClassifier(5)
# 调用knn对象的训练方法,主要接收两个参数,训练数据集和训练标签集
knn.fit(iris_train_datas, iris_train_target)
# 调用预测方法,主要接收一个参数 测试数据集
iris_test_predict = knn.predict(iris_test_datas)
# 计算各测试样本的预测概率值,这里我没有用概率值,但是在实际工作中可能会参考概率值进行最后的结果筛选,而不是直接使用给出的标签
property = knn.predict_proba(iris_test_datas)
print(property)
我们运行一下代码,看一下结果
可以看到十条测试数据,经过我们KNN模型得到的评估概率【0-1】,该模型认为,第一条数据有100%概率是杂色鸢尾花,第二条数据有40%概率是杂色鸢尾花,60%概率是维吉尼亚鸢尾花
我们不能利用肉眼一条条判断是否正确,所以sklearn内置了一套比对评估的方法
# sklearn的数据集
from sklearn import datasets
# sklearn模块的KNN模型
from sklearn.neighbors import KNeighborsClassifier
# 矩阵运算工具库 numpy
import numpy as np
np.random.seed(0)
# 设置随机种子,不设置的话是按照系统时间作参数,有随机种子可以保证我们每次运行结果是一样的
# 获取鸢尾花数据集
iris = datasets.load_iris()
# 特征数据
iris_datas = iris.data
# 标签数据
iris_targets = iris.target
# 标签名称
iris_names = iris.target_names
# 查看特征数据数量
print("查看特征数据数量:", len(iris_datas))
# 查看标签名称
print("查看标签名称:", iris_names)
# 从150条数据中选择140条作为训练集,10条作为测试集。
# permutation方法接收一个数作为参数【这里我就直接使用特征数据的数量】,产生一个0-149乱序的一维数组
randomArr = np.random.permutation(len(iris_datas))
# 切割出训练集的特征数据 0~倒数第10个截止,也就是切割140个
iris_train_datas = iris_datas[randomArr[:-10]]
# 切割出训练集标签数据
iris_train_target = iris_targets[randomArr[:-10]]
# 切割出测试集的特征数据 倒数第十个开始 往后获取全部,也就是最后10个数据
iris_test_datas = iris_datas[randomArr[-10:]]
# 切割出测试集的标签数据
iris_test_target = iris_targets[randomArr[-10:]]
# 用Sklearn训练好的KNN模型 定义一个KNN分类器 在这里我们设置超参数K值为5
knn = KNeighborsClassifier(5)
# 调用knn对象的训练方法,主要接收两个参数,训练数据集和训练标签集
knn.fit(iris_train_datas, iris_train_target)
# 调用预测方法,主要接收一个参数 测试数据集
iris_test_predict = knn.predict(iris_test_datas)
# 计算各测试样本的预测概率值,这里我没有用概率值,但是在实际工作中可能会参考概率值进行最后的结果筛选,而不是直接使用给出的标签
#property = knn.predict_proba(iris_test_datas)
#print(property)
# 通过输入测试数据和测试标签,计算出模型准确率
score = knn.score(iris_test_datas,iris_test_target,sample_weight=None)
# 输出测试结果
print("该模型测试评估:", score)
# 输出模型评估标签和原始正确标签,方便比对
print("模型评估的标签:", iris_test_predict)
print("模型评估的标签:", iris_test_target)
我们可以看到该模型准确率有90%,在十条测试数据中,仅第二条数据将杂色鸢尾花评估成了维吉尼亚鸢尾花
感兴趣的小伙伴也可以实时自己去变更超参数K,看不同的超参数范围会给模型预测带来怎样的影响
# 用Sklearn训练好的KNN模型 定义一个KNN分类器 在这里我们设置超参数K值为5
knn = KNeighborsClassifier(5)