机器学习实战:Python基于K近邻KNN进行分类预测(四)

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近邻的应用

应用相对广泛:

  1. 图像识别:KNN算法可以用于图像识别领域,例如面部识别、手写数字识别等。

  2. 推荐系统:KNN算法可以用于基于用户行为和喜好的推荐系统,例如在线商店和社交媒体平台。

  3. 金融领域:KNN算法可以用于信用评分和风险管理,例如预测客户违约概率和市场波动。

  4. 医疗诊断:KNN算法可以用于医疗诊断,例如预测病人患某种疾病的可能性。

  5. 文本分类:KNN算法可以用于文本分类领域,例如垃圾邮件过滤、情感分析等。

  6. 时间序列预测:KNN算法可以用于时间序列预测,例如股票价格预测和气象预测。

  7. 其他领域:KNN算法还可以用于语音识别、游戏AI、生物信息学、物流和交通等领域。

提一下,单是生信领域使用的场景其实还是挺多的

  1. 基因分类:KNN可以用于对基因进行分类,例如在癌症研究中,可以使用KNN分类器来识别患有癌症的患者和健康人之间的基因表达模式的差异。

  2. 蛋白质结构预测:KNN可以用于预测蛋白质的三维结构,例如使用KNN回归来预测残基-残基相互作用的能量。

  3. 突变预测:KNN可以用于预测基因突变的影响,例如在基因组学中,可以使用KNN分类器来预测某种突变对蛋白质功能的影响。

  4. 数据集分类:KNN也可以用于分类未知数据集中的样本,例如在微生物组学中,可以使用KNN分类器来对未知的微生物样本进行分类。

  5. 基因表达数据分析: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都可以考虑,可以结合accstd来选。

6 讨论

KNN是一种惰性学习算法,可用于回归或分类。主要就是关注两个点:k值的选择距离的计算

一般情况下这个k和数据有很大关系,都是交叉验证进行选择,但是建议使用交叉验证的时候,k∈[2,20],使用交叉验证得到一个很好的k值。

样本之间的距离的计算,我们一般使用对于一般使用Lp距离进行计算。当p=1时候,称为曼哈顿距离(Manhattan distance),当p=2时候,称为欧氏距离(Euclidean distance),当p=∞时候,称为极大距离(infty distance), 表示各个坐标的距离最大值,另外也包含夹角余弦等方法。一般采用欧式距离较多,但是文本分类则倾向于使用余弦来计算相似度。

猜你喜欢

转载自blog.csdn.net/weixin_48093827/article/details/130060221