[toc]
1 前言
1.1 K近邻的介绍
K近邻(K-Nearest Neighbor, KNN)是一种基本的分类与回归算法。其基本思想是将新的数据样本与已知类别的数据样本进行比较,根据K个最相似的已知样本的类别进行预测。
具体来说,KNN算法通过计算待分类样本与已知样本之间的距离(欧式距离
、曼哈顿距离
等),选取距离待分类样本最近的K个已知样本,再通过对这K个已知样本的类别进行投票、取平均等方式确定待分类样本的类别。KNN算法中,K的取值是一个重要的超参数,不同的取值可能会影响算法的性能。
优点:
- 简单易懂:KNN算法的原理简单,易于理解和实现。
- 无需训练:KNN算法是一种基于实例的学习方法,它不需要显式地训练模型,而是直接使用训练集中的实例进行预测。
- 适用于多分类问题:KNN算法可以应用于多分类问题,不需要额外的处理。
- 对异常值不敏感:KNN算法不受异常值的影响,因为它是基于相邻的投票决定结果,不会受到单个数据点的影响。
- 模型可解释性高:KNN算法的结果可以被解释为离查询点最近的邻居的平均值或众数。
缺点:
- 需要大量内存:KNN算法需要存储所有的训练样本,因此随着样本数量的增加,内存开销也会相应增加。
- 计算量大:对于每个测试样本,KNN算法需要计算它与所有训练样本之间的距离,因此随着样本数量的增加,计算量也会相应增加。
- 效果受参数K的影响:KNN算法中的K值是一个重要参数,需要根据实际问题进行选择,K值过大或过小都会影响预测结果。
- 受特征空间的影响:KNN算法的效果受特征空间的影响,如果特征空间维度过高,会影响算法的准确性。
- 对于不平衡数据集的处理效果较差:如果训练集中的某个类别的数据样本比其他类别的数据样本数量少,那么KNN算法会偏向于选择数量较多的类别进行预测,从而影响预测结果的准确性。
1.2 K近邻的应用
应用相对广泛:
- 图像识别:KNN算法可以用于图像识别领域,例如面部识别、手写数字识别等。
- 推荐系统:KNN算法可以用于基于用户行为和喜好的推荐系统,例如在线商店和社交媒体平台。
- 金融领域:KNN算法可以用于信用评分和风险管理,例如预测客户违约概率和市场波动。
- 医疗诊断:KNN算法可以用于医疗诊断,例如预测病人患某种疾病的可能性。
- 文本分类:KNN算法可以用于文本分类领域,例如垃圾邮件过滤、情感分析等。
- 时间序列预测:KNN算法可以用于时间序列预测,例如股票价格预测和气象预测。
- 其他领域:KNN算法还可以用于语音识别、游戏AI、生物信息学、物流和交通等领域。
提一下,单是生信领域使用的场景其实还是挺多的
- 基因分类:KNN可以用于对基因进行分类,例如在癌症研究中,可以使用KNN分类器来识别患有癌症的患者和健康人之间的基因表达模式的差异。
- 蛋白质结构预测:KNN可以用于预测蛋白质的三维结构,例如使用KNN回归来预测残基-残基相互作用的能量。
- 突变预测:KNN可以用于预测基因突变的影响,例如在基因组学中,可以使用KNN分类器来预测某种突变对蛋白质功能的影响。
- 数据集分类:KNN也可以用于分类未知数据集中的样本,例如在微生物组学中,可以使用KNN分类器来对未知的微生物样本进行分类。
- 基因表达数据分析:KNN可以用于分析基因表达数据,例如在单细胞RNA测序中,可以使用KNN来对细胞进行分类和聚类。
2 二维数据集演示
2.1 导入函数
import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap from sklearn.neighbors import KNeighborsClassifier from sklearn import datasets # 作图 import matplotlib.pyplot as plt from matplotlib.colors import ListedColormap import numpy as np import seaborn as sns
2.2 导入数据
# 莺尾花数据集的前两维数据 iris = datasets.load_iris() X = iris.data[:, :2] y = iris.target
2.3 训练模型及可视化
k_list = [1, 3, 5, 8, 10, 15] h = .02 # 创建不同颜色的画布 cmap_light = ListedColormap(['#a6cee3', '#1f78b4', '#b2df8a']) cmap_bold = ListedColormap(['#e41a1c', '#377eb8', '#4daf4a']) plt.figure(figsize=(15,14)) # 根据不同的k值进行可视化 for ind,k in enumerate(k_list): clf = KNeighborsClassifier(k) clf.fit(X, y) # 画出决策边界 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) # 根据边界填充颜色 Z = Z.reshape(xx.shape) plt.subplot(321+ind) plt.pcolormesh(xx, yy, Z, cmap=cmap_light) # 数据点可视化到画布 plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold, edgecolor='k', s=20) plt.xlim(xx.min(), xx.max()) plt.ylim(yy.min(), yy.max()) plt.title("3-Class classification (k = %i)"% k) plt.show()
当k=1
的时候,在分界点位置的数据很容易受到局部的影响,图中会有蓝绿相间,主要是数据太局部敏感。当k=15
的时候,不同的数据基本根据颜色分开,当时进行预测的时候,会直接落到对应的区域,模型相对为“粗糙”。
3 莺尾花数据集全数据演示
3.1 导入函数
### iris数据集 import numpy as np # 加载莺尾花数据集 from sklearn import datasets # 导入KNN分类器 from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import train_test_split
3.2 导入数据
iris = datasets.load_iris() X = iris.data y = iris.target # 7/3分 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
3.3 训练模型及预测
# 训练模型 clf = KNeighborsClassifier(n_neighbors=5, p=2, metric="minkowski") clf.fit(X_train, y_train) # 预测 X_pred = clf.predict(X_test) acc = sum(X_pred == y_test) / X_pred.shape[0] print("预测的准确率ACC: %.3f" % acc)
很可,能达到0.978。训练和预测的过程大家可以自行去算,用knn.predict(x)
算出每个样本的欧式距离,给每个样本编一个独立的id、排序、打分
4 模拟数据集演示
4.1 导入函数
import numpy as np import matplotlib.pyplot as plt from sklearn.neighbors import KNeighborsRegressor
4.2 模拟数据集
np.random.seed(0) # 随机生成40个(0, 1)之前的数,乘以5,再进行升序 X = np.sort(5 * np.random.rand(40, 1), axis=0) # 创建[0, 5]之间的500个数的等差数列, 作为测试数据 T = np.linspace(0, 5, 500)[:, np.newaxis] # 使用sin函数得到y值,并拉伸到一维 y = np.sin(X).ravel() # Add noise to targets[y值增加噪声] y[::5] += 1 * (0.5 - np.random.rand(8))
4.3 建模比较
# 设置多个k近邻进行比较 n_neighbors = [1, 3, 5, 8, 10, 40] # 设置图片大小 plt.figure(figsize=(20,14)) # 输出设为两列三行 rows = 3 cols = 2 for i, k in enumerate(n_neighbors): # 默认使用加权平均进行计算predictor clf = KNeighborsRegressor(n_neighbors=k, p=2, metric="minkowski") # 训练 clf.fit(X, y) # 预测 y_ = clf.predict(T) plt.subplot(rows, cols, i + 1) plt.scatter(X, y, color='red', label='data') plt.plot(T, y_, color='navy', label='prediction') plt.axis('tight') plt.legend() plt.title("KNeighborsRegressor (k = %i)" % (k)) plt.tight_layout() plt.show()
很明显,当k=1
时,预测的结果只和最近的一个训练样本相关,从预测曲线中可以看出当k很小时候很容易发生过拟合。当k=40
时,预测的结果和最近的40个样本相关,因为我们只有40个样本,此时是所有样本的平均值,此时所有预测值都是均值,很容易发生欠拟合。
因此根据结果,3,5,8,10都是可以考虑的。
5 马绞痛数据+pipeline演示
5.1 下载数据集
这里包括了数据集和表头信息,确保自己已经安装了wget
# 下载需要用到的数据集 !wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.csv # 下载数据集介绍 !wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.names
5.2 导入函数
## 导入函数 import numpy as np import pandas as pd # kNN分类器 from sklearn.neighbors import KNeighborsClassifier # kNN数据空值填充 from sklearn.impute import KNNImputer # 计算带有空值的欧式距离 from sklearn.metrics.pairwise import nan_euclidean_distances # 交叉验证 from sklearn.model_selection import cross_val_score # KFlod的函数 from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.pipeline import Pipeline import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split
5.3 填充空值
这个数据集里面有很多?
,因此还是用欧式距离计算后填充
## 空值填充 X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]] imputer = KNNImputer(n_neighbors=2, metric='nan_euclidean') imputer.fit_transform(X)
## 带空值的欧式距离计算公式 nan_euclidean_distances([[np.nan, 6, 5], [3, 4, 3]], [[3, 4, 3], [1, 2, np.nan], [8, 8, 7]])
# load dataset, 将?变成空值 input_file = './horse-colic.csv' df_data = pd.read_csv(input_file, header=None, na_values='?') # 得到训练数据和label, 第23列表示是否发生病变, 1: 表示Yes; 2: 表示No. data = df_data.values ix = [i for i in range(data.shape[1]) if i != 23] X, y = data[:, ix], data[:, 23] # 查看所有特征的缺失值个数和缺失率 for i in range(df_data.shape[1]): n_miss = df_data[[i]].isnull().sum() perc = n_miss / df_data.shape[0] * 100 if n_miss.values[0] > 0: print('>Feat: %d, Missing: %d, Missing ratio: (%.2f%%)' % (i, n_miss, perc)) # 查看总的空值个数 print('KNNImputer before Missing: %d' % sum(np.isnan(X).flatten())) # 定义 knnimputer imputer = KNNImputer() # 填充数据集中的空值 imputer.fit(X) # 转换数据集 Xtrans = imputer.transform(X) # 打印转化后的数据集的空值 print('KNNImputer after Missing: %d' % sum(np.isnan(Xtrans).flatten()))
5.4 建模计算
## 构建pipeline results = list() strategies = [str(i) for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 18, 20, 21]] for s in strategies: # create the modeling pipeline pipe = Pipeline(steps=[('imputer', KNNImputer(n_neighbors=int(s))), ('model', KNeighborsClassifier())]) # 数据多次随机划分取平均得分 scores = [] for k in range(20): # 得到训练集合和验证集合, 8: 2 X_train, X_test, y_train, y_test = train_test_split(Xtrans, y, test_size=0.2) pipe.fit(X_train, y_train) # 验证model score = pipe.score(X_test, y_test) scores.append(score) # 保存results results.append(np.array(scores)) print('>k: %s, Acc Mean: %.3f, Std: %.3f' % (s, np.mean(scores), np.std(scores))) # print(results) # plot model performance for comparison plt.boxplot(results, labels=strategies, showmeans=True) plt.show()
可以观察到结果是随着k值增大先上升后下降再上升的一个波浪趋势,在模拟数据集的结果提到,k取太小太大会过拟合和欠拟合,因此在第一次下降谷底时可简单认为不再发生过拟合,因而取此前的结果。这里的话3、4、5都可以考虑,可以结合acc
和std
来选。
6 讨论
KNN是一种惰性学习算法,可用于回归或分类。主要就是关注两个点:k值的选择
和距离的计算
一般情况下这个k和数据有很大关系,都是交叉验证进行选择,但是建议使用交叉验证的时候,k∈[2,20],使用交叉验证得到一个很好的k值。
样本之间的距离的计算,我们一般使用对于一般使用Lp距离进行计算。当p=1时候,称为曼哈顿距离
(Manhattan distance),当p=2时候,称为欧氏距离
(Euclidean distance),当p=∞时候,称为极大距离
(infty distance), 表示各个坐标的距离最大值,另外也包含夹角余弦等方法。一般采用欧式距离较多,但是文本分类则倾向于使用余弦来计算相似度。