【机器学习02】Python 手写 sklearn kNN 封装算法

栏目: 数据库 · 发布时间: 5年前

内容简介:解析Sklearn 中 的 kNN 算法封装。摘要:Python 一步步写出 Sklearn 中的 kNN 封装算法。昨天通过一个酒吧猜红酒的故事,介绍了机器学习中最简单的一个算法:kNN (K 近邻算法),并用 Python 一步步实现这个算法。同时为了对比,调用了 Sklearn 中的 kNN 算法包,仅用了 5 行代码。两种方法殊途同归,都正确解决了二分类问题,即新倒的红酒属于赤霞珠。

解析Sklearn 中 的 kNN 算法封装。

摘要:Python 一步步写出 Sklearn 中的 kNN 封装算法。

昨天通过一个酒吧猜红酒的故事,介绍了机器学习中最简单的一个算法:kNN (K 近邻算法),并用 Python 一步步实现这个算法。同时为了对比,调用了 Sklearn 中的 kNN 算法包,仅用了 5 行代码。两种方法殊途同归,都正确解决了二分类问题,即新倒的红酒属于赤霞珠。

文章传送门:

Python 手写机器学习最简单的 kNN 算法 (可点击)

虽然调用 Sklearn 库算法,简单的几行代码就能解决问题,感觉很爽,但其实我们时处于黑箱中的,Sklearn 背后干了些什么我们其实不明白。作为初学者,如果不搞清楚算法原理就直接调包,学的也只是表面功夫,没什么卵用。

所以今天来我们了解一下 Sklearn 是如何封装 kNN 算法的并自己 Python 实现一下。这样,以后我们再调用 Sklearn 算法包时,会有更清晰的认识。

先来回顾昨天 Sklearn 中 kNN 算法的 5 行代码:

from sklearn.neighbors import KNeighborsClassifier 
kNN_classifier = KNeighborsClassifier(n_neighbors=3)
kNN_classifier.fit(X_train,y_train )
x_test = x_test.reshape(1,-1)
kNN_classifier.predict(x_test)[0]

代码已解释过,今天用一张图继续加深理解:

【机器学习02】Python 手写 sklearn kNN 封装算法

可以说,Sklearn 调用所有的机器学习算法几乎都是按照这样的套路:把训练数据喂给选择的算法进行 fit 拟合,能计算出一个模型,模型有了就把要预测的数据喂给模型,进行预测 predict,最后输出结果,分类和回归算法都是如此。

值得注意的一点是, kNN 是一个特殊算法,它不需要训练(fit)建立模型,直接拿测试数据在训练集上就可以预测出结果。 这也是为什么说 kNN 算法是最简单的机器学习算法原因之一。

但在上面的 Sklearn 中为什么这里还 fit 拟合这一步操作呢,实际上是可以不用的,不过 Sklearn 的接口很整齐统一,所以为了跟多数算法保持一致把训练集当成模型。

随着之后我们学习更多的算法,会发现每个算法都有一些特点,可以总结对比一下。

把昨天的手写代码整理成一个函数就可以看到没有训练过程:

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

def kNNClassify(K, X_train, y_train, X_predict):
    distances = [sqrt(np.sum((x - X_predict)**2)) for x in X_train]
    sort = np.argsort(distances)
    topK = [y_train[i] for i in sort[:K]]
    votes = Counter(topK)
    y_predict = votes.most_common(1)[0][0]
    return y_predict

接下来我们按照上图的思路,把 Sklearn 中封装的 kNN 算法,从底层一步步写出那 5 行代码是如何运行的:

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

class kNNClassifier:
    def __init__(self,k):
        self.k =k
        self._X_train = None
        self._y_train = None

    def fit(self,X_train,y_train):
        self._X_train = X_train
        self._y_train = y_train
        return self

首先,我们需要把之前的函数改写一个名为 kNNClassifier 的 Class 类,因为 Sklearn 中的算法都是面向对象的,使用类更方便。

如果你对类还不熟悉可以参考我以前的一篇文章:

Python 类 Class 的理解 (可点击)

__init__ 函数中定义三个初始变量,k 表示我们要选择传进了的 k 个近邻点。

self._X_trainself._y_train 前面有个 下划线_ ,意思是把它们当成内部私有变量,只在内部运算,外部不能改动。

接着定义一个 fit 函数,这个函数就是用来拟合 kNN 模型,但 kNN 模型并不需要拟合,所以我们就原封不动地把数据集复制一遍,最后返回两个数据集自身。

这里要对输入的变量做一下约束,一个是 X_train 和 y_train 的行数要一样,一个是我们选的 k 近邻点不能是非法数,比如负数或者多于样本点的数, 不然后续计算会出错。用什么做约束呢,可以使用 assert 断言语句:

def fit(self,X_train,y_train):
        assert X_train.shape[0] == y_train.shape[0],"添加 assert 断言是为了确保输入正常的数据集和k值,如果不添加一旦输入不正常的值,难找到出错原因"
        assert self.k <= X_train.shape[0]
        self._X_train = X_train
        self._y_train = y_train
        return self

接下来我们就要传进待预测的样本点,计算它跟每个样本点之间的距离,对应 Sklearn 中的 predict ,这是算法的核心部分。而这一步代码就是我们之前写的函数,可以直接拿过来用,加几行断言保证输入的变量是合理的。

