KNN-k近邻算法

k近邻算法基础

解决分类问题

问题引入:
假设我们给出肿瘤大小作为横轴,发现肿瘤时间作为纵轴的坐标图。
其中红色代表为良性肿瘤,蓝色表示为恶性肿瘤:
在这里插入图片描述
现在,我们想根据新给出的一个肿瘤大小和发现时间的数据,来预测肿瘤是阳性还是阴性(图中绿色的点):
在这里插入图片描述

在这个问题中,我们需要通过已有的8个数据(红,蓝点的分布情况)来判断新给出的数据(绿点)是良性还是恶性肿瘤

想要用KNN解决分类问题也十分简单:

取绿色点周围最近的k(取值自选)个点,颜色相同数最多点的颜色即为最终结果

也就是说,k近邻的判断依据就是:两个样本如果足够相似,它们就有更高的几率属于同一个类别

在Notebook中实现

数据的准备

  • 先创建样本数据数组 raw_data, raw_data_y
    在这里插入图片描述
  • 再将数据集转换为numpy中的array数组
    在这里插入图片描述
  • 将样本数据集与待预测数据绘制散点图
    在这里插入图片描述

KNN过程

  • 先计算所有样本数据点到待预测数据点的距离distances(向量)
    其中,计算两点间的距离我们使用欧拉距离来计算:
    在这里插入图片描述
    在这里插入图片描述
  • 再找出distances中距离中前k个最小的距离点的索引(这里将k=6)
    在这里插入图片描述
  • 然后通过Counter投票找出前k个中最多的结果,就是我们的分类结果
    在这里插入图片描述

通过函数封装来实现

代码:

import numpy as np
from math import sqrt
from collections import Counter


def kNN_classify(k, X_train, y_train, x):
    assert 1 <= k <= X_train.shape[0], "k must be valid"
    assert X_train.shape[0] == y_train.shape[0], \
        "the size of X_train must equal to the size of y_train"
    assert X_train.shape[1] == x.shape[0], \
        "the feature number of x must be equal to X_train"

    distances = [sqrt(np.sum((x_train-x)**2)) for x_train in X_train]
    nearest = np.argsort(distances)

    topK_y = [y_train[i] for i in nearest[:k]]
    votes = Counter(topK_y)

    return votes.most_common(1)[0][0]

再通过Notebook中的魔法命令就可以直接使用了
在这里插入图片描述

scikit-learn 中的机器学习封装

设计机器学习的大致思想:
在这里插入图片描述
其中,对于KNN算法来说,喂入的训练集就是模型

使用scikit-learn中的KNN

引入sklearn包后首先需要传入设定的k值
在这里插入图片描述
再传入样本数据集进行拟合
在这里插入图片描述
进行预测前必须将需要预测的数据转化为矩阵形式传入,否则会报错
在这里插入图片描述

使用自己封装好的kNN

代码如下:

import numpy as np
from math import sqrt
from collections import Counter


class KNNClassifier:
    def __init__(self, k):
        assert k >= 1, "k must be valid"
        self.k = k
        self._X_train = None
        self._y_train = None

    def fit(self, X_train, y_train):
        assert X_train.shape[0] == y_train.shape[0], \
            "the size of X_train must be equal to the size of y_train"
        assert self.k <= X_train.shape[0], \
            "the size of X_train must be at least k."

        self._X_train = X_train
        self._y_train = y_train
        return self

    def predict(self, X_predict):
        assert self._X_train is not None and self._y_train is not None, \
            "must fit before predict!"
        assert X_predict.shape[1] == self._X_train.shape[1], \
            "the feature number of X_predict must be equal to X_train"

        y_predict = [self._predict(x) for x in X_predict]
        return np.array(y_predict)

    def _predict(self, x):
        assert x.shape[0] == self._X_train.shape[1], \
            "the feature number of x must be equal to X_train"

        distances = [sqrt(np.sum((x_train-x) ** 2)) for x_train in self._X_train]

        nearest = np.argsort(distances)

        topK_y = [self._y_train[i] for i in nearest[:self.k]]
        votes = Counter(topK_y)

        return votes.most_common(1)[0][0]

    def __repr__(self):
        return "kNN(k=%d)" % self.k

