【机器学习算法 - 1】KNN

目录

基本原理

算法实现

数据集

距离计算方法

python实现

运行结果

KNN算法优缺点

KNN算法改进


机器学习中的KNN(K-NearestNeighbor)算法,是数据挖掘分类中最技术简单的方法之一。

基本原理

KNN算法的核心思想是,一个样本的类别为,其在特征空间中的K个最相邻的样本中的大多数属于的某一个类别。

如下图,\omega _{1}\omega _{2}\omega _{3}为样本集中已知的三种类别的样本,现在需要对未知样本Xu进行预测,其在特征空间中最相邻的K个(此图中定义K为5)“邻居”中有大多数都属于红色(4/5),则Xu样本便也属于红色.

61195cab5e311832d55918671dd8f7ca.png

算法实现

数据集

使用鸢尾花数据集,包含三种类别的花品种,每种各50条数据,

单条样本记录结构为:【属性1,属性2,属性3,属性4,类别】

数据集可以去我的github下载:

Squirtle339/KNN: 基于鸢尾花数据集(iris)实现KNN,并比较在不同距离计算方式和不同K值下的预测准确率 (github.com)https://github.com/Squirtle339/KNN/tree/main

距离计算方法

1. 闵可夫斯基距离(Minkowski distance ,lp距离)

x_{i},x_{j}L_{p}距离定义为:

{L_p}({x_i},{x_j}) = {(\sum\limits_{l = 1}^n {|{x_i}^{(l)} - {x_j}^{(l)}{|^p}} )^{\frac{1}{p}}}

2. 曼哈顿距离(Manhattan distance)

L_{p}距离的p=1时,L_{p}就变成了曼哈顿距离

{L_1}({x_i},{x_j}) = (\sum\limits_{l = 1}^n {|{x_i}^{(l)} - {x_j}^{(l)}|} )

3.  欧式距离(Euclidean distance)

当当L_{p}距离的p=2时,L_{p}就变成了欧式距离

{L_2}({x_i},{x_j}) = {(\sum\limits_{l = 1}^n {|{x_i}^{(l)} - {x_j}^{(l)}{|^2}} )^{\frac{1}{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算法改进

  1. 寻求更接近于实际的距离函数以取代标准的欧氏距离,典型的工作包括 WAKNN、VDM ;
  2. 搜索更加合理的K值以取代指定大小的K值典型的工作包括SNNB、 DKNAW   
  3. 运用更加精确的概率估测方法去取代简单的投票机制,典型的工作包括 KNNDW、LWNB、 ICLNB ;
  4. 建立高效的索引,以提高KNN算法的运行效率,代表性的研究工作包括 KDTree、 NBTree。

参考资料

邻近算法_百度百科 (baidu.com)

LP距离、欧式距离、曼哈顿距离、切比雪夫距离_齐在的专栏-CSDN博客_lp距离

《西瓜书》——周志华

《数据挖掘导论》——朱明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我不会写BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值