def predict(self,X_predict):
        assert self._X_train is not None,"要求predict 之前要先运行 fit 这样self._X_train 就不会为空"
        assert self._y_train is not None
        assert X_predict.shape[1] == self._X_train.shape[1],"要求测试集和预测集的特征数量一致"

        distances = [sqrt(np.sum((x_train - X_predict)**2)) for x_train in self._X_train]
        sort = np.argsort(distances)
        topK = [self._y_train[i] for i in sort[:self.k]]
        votes = Counter(topK)
        y_predict = votes.most_common(1)[0][0]
        return y_predict

到这儿我们就完成了一个简易的 Sklearn kNN 封装算法,保存为 kNN_sklearn.py 文件,然后在 jupyter notebook 运行测试一下:

先获得基础数据:

# 样本集
X_raw = [[13.23,  5.64],
       [13.2 ,  4.38],
       [13.16,  4.68],
       [13.37,  4.8 ],
       [13.24,  4.32],
       [12.07,  2.76],
       [12.43,  3.94],
       [11.79,  3.  ],
       [12.37,  2.12],
       [12.04,  2.6 ]]
X_train = np.array(X_raw)

# 特征值
y_raw = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
y_train = np.array(y_raw)

# 待预测值
x_test= np.array([12.08,  3.3])
X_predict = x_test.reshape(1,-1)

注意:当预测变量只有一个时,一定要 reshape(1,-1) 成二维数组不然会报错。

在 jupyter notebook 中运行程序可以使用一个魔法命令 %run:

%run kNN_Euler.py

这样就直接运行好了 kNN_Euler.py 程序,然后就可以调用程序中的 kNNClassifier 类,赋予 k 参数为 3,命名为一个实例 kNN_classify 。

kNN_classify = kNNClassifier(3)

接着把样本集 X_train,y_train 传给实例 fit :

kNN_classify.fit(X_train,y_train)

fit 好后再传入待预测样本 X_predict 进行预测就可以得到分类结果了:

y_predict = kNN_classify.predict(X_predict)
y_predict

[out]:1

答案是 1 和昨天两种方法的结果是一样的。

是不是不难?

再进一步,如果我们一次预测不只一个点,而是多个点,比如要预测下面这两个点属于哪一类:

【机器学习02】Python 手写 sklearn kNN 封装算法

那能不能同时给出预测的分类结果呢?答案当然是可以的,我们只需要稍微修改以下上面的封装算法就可以了,把 predict 函数作如下修改:

def predict(self,X_predict):
        y_predict = [self._predict(x) for x in X_predict]  # 列表生成是把分类结果都存储到list 中然后返回
        return np.array(y_predict)

def _predict(self,x):  # _predict私有函数
        assert self._X_train is not None
        assert self._y_train is not None

        distances = [sqrt(np.sum((x_train - x)**2)) for x_train in self._X_train]
        sort = np.argsort(distances)
        topK = [self._y_train[i] for i in sort[:self.k]]
        votes = Counter(topK)
        y_predict = votes.most_common(1)[0][0]
        return y_predict

这里定义了两个函数,predict 用列表生成式来存储多个预测分类值,预测值从哪里来呢,就是利用 _predict 函数计算, _predict 前面的下划线同样表明它是封装的私有函数,只在内部使用,外界不能调用,因为不需要。

算法写好,只需要传入多个预测样本就可以了,这里我们传递两个:

X_predict = np.array([[12.08,  3.3 ],
		[12.8,4.1]])

输出预测结果:

y_predict = kNN_classify.predict(X_predict)
y_predict

[out]:array([1, 0])

看,返回了两个值,第一个样本的分类结果是 1 即赤霞珠,第二个样本结果是 0 即黑皮诺。和实际结果一致,很完美。

到这里,我们就按照 Sklearn 算法封装方式写出了 kNN 算法,不过 Sklearn 中的 kNN 算法要比这复杂地多,因为 kNN 算法还有很多要考虑的,比如处理 kNN 算法的一个缺点:计算耗时。 简单说就是 kNN 算法运行时间高度依赖样本集有和特征值数量的维度,当维度很高时算法运行时间就极速增加,具体原因和改善方法我们后续再说。

现在还有一个重要的问题,我们在全部训练集上实现了 kNN 算法,但它预测的效果和准确率怎么样,我们并不清楚,下一篇文章,来说说怎么衡量 kNN 算法效果的好坏。

本文的 jupyter notebook 代码,可以在我公众号:「 高级农民工 」后台回复「 kNN2 」得到,加油!


以上所述就是小编给大家介绍的《【机器学习02】Python 手写 sklearn kNN 封装算法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

高效程序的奥秘

高效程序的奥秘

沃瑞恩 / 冯速 / 机械工业出版社 / 2004-5 / 28.00元

本书适合程序库、编译器开发者及追求优美程序设计的人员阅读,适合用作计算机专业高年级学生及研究生的参考用书。  本书直观明了地讲述了计算机算术的更深层次的、更隐秘的技术,汇集了各种编辑的小技巧,包括常购的任务的小算法,2的幂边界和边界检测、位和字节的重排列、整数除法和常量除法、针对整数的基涵义,空间填充曲线、素数公式等。一起来看看 《高效程序的奥秘》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具