参考文章:
- 一文搞懂k近邻(k-NN)算法
- KNN算法的算法思想
- 慕课网《Python3入门教程》
目录
一.KNN算法
1.1 什么是KNN算法
KNN算法,也叫K近邻算法,是一种是一种基本分类和回归方法。
KNN算法的原理:给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例,这K个实例的多数属于某个类,就把该输入实例分类到这个类中。
根据以往的经验,肿瘤良性恶性和肿瘤发现时间之间的关系如上图所示。其中红色表示良性,蓝色表示恶性。现在有这么一个病人,他的肿瘤发现时间如图上绿色(红绿色盲症的朋友们,对不住~~~)所示,那么他更有可能是良性还是恶性呢?
- 取K = 3,也就是离它最近的3个点,发现都是蓝色,那么很不幸的,这位同志很有可能患有恶性肿瘤。
1.2 KNN算法的核心和原则
核心:物以类聚,人以群分
原则:少数服从多数
二. KNN算法实现
2.1 KNN算法实战
KNN算法的核心就是计算待预测点附近的K个点,求他们中占比最大的分类。
所以,KNN算法的求解步骤应该是:
- 计算所有点距离待预测点(我们叫做x)的距离
- 取出距离最近的k个点
- 计算各个分类的占比
- 取出占比最大的那个分类
任务: 通过给定的x点,预测它的y值
- 第一步:造假训练集
import numpy as np
import matplotlib.pyplot as plt
raw_data_x = [[3.9353233211,2.331273381],
[3.11007483,1.23792372380],
[1.9723425435,3.234256432],
[2.36675576541,4.23347654],
[7.45465765877655,4.236588767]]
raw_data_y =[0,0,1,1,1]
X_train = np.array(raw_data_x)
y_train = np.array(raw_data_y)
准备工作完成,我们现在有两个数组,可以进行绘制散点图。
- 第二步:绘制散点图
代码:
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color='g')
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color='r')
plt.show()
回车后,可以看到散点图如下:
OK。现在我们加入我们要进行预测的点。
# 定义一个点,假设这是我们要进行猜测的点
x= np.array([8.0293657928,3.8928785])
绘图在图上:
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color='g')
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color='r')
plt.scatter(x[0],x[0],color='b')
plt.show()
- 第三步:计算距离
接下来,我们要进行计算预测的点和图上各个点的距离。依据我们伟大的欧拉公式:
接下来,反手就是算:
from math import sqrt
distance=[]
for x_train in X_train:
d = sqrt(np.sum((x_train - x) ** 2))
distance.append(d)
distance
当然,更有经验的小朋友肯定知道上面这段代码可以简写成:
# 更简便的写法
distance = [sqrt(np.sum((x_train -x)** 2)) for x_train in X_train]
反正渣渣我是不懂~~
回车后,我们可以看到运算结果:
- 第四步:排序并预测结果
结果计算出来后,我们要进行排序,并返回它的预测结果:
# 进行排序,并返回索引
nearest = np.argsort(distance)
k = 3
# 获取每个索引对应的y值
topK_y =[y_train[i] for i in nearest]
topK_y
得到结果:
接下来,我们要预测,预测,也就是说看看当前的点和得到哪些点的票数最多,所以我们需要进行投票统计啦。
from collections import Counter
votes = Counter(topK_y)
votes
得到结果:
从图中可以看到,1有3票,0有2票,很明显,预测点的y值很有可能是1,而不是0。 当然,我们可以让程序自己输出预测值:
predict_y = votes.most_common(1)[0][0]
predict_y
结果:
可以看到,结果是1,跟我们预测的一样~~
2.2 自己封装KNN算法
- 创建一个kNN.py文件,定义KNN算法
# 定义了一个方法,kNN_classify
def kNN_classify(k,X_train,y_train,x):
# k要大于1,小于矩阵的大小
assert 1 <= k <= X_train.shape[0], "k must be valid"
# x,y的大小要相同
assert X_train.shape[0] == y_train.shape[0],\
"the size of X_train must equal to the siz of y_train"
# 待预测矩阵要和x相同
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]
# 返回预测的y值
return votes.most_common(1)[0][0]
- 到Jupyter Notebook中调用封装的方法
%run kNN_function/kNN.py
# 引入我们上节课的数据
raw_data_x = [[3.9353233211,2.331273381],
[3.11007483,1.23792372380],
[1.9723425435,3.234256432],
[2.36675576541,4.23347654],
[7.45465765877655,4.236588767]]
raw_data_y =[0,0,1,1,1]
# 这是我们要进行猜测的点
x= np.array([8.0293657928,3.8928785])
predict_y = kNN_classify(6,X_train,y_train,x)
#打印预测的y值
predict_y
至此,我们已经完成了的KNN算法的封装。
2.3 scikit-learn中KNN算法的封装
在看scikit-learn的KNN算法之前,先来看一下什么是机器学习。
机器学习就是 输入训练集进行训练,然后输出模型,通过模型来对样例进行预测的过程。
However,对于kNN算法来说,训练集就是模型。这一点是kNN算法跟其他算法不太一样的地方。
为什么这里要插播机器学习的东西呢?
因为等会我们就会看到,scikit-learn中封装的各种机器学习算法,暴露出现的方法无非就是这几个:
- fit(参数) -> 训练模型
- predict(参数) -> 预测结果
- score() -> 衡量算法准确度
OK,回到正题。
scikit-learn中封装了KNN算法,叫KNeighborsClassifier。接下来来学习该如何调用它。
from sklearn.neighbors import KNeighborsClassifier
kNN_classifier = KNeighborsClassfier(n_neighbors = 6)
#进行训练,输出模型,这个模型会保存在这个kNN_classifier对象中
kNN_classifier.fit(X_train,y_train)
#基于模型进行预测
#这里要进行reshape 是因为编译器会进行警告,大家可以换成predict(x)看一下警告内容
y_predict = kNN_classifier.predict(x.reshape([1,-1])
# y_predict是一个数组,里面保存了预测的结果,因为我们只要预测一个值,所以我们取第0个
y_predict[0]
# 至此,调用过程结束
2.4 自己再封装一次KNN算法
为什么要再封装一次KNN算法?因为,为了更好的理解scikit-learn中算法的封装思想,提高代码质量呀~~
我们刚刚封装算法是用面向过程的思想写的,而是scikit-learn中kNN算法是用面向对象的思想写的,现在我们来改造一下。
import numpy as np
from math import sqrt
from collections import Counter
class KNNClassifier:
def _init(self, k):
"""初始化KNN分类器"""
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):
"""根据训练集X_train和y_train训练kNN分类器"""
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must equal to the siz of y_train"
assert X_train.shape[1] == x.shape[0], \
"the feature number of x must be equal to X_train"
"""我们前面说过,kNN模型比较特殊,它的模型就是它的训练集,所以这里直接赋值了~"""
self._X_train = X_train
self._y_train = y_train
return self
def predict(self, X_predict):
"""给定待预测数据集X_predict,返回表示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):
"""给定单个带预测数据x,返回x的预测结果值"""
assert x.shape[0]== self._X_train.shape[1],\
"the feature number of x must be equal to 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
接下来,老规矩,验证一下:
%run kNN/kNN.py
knn_clf = KNNClassifier(k=6)
knn_clf.fit(x_train,y_train)
y_predict = knn_clf.predict(x_predict)
y_predict[0]
em ~~~~ 累了,不想写了。先这样吧,下次再更。
哈?你问我啥时更?我也不知道,看心情,略略略O(∩_∩)O