目录
机器学习中的KNN(K-NearestNeighbor)算法,是数据挖掘分类中最技术简单的方法之一。
基本原理
KNN算法的核心思想是,一个样本的类别为,其在特征空间中的K个最相邻的样本中的大多数属于的某一个类别。
如下图,为样本集中已知的三种类别的样本,现在需要对未知样本Xu进行预测,其在特征空间中最相邻的K个(此图中定义K为5)“邻居”中有大多数都属于红色(4/5),则Xu样本便也属于红色.
算法实现
数据集
使用鸢尾花数据集,包含三种类别的花品种,每种各50条数据,
单条样本记录结构为:【属性1,属性2,属性3,属性4,类别】
数据集可以去我的github下载:
距离计算方法
1. 闵可夫斯基距离(Minkowski distance ,lp距离)
的距离定义为:
2. 曼哈顿距离(Manhattan distance)
当距离的p=1时,就变成了曼哈顿距离
3. 欧式距离(Euclidean distance)
当当距离的p=2时,就变成了欧式距离
python代码
KNN算法实现的基本步骤如下:
1. 准备数据集,其中每个样本已被标注类别(label)
2. 计算某个未知类别的样本与整个数据集每个记录的的距离
3. 对所有距离进行排序,距离最近的K个“邻居”
4. 选择K个邻居中出现次数最多的类别作为未知样本的预测类别
"""KNN"""
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# 计算距离
class KNearestNeighbor(object):
def __init__(self):
pass
def loadData(self, path):
data = pd.read_csv(path, header=None)
# 特征类别及label
# data.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'species']
# 取前四列特征
X = data.iloc[:, 0:4].values
# 取最后一列label
y = data.iloc[:, 4].values
# 三种花分别由0 1 2表示
y[y == 'Iris-setosa'] = 0
y[y == 'Iris-versicolor'] = 1
y[y == 'Iris-virginica'] = 2
# 从数据集的对应位置取出三种花的对应数据
self.X_setosa, self.y_setosa = X[0:50], y[0:50]
self.X_versicolor, self.y_versicolor = X[50:100], y[50:100]
self.X_virginica, self.y_virginica = X[100:150], y[100:150]
# 训练集,占3/5
self.X_train = np.vstack([self.X_setosa[:30, :], self.X_versicolor[:30, :], self.X_virginica[:30, :]])
self.y_train = np.hstack([self.y_setosa[:30], self.y_versicolor[:30], self.y_virginica[:30]])
# 测试集,占2/5
self.X_test = np.vstack([self.X_setosa[30:50, :], self.X_versicolor[30:50, :], self.X_virginica[30:50, :]])
self.y_test = np.hstack([self.y_setosa[30:50], self.y_versicolor[30:50], self.y_virginica[30:50]])
def predict(self, X, k, method='M'):
num_test = X.shape[0]
if method == 'E':
# 计算欧氏距离
# (X - X_train)^2 = -2X*X_train + X_train^2+X^2
dist = -2 * np.dot(X, self.X_train.T) + np.sum(np.square(X), axis=1, keepdims=True) + np.sum(
np.square(self.X_train), axis=1)
distance = np.square(dist)
else:
# 计算曼哈顿距离
distance = []
for i in range(num_test):
distance.append(np.sum(np.abs(X[i, :] - self.X_train), axis=1))
y_pred = np.zeros(num_test)
for i in range(num_test):
# 按距离排序并选择最近的k个点的索引
dist_k_min = np.argsort(distance[i])[:k]
# 取出最近的k个点的label
y_kclose = self.y_train[dist_k_min]
# 找出k个标签中从属类别最多的作为预测类别
y_pred[i] = np.argmax(np.bincount(y_kclose.tolist()))
return y_pred
if __name__ == "__main__":
path = "Iris.data"
knn = KNearestNeighbor()
knn.loadData(path)
accuracy_E = []
accuracy_M = []
# 只用一个K值就不要for循环,直接给K赋个值就行
for k in range(1, 15):
#分两种距离计算方式进行比较,不想比较的直接注释掉其中一个,改一下后面的画图代码就行
# 欧拉距离
y_pred = knn.predict(X=knn.X_test, k=k, method='E')
accuracy_E.append(np.mean(y_pred == knn.y_test))
# 曼哈顿距离
y_pred = knn.predict(X=knn.X_test, k=k)
accuracy_M.append(np.mean(y_pred == knn.y_test))
# 绘制不同k值、不同距离计算方式准确率的折线图
plt.title('The accuracy of Euler distance and Manhattan distance in different K') # 折线图标题
plt.plot(range(1, 15), accuracy_E,range(1, 15), accuracy_M)
plt.legend(['Euler distance','Manhattan distance'])
plt.xlabel("K")
plt.ylabel("Accuracy")
plt.show()
运行结果
对于不用的数据集,k值的选择,距离计算方式的选择,都会对准确率有一定的影响
KNN算法优缺点
优点:思路简单,易于理解,易于实现,无需估计参数
- KNN有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数 。
- 另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点
KNN算法改进
- 寻求更接近于实际的距离函数以取代标准的欧氏距离,典型的工作包括 WAKNN、VDM ;
- 搜索更加合理的K值以取代指定大小的K值典型的工作包括SNNB、 DKNAW ;
- 运用更加精确的概率估测方法去取代简单的投票机制,典型的工作包括 KNNDW、LWNB、 ICLNB ;
- 建立高效的索引,以提高KNN算法的运行效率,代表性的研究工作包括 KDTree、 NBTree。
参考资料
LP距离、欧式距离、曼哈顿距离、切比雪夫距离_齐在的专栏-CSDN博客_lp距离
《西瓜书》——周志华
《数据挖掘导论》——朱明