数据挖掘十大算法之 k-NN

k k k-NN ( k k k-nearest neighbor) 由 CoverHart 于 1968 年提出,属于机器学习算法中的监督学习算法,可以用来解决分类和回归问题。

k k k-NN 的工作原理

为了对 k k k-NN 算法有个直观的认识,我们看个例子:
在这里插入图片描述

有两类物体 A 和 B,它们在坐标轴中的分布如上图所示。现在来了一个未知类别的物体,由图中的正方形表示,我们该把它归为哪一类呢? k k k-NN 算法的工作原理是看离待分类物体最近的 k k k 个物体的类别,这 k k k 个物体的大多数属于那个类别,待分类物体也就属于那个类别。例如:当 k k k = 3 时,离待分类物体最近的 3 个物体中,有 1 个 A 类物体,2 个 B 类物体,所以待分类物体属于 B 类;当 k k k = 9 时,离待分类物体最近的 9 个物体中,有 5 个 A 类物体,4 个 B 类物体,所以待分类物体属于 A 类。由上面的例子可知,* k k k-NN 的工作流程分为三步:

  1. 计算待分类物体和其他物体之间的距离。
  2. 选取距离待分类物体最近的 k k k 个物体。
  3. k k k 个物体的大多数属于那个分类,待分类物体也就属于那个分类。

距离的计算

特征空间中两个实例点之间的距离反映了两个实例点的相似程度。距离越大,相似度越小;距离越小,相似度越大。 k k k 近邻模型的特征空间一般是 n n n 维实数向量空间 R n R^n Rn。使用的距离是欧式距离,但也可以是其他距离,如更一般的 L p L_p Lp L p L_p Lp distance) 距离或闵可夫斯基距离(Minkowski distance)。
设特征空间 χ \chi χ n n n 维实数向量空间 R n R^n Rn x i x_i xi x j x_j xj ∈ \in χ \chi χ x i x_i xi = ( x i ( 1 ) x_i^{(1)} xi(1), x i ( 2 ) x_i^{(2)} xi(2) ,…, x i ( n ) x_i^{(n)} xi(n)) T ^T T x j x_j xj = ( x j ( 1 ) x_j^{(1)} xj(1), x j ( 2 ) x_j^{(2)} xj(2) ,…, x j ( n ) x_j^{(n)} xj(n)) T ^T T x i x_i xi x j x_j xj L p L_p Lp 距离定义为

L p ( x i , y i ) L_p(x_i, y_i) Lp(xi,yi) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p \Bigg(\displaystyle \sum^n_{l=1}|x_i^{(l)}-x_j^{(l)}|^p\Bigg)^\frac{1}{p} (l=1nxi(l)xj(l)p)p1 p ≥ 1 p \geq 1 p1

p = 2 p = 2 p=2时,称为欧式距离,即
L 2 ( x i , y i ) L_2(x_i, y_i) L2(xi,yi) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ 2 ) 1 2 \Bigg(\displaystyle \sum^n_{l=1}|x_i^{(l)}-x_j^{(l)}|^2\Bigg)^\frac{1}{2} (l=1nxi(l)xj(l)2)21
p = 1 p = 1 p=1 时,称为曼哈顿距离,即
L 1 ( x i , y i ) L_1(x_i, y_i) L1(xi,yi) = ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ \displaystyle \sum^n_{l=1}|x_i^{(l)}-x_j^{(l)}| l=1nxi(l)xj(l)
p = ∞ p = \infty p= 时,它是各个坐标距离的最大值,即
L ∞ ( x i , y j ) = m a x l ∣ x i ( l ) − x j ( l ) ∣ L_\infty(x_i, y_j) = \mathop{max}\limits_{l}|x_i^{(l)}-x_j^{(l)}| L(xi,yj)=lmaxxi(l)xj(l)
称为切比雪夫距离。

k k k 值的选择

从上面的例子我们看到, k k k 值的选择会对结果产生重大的影响。同一个物体,如果 k k k 值选择的不同,结果可能完全不同。另外, k k k 值的选择也对模型的预测效果产生较大影响。如果 k k k 值选择的较小,只有较小邻域内的训练实例才会对预测结果起作用,这时整体模型变得复杂,容易发生过拟合;如果 k k k 值选择的较大,意味着距离输入实例较远的训练实例也会对预测结果起作用,这时整体模型变得简单,容易发生欠拟合。在应用中,一般采用交叉验证法来选取最优的 k k k 值。

决策规则

