KNN-k近邻算法
k近邻算法基础
解决分类问题
问题引入:
假设我们给出肿瘤大小作为横轴,发现肿瘤时间作为纵轴的坐标图。
其中红色代表为良性肿瘤,蓝色表示为恶性肿瘤:
现在,我们想根据新给出的一个肿瘤大小和发现时间的数据,来预测肿瘤是阳性还是阴性(图中绿色的点):
在这个问题中,我们需要通过已有的8个数据(红,蓝点的分布情况)来判断新给出的数据(绿点)是良性还是恶性肿瘤
想要用KNN解决分类问题也十分简单:
取绿色点周围最近的k(取值自选)个点,颜色相同数最多点的颜色即为最终结果
也就是说,k近邻的判断依据就是:两个样本如果足够相似,它们就有更高的几率属于同一个类别
在Notebook中实现
数据的准备
- 先创建样本数据数组 raw_data, raw_data_y
- 再将数据集转换为numpy中的array数组
- 将样本数据集与待预测数据绘制散点图
KNN过程
- 先计算所有样本数据点到待预测数据点的距离distances(向量)
其中,计算两点间的距离我们使用欧拉距离来计算:
- 再找出distances中距离中前k个最小的距离点的索引(这里将k=6)
- 然后通过Counter投票找出前k个中最多的结果,就是我们的分类结果
通过函数封装来实现
代码:
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"
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)
return votes.most_common(1)[0][0]
再通过Notebook中的魔法命令就可以直接使用了
scikit-learn 中的机器学习封装
设计机器学习的大致思想:
其中,对于KNN算法来说,喂入的训练集就是模型
使用scikit-learn中的KNN
引入sklearn包后首先需要传入设定的k值
再传入样本数据集进行拟合
进行预测前必须将需要预测的数据转化为矩阵形式传入,否则会报错
使用自己封装好的kNN
代码如下:
import numpy as np
from math import sqrt
from collections import Counter
class KNNClassifier:
def __init__(self, k):
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):
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
assert self.k <= X_train.shape[0], \
"the size of X_train must be at least k."
self._X_train = X_train
self._y_train = y_train
return self
def predict(self, 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):
assert x.shape[0] == self._X_train.shape[1], \
"the feature number of x must be equal to X_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
同样地,我们再使用魔法命令运行我们自己写的kNN
即可成功运行
训练、测试数据集
在实际问题中,除了设计出机器学习模型,我们还要去测试这种学习模型的准确率。
那么有什么方法能够判断出机器训练出的模型的准确率有多少呢?
我们可以从给出的训练数据集中抽出一部分,作为测试数据
根据这样的思想,我们可以测试一下我们自己刚刚编写的kNN的准确率
测试算法的准确率
这里我们使用sklearn中的鸢尾花数据集
首先加载鸢尾花数据集,将x,y特征值分别存入X,Y中
打乱数据的索引
计算测试数据集的容量(这里定为总样本的20%)
将训练与测试数据集索引分离
根据分离好的索引,分别求出x,y
编写我们自己的分离数据函数
代码如下:
import numpy as np
def train_test_split(X, y, test_ratio=0.2, seed=None):
assert X.shape[0] == y.shape[0], \
"the size of X must be equal to the size of y"
assert 0.0 <= test_ratio <= 1.0, \
"test_train must be valid"
if seed:
np.random.seed(seed)
shuffled_indexes = np.random.permutation(len(X))
test_size = int(len(X) * test_ratio)
test_indexes = shuffled_indexes[:test_size]
train_indexes = shuffled_indexes[test_size:]
X_train = X[train_indexes]
y_train = y[train_indexes]
X_test = X[test_indexes]
y_test = y[test_indexes]
return X_train, X_test, y_train, y_test
测试一下编写的代码:
同样可以根据这种原理,算出我们编写程序的准确率:
使用sklearn中封装好的测试模型
分类准确度
我们通过sklearn库中的手写数据库中的数据来验证一下算法的准确度
首先,我们引入sklearn的手写数据集
先用x,y分别接收一下数据
我们可以取出一部分数据查看一下
我们随意取出一个位置(索引为666)的数据进行绘图查看一下
接下来,计算我们训练好的模型的准确率就很简单了。
先将我们通过模型预测的结果集设为y_train,测试集对应结果命名为y_test。计算y_train == y_test 的个数,该个数与y_test总个数的比值就是我们要求的精确度比值了
代码如下:
import numpy as np
def accuracy_score(y_true, y_predict):
# 计算y_true和y_predict之间的准确率
assert y_true.shape[0] == y_predict.shape[0], \
"the size of y_true must be equal to the size of y_predict"
return sum(y_true == y_predict) / len(y_true)
我们在Notebook上运行一下
sklearn中的accuracy_score
sklearn库中的使用方法基本和我们自己实现的方法相似