2.KNN近邻分类算法-机器学习

KNN(K- Nearest Neighbor)近邻算法是最简单的机器学习模型之一。通过计算样本特征间的距离来预测结果。

1 KNN算法介绍

1.1 k近邻法原理

常言道,近朱者赤近墨者黑,而KNN就是这样一种分类模型:
第一步:首先需要给定一堆带有标签y_test的数据X_test,作为模型的训练集;
第二步:然后输入没有一条没有标签的新样本,依次计算新样本和训练集X_test所有样本的距离,一般采用欧氏距离或曼哈顿距离。
第三步:取出距离新样本最近的K个训练集样本,用K个样本中所属标签占比最多那一类作为新样本的分类标签。
K近邻法

1.2 KNN优缺点

优点:易于理解实现,精确度相对来说也是比较高的。
缺点:由于要计算大量的数据值,这种模型算法时间复杂度和空间复杂度较高。另外如果训练集的样本标签不平衡,有的标签多,有的标签少,就会导致**输入样本容易被错误的划分到标签多的样本里。**如果出现这类情况,可以采用对K近邻点加权的方式,让距离近的点有较大的权重。

1.3 KNN注意点分析

  • 数据量化:KNN模型是计算特征之间的距离,也就是把每个样本当作多维空间的点,计算点与点之间的距离。因此输入的训练集数据需要是数值型的,非数值型数据要进行量化处理。
  • 数据归一化:同一个样本有多条特征,每条特征的的数据的值域都不同,就会导致在计算时,值域大的特征更容易影响到计算结果,因此训练集的数据一般要进行归一化处理,确保每一条特征对距离的影响相同。
  • K值的选取:K值是新样本的邻居个数,一般不会超过20个。K的选择太低容易欠拟合,太高又容易过拟合,因此可以采用交叉验证的方式来选择合适的K。

2 代码实现

sklearn是基于python语言实现的机器学习工具,其中已经封装好了很多机器学习模型,而且内置了部分数据集提供我们学习使用。

2.1 加载数据

测试的数据来源于Sklearn模块中的鸢尾花数据,调取鸢尾花数据的代码如下所示

import pandas as pd
# 导入内置数据集
from sklearn import datasets
from sklearn.model_selection import train_test_split as split
# 加载鸢尾花数据
iris = datasets.load_iris()
iris_df = pd.DataFrame(iris.data,columns=iris.feature_names)
iris_df['target'] = iris.target
# 分割数据
X_train, X_test, y_train, y_test = split(iris_df.iloc[:,:-1], iris_df.iloc[:, -1])

2.2 调用KNN模块实现knn分类

# 导入knn分类模块
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier() #创建模型
# fit训练数据,predict预测数据
knn1.fit(X_train,y_train)
print(knn1.predict(X_test))

虽然sklearn已经帮我们封装好了很多模型,使用也相当简单,但要真正入门理解机器学习模型,我们就需要尝试自己去封装模型。

2.3 自己封装简单的KNN模型