同样地,我们再使用魔法命令运行我们自己写的kNN
在这里插入图片描述
即可成功运行

训练、测试数据集

在实际问题中,除了设计出机器学习模型,我们还要去测试这种学习模型的准确率。
那么有什么方法能够判断出机器训练出的模型的准确率有多少呢?
我们可以从给出的训练数据集中抽出一部分,作为测试数据
在这里插入图片描述
根据这样的思想,我们可以测试一下我们自己刚刚编写的kNN的准确率

测试算法的准确率

这里我们使用sklearn中的鸢尾花数据集

首先加载鸢尾花数据集,将x,y特征值分别存入X,Y中
在这里插入图片描述

在这里插入图片描述

这里我们可以看到,Y的数据是很规律的,我们如果只选取前n个数据,得到的结果误差一定相当大。所以,我们需要先把数据打乱,再进行训练数据和测试数据的划分

打乱数据的索引
在这里插入图片描述
计算测试数据集的容量(这里定为总样本的20%)
在这里插入图片描述
将训练与测试数据集索引分离
在这里插入图片描述
根据分离好的索引,分别求出x,y
在这里插入图片描述

编写我们自己的分离数据函数

代码如下:

import numpy as np


def train_test_split(X, y, test_ratio=0.2, seed=None):
    assert X.shape[0] == y.shape[0], \
        "the size of X must be equal to the size of y"
    assert 0.0 <= test_ratio <= 1.0, \
        "test_train must be valid"

    if seed:
        np.random.seed(seed)

    shuffled_indexes = np.random.permutation(len(X))

    test_size = int(len(X) * test_ratio)
    test_indexes = shuffled_indexes[:test_size]
    train_indexes = shuffled_indexes[test_size:]

    X_train = X[train_indexes]
    y_train = y[train_indexes]

    X_test = X[test_indexes]
    y_test = y[test_indexes]

    return X_train, X_test, y_train, y_test

测试一下编写的代码:
在这里插入图片描述
同样可以根据这种原理,算出我们编写程序的准确率:
在这里插入图片描述

使用sklearn中封装好的测试模型

在这里插入图片描述

分类准确度

我们通过sklearn库中的手写数据库中的数据来验证一下算法的准确度

首先,我们引入sklearn的手写数据集
在这里插入图片描述
先用x,y分别接收一下数据
在这里插入图片描述
我们可以取出一部分数据查看一下
在这里插入图片描述
在这里插入图片描述
我们随意取出一个位置(索引为666)的数据进行绘图查看一下
在这里插入图片描述

这里可以看到是手写数字0

接下来,计算我们训练好的模型的准确率就很简单了。
先将我们通过模型预测的结果集设为y_train,测试集对应结果命名为y_test。计算y_train == y_test 的个数,该个数与y_test总个数的比值就是我们要求的精确度比值了

代码如下:

import numpy as np


def accuracy_score(y_true, y_predict):
    #  计算y_true和y_predict之间的准确率
    assert y_true.shape[0] == y_predict.shape[0], \
        "the size of y_true must be equal to the size of y_predict"

    return sum(y_true == y_predict) / len(y_true)

我们在Notebook上运行一下

在这里插入图片描述

这样,我们就计算出了我们预测的准确值

sklearn中的accuracy_score

sklearn库中的使用方法基本和我们自己实现的方法相似
在这里插入图片描述

超参数

超参数:在算法运行前需要决定的参数
模型参数:算法过程中学习的参数

其中,kNN算法没有模型参数,kNN算法中的k就是典型的超参数

我们想要寻找好的超参数,主要就由三个方面的因素决定:领域知识经验知识实验搜索

接下来,我们使用模型搜索的方法来测试寻找较好的超参数

