KNN算法既可以解决分类问题,也可以解决预测问题。
对于离散型因变量,从k个最近的已知类别样本中挑选出频率最高的类别用于未知样本的判断;对于连续型因变量,将k个最近的已知样本均值用作未知样本的预测。
k值过小,模型过拟合,例如k=1,未知样本的类别将由最近的1个已知样本点来决定,对于训练数据来说,训练误差几乎为0,对于测试数据来说,训练误差可能会很大,因为距离最近的1个已知样本点可以是异常观测值,也可以是正常观测值。
k值过大,模型欠拟合,例如k=N,未知样本的类别将由所有已知样本中频数最高的类别决定,不管是训练集还是测试集被判为一种类别,易欠拟合。
一般利用多重交叉验证得到平均误差最小的k值。还有一种方法是设置k近邻样本的投票权重,对已知样本距离较远的设置权重低一些,较近的设置权重高一些,通常将权重设置为距离的倒数。
点与点之间的距离即相似性,一般用欧氏距离,即L2范数
或者曼哈顿距离,即L1范数
或者余弦相似度cosα
或者杰卡德相似系数,即J=|A∩B|/|A∪B|
在使用距离方法来度量相似性时,要使所有变量数值化(通过哑变量或者重编码为0,1,2),而且采用标准化方法进行归一化,防止数值变量的量纲影响
近邻搜寻方法包括:暴力搜寻法(全表扫描),kd树(k为训练集中包含的变量个数,而非KNN中的k个邻近样本,先用所有已知类别的样本点构造一棵树,再将未知类别应用在树上),球树搜寻(将kd树中的超矩形体换成了超球体)。
优点:
精度高,对异常值不敏感,无数据输入假定;
KNN 是一种在线技术,新数据可以直接加入数据集而不必进行重新训练;
KNN 理论简单,容易实现。
缺点:
对于样本容量大的数据集计算量比较大,即计算复杂度高;
必须保存全部数据集,即空间复杂度高;
KNN 每一次分类都会重新进行一次全局运算;
样本不平衡时,预测偏差比较大。如:某一类的样本比较少,而其它类样本比较多;
K 值大小的选择;
KNN 无法给出基础结构信息,无法知晓平均实例样本与典型实例样本具有什么特征,即无法给出数据的内在含义。
应用领域:
文本分类;模式识别;聚类分析;多分类领域。
# 导入第三方包
import pandas as pd
# 导入数据
Knowledge = pd.read_excel(r'F:\Knowledge.xlsx')
# 返回前5行数据
Knowledge.head()
行表示每一个被观测的学生,
STG:在目标学科上的学习时长,
SCG:重复次数
STR:相关科目的学习时长
LPR:相关科目的考试成绩
PEG:目标科目的考试成绩
(以上指标均已标准化)
UNG:对知识的掌握程度高低
# 构造训练集和测试集
# 导入第三方模块
from sklearn import model_selection
# 将数据集拆分为训练集和测试集
predictors = Knowledge.columns[:-1]
X_train, X_test, y_train, y_test = model_selection.train_test_split(Knowledge[predictors], Knowledge.UNS,
test_size = 0.25, random_state = 1234)
利用多重交叉验证获取符合数据的理想k值
Knowledge.shape[0]
import numpy as np
np.log2(Knowledge.shape[0])
np.ceil(np.log2(Knowledge.shape[0]))
# 导入第三方模块
from sklearn import neighbors
import matplotlib.pyplot as plt
# 设置待测试的不同k值
K = np.arange(1,np.ceil(np.log2(Knowledge.shape[0])))
K
# 构建空的列表,用于存储平均准确率
accuracy = []
for k in K:
# 使用10重交叉验证的方法,比对每一个k值下KNN模型的预测准确率
cv_result = model_selection.cross_val_score(neighbors.KNeighborsClassifier(n_neighbors = int(k), weights = 'distance'),
X_train, y_train, cv = 10, scoring='accuracy')
accuracy.append(cv_result.mean())
accuracy
# 从k个平均准确率中挑选出最大值所对应的下标
arg_max = np.array(accuracy).argmax()
arg_max
# 中文和负号的正常显示
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
# 绘制不同K值与平均预测准确率之间的折线图
plt.plot(K, accuracy)
# 添加点图
plt.scatter(K, accuracy)
# 添加文字说明
plt.text(K[arg_max], accuracy[arg_max], '最佳k值为%s' %int(K[arg_max]))
# 显示图形
plt.show()
经过10重交叉验证,最佳的近邻个数为6
# 导入第三方模块
from sklearn import metrics
# 重新构建模型,并将最佳的近邻个数设置为6
knn_class = neighbors.KNeighborsClassifier(n_neighbors = 6, weights = 'distance')
# 模型拟合
knn_class.fit(X_train, y_train)
# 模型在测试数据集上的预测
predict = knn_class.predict(X_test)
# 构建混淆矩阵
cm = pd.crosstab(predict,y_test)
cm
weights=uniform,表示投票权重一样
=distance,表示投票权重与距离成反比
从主对角线看,绝大多数样本被正确分类
通过热力图可视化混淆矩阵
# 导入第三方模块
import seaborn as sns
# 将混淆矩阵构造成数据框,并加上字段名和行名称,用于行或列的含义说明
cm = pd.DataFrame(cm)
# 绘制热力图
sns.heatmap(cm, annot = True,cmap = 'GnBu')
# 添加x轴和y轴的标签
plt.xlabel(' Real Lable')
plt.ylabel(' Predict Lable')
# 图形显示
plt.show()
行代表真实地,列代表预测的,主对角线上的颜色比较深,说明绝大多数样本是被正确分类的。
下面得到模型在测试集上的预测准确率:
# 模型整体的预测准确率
metrics.scorer.accuracy_score(y_test, predict)
整体预测准确率为91.09%,要得到每个类别的准确率:
# 分类模型的评估报告
print(metrics.classification_report(y_test, predict))
第一列为预测精度,即”预测正确的类别个数/该类别预测的所有个数"
第二列为预测覆盖率,即”预测正确的类别个数/该类别实际的所有个数"
第三列为前两列的加权结果
第四列为类别实际的样本个数
对于预测问题的解决同决策树中一样,用MSE衡量
网友评论