机器学习 K近邻算法(KNN算法)

2.K近邻算法(KNN算法)

2.1简单KNN模型实例

2.1.1问题描述

本次实例采用暴力搜索的方法,即最原始的 kNN 方法进行实例讲解。数据集为鸢尾花数据集,可以在机器学习函数库 sklearn 中调用。

2.1.2鸢尾花数据集

Iris 鸢尾花数据集是一个经典的数据集,在统计学习和机器学习领域中经常被用作示例。数据集内包含 3 类共 150 条记录,每类记录各有 50 个数据,每条记录都有 4 项特征:花萼长度;花萼宽度;花瓣长度;花瓣宽度。因此,可以通过这 4 个特征来预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种[129]。

2.1.3算法实现

实例分析:BFS 实现鸢尾花数据集分类
(1) 处理数据:使用 sklearn 数据集,加载鸢尾花数据集。然后划分为训练集和测试集。
(2) 构建 kNN 算法:由于 kNN 算法中不具有显式学习过程,所以该部分代码包含预测代码。
(3) 评估精度:评估对于测试数据集的预测精度作为预测正确率[19]。
(4) 完整代码:使用所有代码呈现一个完整的、独立的支持向量机算法的实现[18]。
(5) 实验结果:通过改变 k 的取值,观察实验结果。

2.2代码及其解释

2.2.1生成/导入数据

def create_data():
    iris = load_iris()
    data, target = iris.data, iris.target
    name = ["花萼长度", "花萼宽度", "花瓣长度", "花瓣宽度"]
    visualize_feature(data, target, name)
    x_train,x_test,y_train,y_test=train_test_split(data, target, test_size=0.33, random_state=42)
    return x_train, x_test, y_train, y_test

2.2.2数据可视化

下面通过可视化鸢尾花数据的 4 种特征,帮助我们更好了解数据分布,具体代码如下:

def visualize_feature(X, Y, name_data):
    for i in range(len(name_data)):
        plt.subplot(2, 2, i+1)
        plt.scatter(y=X[:, i], x=Y[:, ], c=Y[:, ])
        plt.xlabel("鸢尾花标签")
        plt.ylabel(name_data[i])

    plt.suptitle("鸢尾花数据的4种特征")
    plt.tight_layout()  # 使子图标题和全局标题与坐标轴不重叠
    plt.show()

image

2.2.3构建KNN算法

(1)参数初始化

def __init__(self, K, X_train, Y_train):
    self.k = K
    self.x_train = np.array(X_train)
    self.y_train = np.array(Y_train)

(2)对测试集进行预测

def predict(self, data):
    labels = np.repeat(self.y_train.reshape(1, -1), len(data), axis=0)
    dist = cdist(data, self.x_train)
    for i in range(len(labels)):
        Z = zip(dist[i], labels[i])
        Z = sorted(Z, reverse=False)
        dist[i], labels[i] = zip(*Z)
    labels = labels[:, :self.k]
    res = [np.argmax(np.bincount(label)) for label in labels]
    return res

(3)评估精度

    def evaluate(self, data, label, i):
        """
        评估精度
        """
        pred = self.predict(data)
        acc = np.sum(pred == label) / len(label)
        print("当 k = {} 时, 准确率为:{}%".format(i, acc * 100))
        return acc

2.2.4完整代码

import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

plt.rcParams['font.sans-serif'] = ['SimHei']  # 正常显示中文标签

class KNN(object):
    def __init__(self, K, X_train, Y_train):
        self.k = K
        self.x_train = np.array(X_train)
        self.y_train = np.array(Y_train)

    def predict(self, data):
        """
        对测试集进行预测
        """
        labels = np.repeat(self.y_train.reshape(1, -1), len(data), axis=0)
        dist = cdist(data, self.x_train)
        for i in range(len(labels)):
            Z = zip(dist[i], labels[i])
            Z = sorted(Z, reverse=False)
            dist[i], labels[i] = zip(*Z)
        labels = labels[:, :self.k]
        res = [np.argmax(np.bincount(label)) for label in labels]
        return res

    def evaluate(self, data, label, i):
        """
        评估精度
        """
        pred = self.predict(data)
        acc = np.sum(pred == label) / len(label)
        print("当 k = {} 时, 准确率为:{}%".format(i, acc * 100))
        return acc

def create_data():
    """
    生成数据
    """
    iris = load_iris()
    data, target = iris.data, iris.target
    name = ["花萼长度", "花萼宽度", "花瓣长度", "花瓣宽度"]
    visualize_feature(data, target, name)
    x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.33, random_state=42)
    return x_train, x_test, y_train, y_test

def visualize_feature(X, Y, name_data):
    """
    可视化鸢尾花的4个特征数据
    """
    for i in range(len(name_data)):
        plt.subplot(2, 2, i + 1)
        plt.scatter(y=X[:, i], x=Y[:, ], c=Y[:, ])
        plt.xlabel("鸢尾花标签")
        plt.ylabel(name_data[i])

    plt.suptitle("鸢尾花数据的4种特征")
    plt.tight_layout()  # 使子图标题和全局标题与坐标轴不重叠
    plt.show()

def visualize_results(k, acc):
    plt.plot(k, acc)
    plt.xlabel("K 值")
    plt.ylabel("准确率")
    plt.title("暴力搜索算法")
    plt.show()

if __name__ == '__main__':
    x_train, x_test, y_train, y_test = create_data()
    acc_list = []
    k_list = []
    for i in range(1, 102, 2):
        k = KNN(i, x_train, y_train)
        acc = k.evaluate(x_test, y_test, i)
        acc_list.append(acc)
        k_list.append(i)
    visualize_results(k_list, acc_list)

    # 根据 k_list 与 acc_list 选取k值11进行验证
    knn = KNeighborsClassifier(n_neighbors=11)
    knn.fit(x_train, y_train)
    p_res = knn.predict(x_test)
    print(f"预测结果:{p_res}")
    score = knn.score(x_test, y_test)
    print(f"预测准确率:{score * 100}%")

2.3实验结果

2.3.1观察K值变化对准确度的影响

for i in range(1, 102, 2):
        k = KNN(i, x_train, y_train)
        acc = k.evaluate(x_test, y_test, i)
        acc_list.append(acc)
        k_list.append(i)

image

从上面的实验结果可以证明,k 值的选择直接可以影响实验结果,当 k 取 1 时为最近邻算法,实际上,当 k 为 1 时,算法对于测试数据点将会非常敏感,但是由于鸢尾花数据集简单、异常数据少,所以能够呈现出较好的效果。随着 k 值的增大,算法的表现能力也开始变得更为优秀,但是当 k 值取得很大的时候,模型的表现迅速下降,所以 k 值的选择应该要小于数据集大小的开方。并且需要注意一点的就是 k 值的选取需为奇数。

2.3.2验证实验结果

# 根据 k_list 与 acc_list 选取k值11进行验证
knn = KNeighborsClassifier(n_neighbors=11)  
knn.fit(x_train, y_train)
p_res = knn.predict(x_test)
print(f"预测结果:{p_res}")
score = knn.score(x_test, y_test)
print(f"预测准确率:{score * 100}%")

>>>
预测结果:[1 0 2 1 1 0 1 2 1 1 2 0 0 0 0 1 2 1 1 2 0 2 0 2 2 2 2 2 0 0 0 0 1 0 0 2 1 0 0 0 2 1 1 0 0 1 2 2 1 2]
预测准确率:100.0%
posted @ 2021-06-11 11:10  Qiyez七夜  阅读(305)  评论(0)    收藏  举报