KNN算法

2,033 阅读6分钟

参考文章:

目录

一.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