从零开始学数据挖掘1-KNN算法

1,091 阅读8分钟

KNN算法简介

KNN 即 最近邻算法(K-NearestNeighbor),这个算法的核心思想就是物以类聚,近朱者赤近墨者黑,

图片.png

直接计算K个距离未知点的样本距离,距离最近的样本点取最多的一个类别作为新数据的类别

例如图中,若我们设立K值为1,那么他就会被归纳到Class2中,若k值为5,那么Class1的数量要多余class2了,那么它就会被归纳到class1中,K值决定了距离辐射的范围从而影响着分类结果的准确性

KNN优缺点

KNN优点

  • KNN是简单易实现的,KNN算法没有建立任何模型,而是把过往历史数据当作模型本身,将新数据跟历史每一条数据进行对比,不需要任何的训练,得到一条新的未知数据就直接可以进行预测
  • KNN对于边界不规则的数据效果好,相较于线性算法用一条直线进行区分,遇到图上这种边界不规则的数据很难做到有效区分

KNN缺点

  • 它只适合小数据集,因为他实现简单每次进行新数据的预测只需要比对所有的数据集,所以当数据集非常大时,预测就会消耗非常长的时间,占用非常大的的数据集存储空间
  • 数据不平衡的预测效果不好,例如当某种数据样本数量非常多,那么它的竞争优势会远大于其他数据,导致KNN算法失效
  • 数据必须标准化,因为使用距离来计算,如果数据字段的量度不同,数值较大的字段影响就会变大,所以需要对数据进行标准化,比如都转化为0-1的区间内
  • 不适合太多特征维度的数据,如果数据维度太多,那么数据样本在每个维度上的分布就会很少,例如我们有3个样本,每个样本只有一个维度,这比有1个样本且样本有三个维度,前者特征要明显很多。

K值的选取会影响到模型的效果

图片.png

在图中我们可以看到左侧是富人区,右侧是普通民众,普通民众的数量是会远大于富人的,若我们设置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 (山鸢尾花)

图片.png

那我们的思路就是,通过计算一个新的鸢尾花每个特征 距离 各个样本鸢尾花特征的差值,累计起来,距离最近的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)

我们运行一下代码,看一下结果

图片.png

可以看到十条测试数据,经过我们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)

图片.png

我们可以看到该模型准确率有90%,在十条测试数据中,仅第二条数据将杂色鸢尾花评估成了维吉尼亚鸢尾花

感兴趣的小伙伴也可以实时自己去变更超参数K,看不同的超参数范围会给模型预测带来怎样的影响

# 用Sklearn训练好的KNN模型 定义一个KNN分类器 在这里我们设置超参数K值为5
knn = KNeighborsClassifier(5)