import numpy as np
class Knnclass(object):
    '''
        本模型为knn分类模型,构造时需要允许传递的参数如下所示:
        n_neighbors:默认为5。表示邻点的个数n,训练时会把距离最短的n个邻居加到列表中,最后出现次数最多的预测结果
        p:默认为2,表示计算距离的方式为欧氏距离,设置为1时,则采用曼哈顿距离进行计算
        knn不需要训练数据,因此在调用fit函数时,程序只是将训练数据传给对象的x_train和y_train
    '''
    # 类初始化
    def __init__(self, *, n_neighbors=5, p=2):
        # 默认邻居个数为5个
        self.n_neighbors = n_neighbors
        # 默认计算距离方式为p=2表示欧氏距离p=1计算曼哈顿距离
        self.p = p
        # 将测试数据私有化,只有在调用fit函数时可以进行修改
        self._x_train = None
        self._y_train = None

        # 用于存放测试后的y数据
        self._y_test = None

    # fit 函数用于训练样本
    def fit(self,x_train ,y_train):
        self._x_train = x_train
        self._y_train = y_train
    def predict(self,x_texts):
        # 定义空列表用于放置预测后的y值
        pre_list = []
        for index in x_texts.index:
            # 一一计算预测y值并添加到列表中
            pre_list.append(self.predict_one(x_texts.loc[index]))
        self._y_test = np.array(pre_list)
        return np.array(pre_list)
    # predict函数用于预测样本
    def predict_one(self,x_text):
        # 把用于分类邻居加到一个列表里
        knn_list = []
        for i in self._x_train.index:
            # 判断列表中个数不得超过邻居
            if len(knn_list) < self.n_neighbors:
            # print(x_text-self._x_train.iloc[])
            # 计算前self.n_neighbors个特征数据与测试的距离
            # ord表示要求的距离1表示曼哈顿,2表示欧氏距离。keepdims表示保留原数组格式,因为只需要求距离的值,因此不保留
                dist = np.linalg.norm(x_text-self._x_train.loc[i],ord=self.p,keepdims=False)
                # 将距离和y_train的结果一一添加到列表中
                knn_list.append((dist,self._y_train.loc[i]))
                # 找到列表中距离最长的索引
                max_index = knn_list.index(max(knn_list,key=lambda x:x[0]))
            else:
                # 继续计算剩余特征集的距离,若计算的距离小于knn_list中任意一个,则将其替换
                dist = np.linalg.norm(x_text-self._x_train.loc[i],ord=self.p,keepdims=False)
                if dist < knn_list[max_index][0]:
                    knn_list[max_index] = (dist,self._y_train.loc[i]) # 替换

                    # 最大值的索引更新
                    max_index = knn_list.index(max(knn_list,key=lambda x:x[0]))
            # print(dist,knn_list)
        # 将结果y的值和出现的次数分别作为键和值加入字典
        dist_dict={}
        for j in knn_list:
            if j[1] not in dist_dict:
                dist_dict[j[1]] = 1
            else:
                dist_dict[j[1]] += 1
        # 找到出现字典中次数最多的结果y:根据值排序并返回元组,升序排序因此获取最后一个元素的值
        max_count_y = sorted(dist_dict.items(),key=lambda x:x[1])[-1][0]
        return max_count_y
    # 计算得分
    def score(self,x_test,y_test):
        self.predict(x_test)
        y_test = y_test.ravel()
        result = (y_test == self._y_test)
        # 将result中正确的True加入一个列表中,然后用个数除以测试集总个数得到训练的分数
        score = len([i for i in result if i])/len(y_test)
        return score

2.3 测试

from sklearn.model_selection import train_test_split as split
import pandas as pd
# 导入并加载鸢尾花数据集
from sklearn import datasets
iris = datasets.load_iris()
# 将数据集转成dataframe数据表
iris_df = pd.DataFrame(iris.data,columns=iris.feature_names)
iris_df['target'] = iris.target
# 分割数据
X_train, X_test, y_train, y_test = split(iris_df.iloc[:,:-1], iris_df.iloc[:, -1])

# 打印真实y数据
print('真实结果',y_test.ravel())
print('-'*30)

from KnnClass import Knnclass # 导入knn分类模型
knn = Knnclass()
knn.fit(X_train,y_train)
# knn训练后的结果
# print('KNN预测后的结果',knn.predict(X_test))
print('KNN预测后的分数',knn.score(X_test,y_test))

print('-'*30)

# 导入官方knn模型
from sklearn.neighbors import KNeighborsClassifier
# knn训练后的结果
knn1 = KNeighborsClassifier()
knn1.fit(X_train,y_train)
# print('官方KNN预测数据',knn1.predict(X_test))
print('官方KNN预测分数',knn1.score(X_test,y_test))

下面是测试的结果

真实结果 [0 0 1 2 2 1 1 1 2 1 1 1 2 2 0 0 0 1 2 0 1 0 2 2 0 2 0 2 0 1 1 2 1 0 0 0 0
 2]
------------------------------
KNN预测后的结果 [0 0 1 2 2 1 1 1 2 2 1 1 2 2 0 0 0 1 2 0 1 0 1 2 0 2 0 2 0 1 1 2 1 0 0 0 0
 2]
KNN预测后的分数 0.9473684210526315
------------------------------
官方KNN预测数据 [0 0 1 2 2 1 1 1 2 2 1 1 2 2 0 0 0 1 2 0 1 0 1 2 0 2 0 2 0 1 1 2 1 0 0 0 0
 2]
官方KNN预测分数 0.9473684210526315
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值