KNN - k 近邻算法(k-Nearest Neighbors)
先对 k 近邻算法里有个大概印象。
- 思想极度简单
- 应用数学知识少(近乎为零)
- 效果好(缺点?)
- 可以解释机器学习算法使用过程中的很多细节问题
- 更完整的刻画机器学习应用的流程
下面用一个肿瘤病人来举例说明 k 近邻算法。下图在一个二维空间中描述了肿瘤大小与时间的关系,红色表示良性肿瘤,蓝色表示恶性肿瘤。
上图描述了初始信息。如果又新来了一个病人,下图绿色的点表示,那么问题是我们怎么通过以上给出的信息判断这个病人是良性肿瘤还是恶性肿瘤?
此时,我们便要用到 k 近邻算法。首先我们得先取一个 k 值,假设 k = 3,接下来我们要做的就是找出离新的点最近的三个点。
然后,这些最近的点以它们自己的 label,自己的结果进行投票,换句话说,离它最近的三个点都是恶性肿瘤,所以恶心肿瘤:良性肿瘤 = 3:0,所以 k 近邻算法就说这个新的点有很大可能是蓝色,即该病人所患的是恶性肿瘤,这就叫做 k 近邻算法。
k 近邻算法的本质其实是认为两个样本它们足够的相似的话,它们就有更高的概率属于同一个类别,当然,如果我们只看离它最近的一个样本是不靠谱的,所以在这里我们多看几个样本,多看 k 个样本,看 k 个样本中那个样本所占的比例大,我们就认为新的样本最有可能属于哪种类别。在这里,我们描述两个样本的是否相似这个相似性就是看这两个样本在这个特征空间中的距离来进行描述的。
比如,我们再举一个例子,又新来了一个病人。依然用绿色的点进行标注。
那么,我们依然找出离它最近的三个点,那么对于这三个点来说,红色:蓝色 = 2:1,那么红色所占比例更大,所以该点是红色的几率要大些,即该病人有更大的几率为良性肿瘤。
到此,k 近邻算法的大致原理我们已经了解了是不是觉得 k 近邻算法比较简单呢。
通过示例,k 近邻算法首先可以解决的就是监督学习的分类问题。不过,k 近邻算法也可以解决回归问题,我们在后续再做介绍,下面我们尝试编码,来实现 k 近邻算法解决分类这一类问题。
补充:
欧拉距离
下面我们将使用函数来进行封装,然后使用魔法命令 %run
运行
# KNN.py
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"
distance = [ sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train]
nearest = np.argsort(distance)
topK_y = [ y_train[i] for i in nearest[:k] ]
votes = Counter(topK_y)
return votes.most_common(1)[0][0]
【运行】
我们再来回顾一下什么是机器学习,我们可以用下面一个流程图来表示。
在 kNN 算法中,大量的学习资料更加专业一点的叫法叫做训练数据集,在 kNN 算法中包括 X_train(数据相应的特征), y_train(数据所对应的标签)。机器学习算法训练出一个模型的过程叫做 fit(拟合)
,模型到输出结果的过程叫做 predict
。
我们会发现 kNN 算法并没有训练出模型鸭?事实上,确实如此。这可能也是 kNN 算法一个非常重要的特性,近乎可以说 kNN 是一个不需要训练过程的算法
。换句话说,输入样例可以直接输入训练数据集中,在训练数据集中找到离输入样例最近的 k 个样例,然后投票选出投票数最高的一个标签就是结果。
所以对于 k 近邻算法是非常特殊的,可以被认为是没有模型的算法。为了和其他算法统一,可以认为训练数据集就是模型本身。
scikit-learn中的kNN
看完了 scikit-learn中的kNN算法,下面我们重新整理我们的 kNN 代码
import numpy as np
from collections import Counter
from math import sqrt
class kNNClassifier:
def __init__(self, k):
"""初始化kNN分类器"""
assert k >= 1, "k must be valid"
self.k = k
self._X_tarin = None
self._y_train = None
def fit(self, X_train, y_train):
"""根据训练数据集X_train和y_train训练kNN分类器"""
self._X_train = X_train
self._y_train = y_train
return self
def predict(self, X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
y_predict = [self._predict(x) for x in X_predict]
return np.array(y_predict)
def _predict(self, x):
"""给定单个待预测数据x,返回x_predict的预测结果值"""
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
具体代码见Machine-Learning