我的机器学习之路-KNN算法

我的机器学习之路-KNN算法

一、 K近邻算法

k-近邻(k-Nearest Neighbour,简称KNN)是一种基本分类与回归方法,属于是有监督学习中的分类算法。

二、 算法介绍

2.1 思路

k近邻算法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多类。k近邻法假设给定一个训练数据集,其中的实例类别已定。分类时,对新的实例,根据其k个最近邻的训练实例的类别,通过多数表决等方式进行预测。因此k近邻法不具有显示的学习过程。k近邻法实际上利用训练数据集对特征向量空间进行划分,并作为其分类的“模型”。

2.2 距离计算

特征空间中两个实例点的距离是两个实例点相似程度的反映。k近邻模型的特征空间一般是n维实数向量空间Rn。使用的距离是欧式距离,也可以是其它距离。

2.2.1 数学公式

欧拉公式

二维空间:
( x m x n ) 2 + ( y m y n ) 2 \sqrt{(x^m-x^n)^2+(y^m-y^n)^2}

如果在三维空间,就多了个z轴:
( x m x n ) 2 + ( y m y n ) 2 + ( z m z n ) 2 \sqrt{(x^m-x^n)^2+(y^m-y^n)^2+(z^m-z^n)^2}

在整个特征空间中可能维度远不止3维,那么更加方便的写法就是X加下标用来表示特征的维度,N为空间的公式:

( x 1 m x 1 n ) 2 + ( x 2 m x 2 n ) 2 + . . . + ( x n m x n n ) 2 \sqrt{(x_1^m-x_1^n)^2+(x_2^m-x_2^n)^2+...+(x_n^m-x_n^n)^2}

最后化简:

i = 1 n ( x i m x i n ) 2 \sqrt{\sum_{i=1}^n(x_i^m-x_i^n)^2}

三、Python实现

3.1 测试数据集

选取sklearn库中的鸢尾花卉数据集(iris),iris是一类多重变量分析的数据集。数据集包含150个数据样本,分为3类,每类50个数据,每个数据包含4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性,预测鸢尾花卉属于(Setosa,Versicolour,
Virginica)三个种类中的哪一类。

3.2 数据预处理

选取花萼的长度和宽度作为特征,总共选取20个数据点作为数据集,对应的类别分别用0和1表示。

from sklearn.datasets import load_iris
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

iris = load_iris()# 返回的是Bunch对象 与字典非常类似 包含键和值
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']

plt.scatter(df[0:10]['sepal length'], df[:10]['sepal width'], label='0')
plt.scatter(df[70:80]['sepal length'], df[70:80]['sepal width'], label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
# plt.show()

通过matplotlib画图可以看到:
在这里插入图片描述

分别选取十个不同类的鸢尾花数据,总共20个数据把它们作为接下来的数据集合:

data1 = np.array(df.iloc[0:10, [0, 1, -1]])
data2 = np.array(df.iloc[70:80, [0, 1, -1]])
data = np.append(data1,data2,axis=0) # 合并两个narray数组

再将数据分成训练集和测试集:

X, y = data[:,:-1], data[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

3.3 KNN算法实现

from collections import Counter
import numpy as np

class KNN_Model():

    def __init__(self,X_train,y_train,n_neighbors = 3,p = 2):
        """
        :param X_train:  训练集
        :param y_train:  训练集
        :param n_neighbors: 临近点数量  此处默认为3
        :param p:  距离度量 此处默认使用欧式距离
        """
        self.X_train = X_train

        self.y_train = y_train

        self.neighbors = n_neighbors

        self.p = p

    def prediction(self,X):

        knn_list = []

        # 首先选取前3个点进行计算
        for i in range(self.neighbors):
            # 计算 待分类点与训练集中数据的欧式距离
            distance = np.linalg.norm(X - self.X_train[i],ord=self.p)
            # 将距离 和 对应的类别存在列表中
            knn_list.append((distance,self.y_train[i]))

        # 此时knn_list的数据如下
        # 每个元组里面 前一个数据为距离 后一个数据为对应的类别
        # [(0.8944271909999156, 1.0), (2.209072203437452, 1.0), (0.3999999999999999, 0.0)]

        for i in range(self.neighbors,len(self.X_train)):

            # 首先max函数里面选取出  knn_list 里面最大的距离数据
            # 然后取出它的索引存在 max_index
            max_index = knn_list.index(max(knn_list, key=lambda x: x[0]))

            # 继续计算待分类点与其他训练集数据的欧式距离
            distance = np.linalg.norm(X - self.X_train[i],ord=self.p)
            
            # 每次循环迭代knn_list的数据 目标是找到距离小的点
            # 如果新的点比之前列表中的距离要小 那么就添加进去 因为比较的是最大点
            # 要做的就是把距离大的点从列表中剔除出去
            if knn_list[max_index][0] > distance :

                knn_list[max_index] = (distance,self.y_train[i])

        # 解析列表 把对应的类别放入新的列表
        # 此处数据形式
        # [1.0, 1.0, 1.0]
        knn = [k[-1] for k in knn_list]

        #  Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key
        # 此处就是为了统计每个类别出现的次数,好方便后面最终确定待分类点属于哪一类
        # 对应数据格式
        # Counter({1.0: 3})
        count_pairs = Counter(knn)

        # 最后进行排序 得出出现次数最多的那个类别 作为最终的分类结果
        # 因为 sorted返回的数据格式是列表形式 所以需要进行索引
        # 如  [1.0]
        max_count = sorted(count_pairs, key=lambda x: x)[-1]

        return max_count

    def score(self,X_test,y_test):

        # 分类正确的次数 初始为 0
        right_count = 0
        
        for X, y in zip(X_test, y_test):
            label = self.prediction(X) # 调用预测模型 得出分类标签

            # print("预测类别为:{}".format(label))
            # print("实际类别为: {}".format(y))
            # print("")

            if label == y: # 如果分类正确
                right_count += 1  #次数 +1

        # 返回最终分类结果表现 正确次数/测试数据集总数
        return right_count / len(X_test)


3.4 模型评估

使用之前处理好的鸢尾花数据来进行测试:


from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import Statistical_learning_method.Chapter_3.MyFunction as Myfun
from Statistical_learning_method.Chapter_3.DataSet_1 import data as source
# from Statistical_learning_method.Chapter_3.KNN import KNN as the_old_knn

data = source
X, y = data[:,:-1], data[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# print("X_train:\n{}".format(X_train))
# print("")
# print("X_test : \n{}".format(X_test))
# print("")
# print("y_train:\n{}".format(y_train))
# print("")
# print("y_test:\n{}".format(y_test))

# clf = Myfun.KNN_Model(X_train, y_train)
# clf = the_old_knn(X_train,y_train)

# 调用sklearn库封装的KNN算法
clf_sk = KNeighborsClassifier()
clf_sk.fit(X_train, y_train)
print(clf_sk.score(X_test,y_test))

参考资料: 李航<<统计学习方法>>

发布了189 篇原创文章 · 获赞 58 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/Coder_py/article/details/103392896