近日在学习机器学习入门知识,整理了一些笔记,也加上了一些个人对它的理解,希望这篇文章能帮助大家,同时如果有错误或瑕疵,欢迎大家批评指正,一起学习,共同进步。
knn(k近邻算法)
k近邻算法,顾名思义,近邻,就是某个东西的离他比较近的东西,k是什么呢?k是数量,就是k个和它离得比较近的东西啦。
假如我们有个土豪同学叫小明,开着100万的车,戴着100万的手表,我们想分析一下小明的身价究竟是多少,我们可以从小明的其他朋友开始分析。这个时候,我们发现小明有四个和他情况很相近的朋友,他们也都开着80-150万的车,戴着100万左右的手表,他们每个人都是千万富翁,我们想,小明能和他们开差不多的车,戴着差不多的手表,所以小明就是一位差不多先生,呸,很有可能小明也是一位身价上千万的富翁。我们估计出了小明的身价。这也就是knn算法的基本思想啦。
在上面那个例子中,我们找了小明四个朋友,所以在knn算法中的k就是4了,我们考虑了小明朋友的两个维度的事情:开多少钱的车和戴多少钱的手表,在机器学习中,这两个维度的事情也叫作特征。我们可以把这些一组一组的特征组合起来,抽象成一个一个的点,再把需要预测的特征值也变成一个点,看这个在特征空间中与其他特征点的位置关系,找到k个离它最近的点,通过这些相邻点的类型来判断这个需要预测的数据的类型。
在小明的例子中,假如我们之前找到小明的那四个朋友是三个千万富翁和一个百万富翁呢,那我们预测小明究竟是千万级的还是百万级的呢?这个就像投票一样,千万富翁三票,百万富翁一票,多者获胜,所以预测小明是千万富翁。
关于K值:我们不难发现,这个k的取值是会直接影响到这个算法的准确度的,所以取多少是有讲究的。以及假如“平票”的时候该怎么办呢?
这涉及到knn算法的超参数知识点,这篇文章中主要介绍基本思想,就先不谈啦,有机会再整理与超参数相关的文章一起探讨,超参数也是很重要的。
小小总结一下:
Knn算法是先用已知训练数据集(比如小明的所有朋友们)组成一个模型,在模型中,每组特征信息变成一个点,散列地表示在特征空间中,表示完以后,就可以对新进来的数据进行预测了。新的数据也将它的特征信息变成一个点,查找离这个新数据最近的k个点,假如k为3的话,就是查找离这个新数据最近的3个点(这三个点都是之前训练数据集中的点),这三个点如果都是类型A的话,那么这个新数据也会被预测成类型A,如果这三个点既有类型A又有类型B,且类型A的点大于类型B的点,则也会被预测成类型A,即票数多的获胜。
所以knn算法它的功能,是可以去对给定特征值的条件下来进行对它的分类预测的(小明就被分到了千万富翁的类别中)。
好了我们再来个更加实际点的例子:
此次使用肿瘤预测的例子,根据肿瘤大小和发现时间来预测肿瘤是良性还是恶性。
只使用肿瘤的两个特征值:
1.肿瘤发现的时间
2.肿瘤的大小
所以构建出来的特征空间是一个二维的空间,方便我们的理解。
一、 首先用训练集训练出我们的模型:
- 使用已知的肿瘤信息来作为训练集,构建模型,来对新的肿瘤进行预测。
其中:train_x就是已知的肿瘤发现时间和大小,train_y就是对应的肿瘤是良性还是恶性(使用索引进行一一对应),比如索引为1的肿瘤(即第2个肿瘤)的信息是,发现时间:1.1年,大小1.3,类型为良性。
可以看到肿瘤的信息也是按照一个向量或者叫1×2的矩阵的形式表现的,可以画出这十个肿瘤的两个特征值(肿瘤大小和发现时间)构建而成二维向量空间,如图所示:
绿色的点为良性的肿瘤,红色的为恶性肿瘤
二、 预测肿瘤
有了这些数据分布,如果新来了一些肿瘤数据,就可以看看这些新肿瘤分布在那个位置,离那些比较近,来判断它是恶性还是良性了
这是新来的两个肿瘤信息,新的肿瘤信息如下:
我们现在需要预测它的类别,先看看他在特征空间中的位置(用蓝色的点表示)
可以看到第一个肿瘤信息离绿色的点比较近,所以可能是良性肿瘤。第二个离红色比较静所以可能是恶性肿瘤。但是这是我们肉眼看出来的,如何在代码中实现呢?,按照knn的算法,需要找到离这个点最近的k个点,然后根据这k个点的类型谁多谁少来确定它是什么类型的。
这时问题来了,如何知道两个点之间的距离呢,此次例子中只有两个特征值,所以特征空间是一个二维的平面,在平面中求两点的距离自然可以用到我们之前学的两点距离公式(根号下x0-x1的平方加上y0-y1的平方)。但是我们之前学的只能求二维平面中两点的距离,实际情况中特征的维度可高达上百上千上万都有可能,在这么多维度下用什么公式计算两点距离呢?当然我们可以把之前学的在二维平面上求两点距离的公式推广一下,得到一个与之相关的新的求两点距离公式,也就是欧拉公式:我们此次就用它了(当然此次用之前学的两点距离公式也可以):
假设需要预测的肿瘤特征为px则它与其他肿瘤特征点的距离为:
distances = [ sqrt( np.sum((i - px) ** 2)) for i in self.train_x]
有了距离就可以筛选出离它比较近的肿瘤了,大致思路是这样,我把算法做了简单的封装,文章的最后我会给出来。
我运行了自己写的简易版knn算法,并设k值为3,得到这两个肿瘤信息:第一个肿瘤结果是0(良性),第二个结果为1(恶性)。
好了,文章的文字内容大概就是这些,这里我贴出我写的简易版knn算法加上肿瘤预测案例的代码:
from collections import Counter
from math import sqrt
import numpy as np
class Mknn():
def __init__(self, train_x, train_y, predict_x):
self.train_x = train_x
self.train_y = train_y
self.predict_x = predict_x
def _fit(self, k, px):
distances = [sqrt(np.sum((i - px) ** 2)) for i in self.train_x]
dis_sort = np.argsort(distances)
top_k_y = [self.train_y[i] for i in dis_sort[:k]]
votes = Counter(top_k_y)
predict_y = votes.most_common(1)[0][0]
return predict_y
def predict(self, k):
predict_y = []
for i in self.predict_x:
predict_y.append(self._fit(k, i))
return np.array(predict_y)
if __name__ == '__main__':
# 训练集: 用已知的数据构建模型。第一列表示肿瘤发现时间, 第二列表示肿瘤大小
train_x = np.array([
[1, 1.2],
[1.1, 1.3],
[1.2, 1.2],
[1.15, 1],
[1.6, 1.5],
[1.5, 2.3],
[1.6, 2.8],
[2, 2.95],
[2.2, 3.2],
[2, 2.5],
])
# 训练结果集分类 0:良性 1:恶性
train_y = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1])
# 需要预测的肿瘤,这里选了两个需要预测的肿瘤
# 其中第一个肿瘤的发现时间是1.2年,大小是1.9、第二个肿瘤发现时间是1.6年,大小是2.3
predict_x = np.array([
[1.2, 1.9],
[1.6, 2.3]
])
# 开始预测
m = Mknn(train_x, train_y, predict_x)
# 得到那两个肿瘤预测的结果
predict_y = m.predict(3)
# 显示结果 :0:良性 , 1:恶性
result_ls = ['良性', '恶性']
print([result_ls[i] for i in predict_y])
感谢大家的观看,祝生活愉快。