这是最简单的机器学习算法,我找不到其他更简单的了,你只需要知道Python,和numpy的基本操作就能看懂,看不懂你打我。
先说个闲话,疫情期间我回老家过年,没想到一待就是两个多月,刚开始每天还写写代码,后来电脑被我妈抢去看剧,我就快一个多月没怎么写过代码,实现这个KNN我都差点卡壳了,所以说,代码必须常写,多练,才能保持强大。
接下来就是正题了。首先,什么是KNN?
KNN是最简单的一个分类算法(当然也可以进行回归,不过不是主流操作)。考虑到你可能不清楚什么是分类,这里我们引入一个公开的数据集来加以解释。
这个数据集叫iris,中文名是安德森鸢尾花卉数据集,iris包含150个样本,对应数据集的每行数据。每行数据包含每个样本的四个特征和样本的类别信息。
通俗地说,iris数据集是用来给花做分类的数据集,每个样本包含了花萼长度、花萼宽度、花瓣长度、花瓣宽度四个特征。一共有三种不同的鸢尾花——山鸢尾、变色鸢尾和维吉尼亚鸢尾。
对鸢尾花数据集进行分类的任务就是要根据鸢尾花的四个特征——花萼长度、花萼宽度、花瓣长度和花瓣宽度,来判断这个样本属于哪一类鸢尾花。
下面我们查看一下鸢尾花数据集。
导入数据
考虑到sklearn自带了这个数据集,所以我们直接将它导入进来就行了。
from sklearn.datasets import load_iris
载入数据
iris = load_iris()
x = iris.data
y = iris.target
查看数据
x.shape
![589f4e171940370cc23bb3c0fc88640f.png](https://i-blog.csdnimg.cn/blog_migrate/90a22d1696b7806ee86c96fb2747a1d3.png)
y
![26ddb9447c471375a2f04b4591b4855a.png](https://i-blog.csdnimg.cn/blog_migrate/f8b3c40f0724914e311faca9bbf538ac.jpeg)
分割训练集和测试集
#划分数据集为百分之八十的训练集,剩余百分之二十是测试集
from sklearn.model_selection import train_test_split
x_train , x_test ,y_train , y_test = train_test_split(x, y, test_size=0.2, random_state=666666)
查看数据
X = x_test[0] #这是我们待会输入进去的数据
X
![13c61c4ed7c0adde5585ffafc100f05c.png](https://i-blog.csdnimg.cn/blog_migrate/f792d2084550f4f1df782bb382e91d11.png)
y_test[0]
![4534d2053814bc54d4c155651ff09a18.png](https://i-blog.csdnimg.cn/blog_migrate/71741d1e5440da1241064230f8627457.png)
KNN算法过程
现在你大概了解了什么是分类以及我们的数据集长什么样,接下来我们就要手写KNN了。
在手写KNN之前,你需要知道KNN的核心思想——谁离我最近,我就跟谁是一类。
其次你需要知道K是什么,K是你人为指定的一个参数,可以根据实际需要调整。KNN其实就是找到前K个离我最近的样本,前K个样本中类别最多的那一类就和我是一类。
你可能注意到了,找到前K个离我最近的样本,什么叫最近?实际上距离这个东西有很多定义,常见的有初中就学过的欧氏距离,即空间中两点的距离(这个公式应该都知道吧?)。
当然还有其他的距离算法,比如闵可夫斯基距离,Jaccard相似系数,余弦相似度,pearson相关系数。。。。。。
我们这里就先采用我们熟悉的欧氏距离。
导入我们需要的numpy
import numpy as np
计算距离
distances = []
for x_data in x_train:
distance = np.sqrt(np.sum(x_data - X)**2) #欧式距离的公式,别说你忘了
distances.append(distance)
![b8905f5c2165d531f8166b223281d03d.png](https://i-blog.csdnimg.cn/blog_migrate/82d392ff976c65091fb33875c9a75d94.jpeg)
上图所示,就是我们输入的样本X与所有训练样本的距离(只放了一部分,太长了)。算完了距离后,需要对距离排序。
对距离排序
numpy中提供了非常好的排序算法,帮我们减轻了负担
np.argsort(distances) #以下所显示的是离我们最近的样本的索引
![5b346f26218ac3137a718076ae299d21.png](https://i-blog.csdnimg.cn/blog_migrate/ae49bcdce25ecd80049e3ce320b380cd.jpeg)
我们先前讲过了,KNN其实就是找到前K个离我最近的样本,前K个样本中类别最多的那一类就和我是一类。
K是多少,我们不知道,并没有一个科学的选法,用专业的话讲,K就是超参数,实际中需要调整,也就是调参(你现在知道调参侠是什么了吧,调的就是超参数)。
我们这里假设k是3。
指定超参数K
k = 3
nearest = np.argsort(distances)[:k] #取前k个
top_k_y = [y_train[index] for index in nearest]
top_k_y #说实话我没想到都是2,我事先试过了,试到了40基本上都还是2,看来这个数据集非常适合KNN
![e338a8f91fdaddc3014063d7eabde889.png](https://i-blog.csdnimg.cn/blog_migrate/33ca9fd448f0e51fca58ee3945b9978d.png)
选出类别数量最多的做为预测结果
这里离样本最近的都是2这个类别的鸢尾花,其实不用看都知道答案了,但是为了算法具有泛化能力我们还是得把具体过程写出来
d = {}
for cls in top_k_y:
d[cls] = d.get(cls,0) + 1
d_list = list(d.items())
d_list.sort(key=lambda x:x[1],reverse=True)
d_list[0][0] #这就是最终预测结果
![2ec60757edccd1c4a4cc527f736104d5.png](https://i-blog.csdnimg.cn/blog_migrate/978062c229e526638f4b66293de92751.png)
至此为止,KNN的过程我们就完整的梳理了一遍
将其完整的封装好就是下面这样,可以好好看看
import numpy as np
class KNNClassifier:
def __init__(self,k=3):
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
def predict(self,x_predict):
y_predict = [self._predict(x) for x in x_predict]
return np.array(y_predict)
def _predict(self,x):
distances = [np.sqrt(np.sum((x_train - x)**2)) for x_train in self.x_train]
nearest = np.argsort(distances)[:self.k]
top_k_y = [self.y_train[index] for index in nearest]
d = {}
for cls in top_k_y:
d[cls] = d.get(cls,0) + 1
d_list = list(d.items())
d_list.sort(key=lambda x:x[1],reverse=True)
return np.array(d_list[0][0])
def __repr__(self):
return "KNN(k={})" .format(self.k)