寻找好的k

这里,我们依旧选用手写数字数据集进行实验

首先,在sklearn库中载入手写数字数据,并做好准备工作
在这里插入图片描述
然后我们使用for循环,逐个在【1,10】中寻找效果最好的k就行了
在这里插入图片描述
这样,我们就找到了针对我们的模型的最好的k

注意:如果我们搜索出的k结果是10,也就是属于范围边缘,我们就需要扩大搜索范围,比如将范围扩大至【1,20】来搜索

考虑到距离因素

分类时,除了考虑按照最近距离的个数分类,有时我们还需要将距离也算进考虑范畴当中
在这里插入图片描述

比如此图中,我们就可以把预测点与红点划为一类,因为红色点最近,即占的比重最大

想要实现,我们只需要将函数中的参数更改一下即可
在这里插入图片描述
同样地,我们还可以更改参数p
在这里插入图片描述

网格搜索

网格搜索的原理很简单,就是将要想要查询的信息存放为字典,多个字典用列表存储再通过GridSearchCV测试就可以了

具体实现方法:
在这里插入图片描述
初始化的时候传入kNeighborsClassifier和param_grid
在这里插入图片描述
我们再用训练集来拟合一下
在这里插入图片描述

然后我们就可以查看最好的拟合效果,等结果
在这里插入图片描述
再将knn_clf运用我们刚刚得到的最佳效果训练参数,就可以达到最好的训练效果
在这里插入图片描述
GridSearchCV中还可以传入不同的参数(n_jobs控制运行的核的个数,verbose决定是否显示运行过程中的结果)
在这里插入图片描述

数据归一化

解决实际问题时,如果不统一一个度量标准,得到的结果差距会十分大。
比如我们举出肿瘤大小与发现天数之间的关系,如果我们对肿瘤发现的时间用不同的单位来统计:
在这里插入图片描述
在这里插入图片描述
这样我们再套入到kNN算法中,计算出的结果会有很大的差距。前者的发现时间占主导而后者是肿瘤大小占主导。

那么有什么方法能够让我们统一不同的量度呢?

接下来我们引入两个不同的计算方法

最值归一化 Normalization

原理:将所有数据都映射到同一尺度
结果:把所有数据映射到0和1之间

计算公式:
在这里插入图片描述

Notebook实现

首先引入我们需要使用的模块
在这里插入图片描述
我们先随机生成一个二维向量
在这里插入图片描述
由于numpy默认生成的是int类型,精度还不够,我们需要强行转换为float类型
在这里插入图片描述
套入上述公式进行计算(维度多的化可以用for循环来写)
在这里插入图片描述
计算结果
在这里插入图片描述
绘图查看一下结果
在这里插入图片描述

该方法适用于以下情况:分布有明显边界

均值方差归一化 Standardization

原理:将所有数据都映射到同一尺度
结果:把所有数据映射到均值0方差为1的分布中
公式:(S表示方差)
在这里插入图片描述

Notebook实现

同样的,我们先随机生成一个二维随机数据集
在这里插入图片描述
套入公式
在这里插入图片描述
绘图查看一下
在这里插入图片描述

上述方法适用于以下情况:
1)分布没有明显的边界
2)有可能存在极端数据值

scikit-learn中的Scaler

由于真实情况中,我们给出的测试数据集的数量通常很少,只考虑测试数据集的平衡值预测出的结果会很不准确,所以我们用训练数据集中的mean_train来代替test_train在这里插入图片描述

Notebook实现

先加载鸢尾花的数据并保存到X,y中
在这里插入图片描述
进行测试集和训练集的分离
在这里插入图片描述
从库中加载进StandardScaler
在这里插入图片描述
输入X_train进行拟合
在这里插入图片描述
将归一好的X_train进行替换
在这里插入图片描述
替换好的结果
在这里插入图片描述
同样地,X_test也要进行归一
在这里插入图片描述
再通过KNN看一下精确度
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值