k k k 近邻法中往往采用多数表决的决策规则,也就是输入实例的 k k k 个近邻的多数类决定输入实例的类。

k d kd kd

在实现 k k k 近邻法时,为了找出距离输入实例最近的 k k k 个训练实例,最简单的方法便是线性扫描,这时要计算输入实例和每个训练实例的距离。当特征空间的维数以及训练集较大时,计算非常耗时。为了提高效率,减少不必要的计算,可以使用 k d kd kd 树来存储训练数据,并通过搜索 k d kd kd 树来减少计算量。

两个例子

鸢尾花的分类

鸢尾花的分类问题属于监督学习中比较经典的问题。鸢尾花有三个类别,分别为山鸢尾(Iris Setosa)、杂色鸢尾(Iris Versicolour)和维吉尼亚鸢尾(Iris Virginica)。每个类别的鸢尾花都有四个属性,分别是花萼长度(SepalLength)、花萼宽度(SepalWidth)、花瓣长度(PetalLength)以及花瓣宽度(PetalWidth)。鸢尾花的分类问题就是通过花萼长度、花萼宽度、花瓣长度以及花瓣宽度的值来对鸢尾花进行分类。下面我们通过 Scikit-learn 中的 k k k-NN 算法对鸢尾花进行分类。

  1. 导入库
import pandas as pd
from sklearn import metrics
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
  1. 读取数据
iris_data = pd.read_csv('Iris.csv')
  1. 将数据集分成训练数据集和测试数据集
y = iris_data['Species']
x = iris_data[['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']]
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.3, random_state=33)
  1. 使用训练数据对模型进行训练,并使用得到的模型对测试数据进行测试。
knn = KNeighborsClassifier()
knn.fit(train_x, train_y)
predict_y = knn.predict(test_x)
  1. 输出测试结果
print("KNN准确率: %.4lf" % accuracy_score(test_y, predict_y))
  1. output
KNN准确率: 0.9556

糖尿病的预测

糖尿病的预测是根据患者的一些信息来预测患者是否有糖尿病。下面我们通过 Scikit-learn 中的 k k k-NN 算法对患者是否患有糖尿病进行预测。

  1. 导入库
import pandas as pd
from sklearn import metrics
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
  1. 读取数据
diabetes_data = pd.read_csv('diabetes.csv')
  1. 对数据进行清洗,对于某列数据中的0值,使用这一列值的平均值进行填充。
diabetes_data.replace({
    'Glucose': 0,
    'BloodPressure': 0,
    'SkinThickness': 0,
    'BMI': 0,
    'Insulin': 0
}, np.NaN, inplace=True)

glucose_mean = diabetes_data['Glucose'].mean()
blood_pressure_mean = diabetes_data['BloodPressure'].mean()
skin_thickness_mean = diabetes_data['SkinThickness'].mean()
bmi_mean = diabetes_data['BMI'].mean()
insulin_mean = diabetes_data['Insulin'].mean()

diabetes_data['Glucose'].replace(np.NaN, glucose_mean, inplace=True)
diabetes_data['BloodPressure'].replace(np.NaN, blood_pressure_mean, inplace=True)
diabetes_data['SkinThickness'].replace(np.NaN, skin_thickness_mean, inplace=True)
diabetes_data['BMI'].replace(np.NaN, bmi_mean, inplace=True)
diabetes_data['Insulin'].replace(np.NaN, insulin_mean, inplace=True)
  1. 将数据集分成训练数据集和测试数据集。
y = diabetes_data['Outcome']
x = diabetes_data[['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age']]
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2, random_state=0)
  1. 对数据进行规范化。
sc_x = StandardScaler()
train_x = sc_x.fit_transform(train_x)
test_x = sc_x.fit_transform(test_x)
  1. 使用训练数据对模型进行训练,并使用得到的模型对测试数据进行测试。
knn = KNeighborsClassifier(n_neighbors=11, metric='euclidean', p=2, weights='uniform', algorithm='auto', leaf_size=30)
knn.fit(train_x, train_y)
predict_y = knn.predict(test_x)
  1. 输出测试结果
print("KNN准确率: %.4lf" % accuracy_score(test_y, predict_y))
  1. output
KNN准确率: 0.8052

总结

本文介绍了 k k k -NN 的工作原理以及在实际问题中的应用。

数据下载

鸢尾花:https://www.kaggle.com/uciml/iris
糖尿病:https://www.kaggle.com/uciml/pima-indians-diabetes-database

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值