机器学习训练营(四):K近邻(k-nearest neighbors)算法

K近邻(k-nearest neighbors)理论

1.K近邻算法原理

K近邻算法的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集种每个数都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前 k k k个最相似的数据,这就是 k k k-近邻算法中 k k k的出处,通常 k k k是不大于20的整数。最后,选择 k k k个最相似数据中出现次数最多的分类,作为新数据的分类。

2.K近邻建立的过程
Step1 给定测试样本,计算它与训练集中的每一个样本的距离。
Step2 找出距离近期的K个训练样本。作为测试样本的近邻。
Step3 依据这K个近邻归属的类别来确定样本的类别。
3.K近邻类别的判定
①投票决定,少数服从多数。取类别最多的为测试样本类别。
②加权投票法,依据计算得出距离的远近,对近邻的投票进行加权,距离越近则权重越大,设定权重为距离平方的倒数。
4.K近邻算法的优缺点

优点: 精度高、对异常值不敏感、无数据输入假定。
缺点:
1、 k k k-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据, k k k-近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。
2、由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。
3、无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具有什么特征。

KNN分类在二位数据集上的实现

Step1: 库函数导入
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.neighbors import KNeighborsClassifier
from sklearn import datasets
Step2: 数据导入
iris = datasets.load_iris()
X = iris.data[:, :2]# 使用莺尾花数据集的前两维数据,便于数据可视化
y = iris.target
Step3: 模型训练&可视化
k_list = [1, 3, 5, 8, 10, 15]
h = .02
# 创建不同颜色的画布
cmap_light = ListedColormap(['orange', 'cyan', 'cornflowerblue'])#橘色,青色,矢车菊的蓝色
cmap_bold = ListedColormap(['darkorange', 'c', 'darkblue'])#深橙色,深蓝色

plt.figure(figsize=(15,14))
# 根据不同的k值进行可视化
for ind,k in enumerate(k_list):
    clf = KNeighborsClassifier(k)#根据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()])#ravel()数据扁平化,做预测
    # 根据边界填充颜色
    Z = Z.reshape(xx.shape)
 
    plt.subplot(321+ind)  
    plt.pcolormesh(xx, yy, Z, cmap=cmap_light)#plt.pcolormesh()会根据Z的结果自动在cmap里选择颜色绘制分类背景图
    # 数据点可视化到画布
    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时,分类边界较明显,对局部数据就不太敏感。

KNN分类在莺尾花数据集上的实现

Step1: 库函数导入
import numpy as np
# 加载莺尾花数据集
from sklearn import datasets
# 导入KNN分类器
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
Step2: 数据导入&分析
# 导入莺尾花数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 得到训练集合和验证集合, 8: 2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
Step3: 模型训练
# 训练模型
clf = KNeighborsClassifier(n_neighbors=5, p=2, metric="minkowski")#设置参数k(n_neighbors)=5, 使用欧式距离(metric=minkowski & p=2)
clf.fit(X_train, y_train)
Step4:模型预测&可视化
# 预测
X_pred = clf.predict(X_test)
acc = sum(X_pred == y_test) / float(X_pred.shape[0])
print("预测的准确率ACC: %.3f" % acc)

在这里插入图片描述

KNN回归在模拟数据集上的实践

Step1: 库函数导入
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
Step2: 数据导入&分析
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))
Step3: 模型训练&预测可视化
# Fit regression model
# 设置多个k近邻进行比较
n_neighbors = [1, 3, 5, 8, 10, 40]
# 设置图片大小
plt.figure(figsize=(10,20))
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(6, 1, 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个样本,此时是所有样本的平均值,此时所有预测值都是均值,很容易发生欠拟合。

一般情况下,使用knn的时候,根据数据规模我们会从[3, 20]之间进行尝试,选择最好的k,例如上图中的[3, 10]相对1和40都是还不错的选择。

kNN数据预处理和kNN分类pipeline在马绞痛数据上的实践

Step1: 库函数导入
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
Step2:kNNImputer空值填充
# load dataset
input_file = './horse-colic.csv'
df_data = pd.read_csv(input_file, header=None, na_values='?')#将?变成空值,300行28列

# 得到训练数据和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()))

输出为:
在这里插入图片描述
由此可见空值被填充了。

Step3: 基于pipeline模型训练&可视化
results = list()
strategies = [str(i) for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 18, 20, 21]]#kNN填充的类别数
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)))

输出为:
在这里插入图片描述

绘制结果图:

# plot model performance for comparison
plt.boxplot(results, labels=strategies, showmeans=True)
plt.show()

在这里插入图片描述

results1=[np.mean(i) for i in results]
plt.plot(strategies,results1)

在这里插入图片描述
我们的实验是每个k值下,随机切分20次数据, 从上述的图片中, 根据k值的增加,我们的测试准确率会有先下降再上升再下降的过程,[4, 6]之间是一个很好的取值。

参考:
《机器学习实战》

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值