KNN-k近邻算法
- k近邻算法基础
* - 解决分类问题
- 在Notebook中实现
– - 通过函数封装来实现
- scikit-learn 中的机器学习封装
* - 使用scikit-learn中的KNN
- 使用自己封装好的kNN
- 训练、测试数据集
* - 测试算法的准确率
- 编写我们自己的分离数据函数
- 使用sklearn中封装好的测试模型
- 分类准确度
* - sklearn中的accuracy_score
- 超参数
* - 寻找好的k
- 考虑到距离因素
- 网格搜索
- 数据归一化
* - 最值归一化 Normalization
– - 均值方差归一化 Standardization
– - scikit-learn中的Scaler
* - Notebook实现
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中
这里我们可以看到,Y的数据是很规律的,我们如果只选取前n个数据,得到的结果误差一定相当大。所以,我们需要先把数据打乱,再进行训练数据和测试数据的划分
打乱数据的索引
计算测试数据集的容量(这里定为总样本的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)的数据进行绘图查看一下
这里可以看到是手写数字0
接下来,计算我们训练好的模型的准确率就很简单了。
先将我们通过模型预测的结果集设为y_train,测试集对应结果命名为y_test。计算y_train == y_test 的个数,该个数与y_test总个数的比值就是我们要求的精确度比值了
代码如下:
import numpy as np
def accuracy_score(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库中的使用方法基本和我们自己实现的方法相似
; 超参数
超参数:在算法运行前需要决定的参数
模型参数:算法过程中学习的参数
其中,kNN算法没有模型参数,kNN算法中的k就是典型的超参数
我们想要寻找好的超参数,主要就由三个方面的因素决定:领域知识,经验知识和实验搜索。
接下来,我们使用模型搜索的方法来测试寻找较好的超参数
寻找好的k
这里,我们依旧选用手写数字数据集进行实验
首先,在sklearn库中载入手写数字数据,并做好准备工作
然后我们使用for循环,逐个在【1,10】中寻找效果最好的k就行了
这样,我们就找到了针对我们的模型的最好的k
注意:如果我们搜索出的k结果是10,也就是属于范围边缘,我们就需要扩大搜索范围,比如将范围扩大至【1,20】来搜索
; 考虑到距离因素
分类时,除了考虑按照最近距离的个数分类,有时我们还需要将距离也算进考虑范畴当中
比如此图中,我们就可以把预测点与红点划为一类,因为红色点最近,即占的比重最大
想要实现,我们只需要将函数中的参数更改一下即可
同样地,我们还可以更改参数p
网格搜索
网格搜索的原理很简单,就是将要想要查询的信息存放为字典,多个字典用列表存储再通过GridSearchCV测试就可以了
具体实现方法:
初始化的时候传入kNeighborsClassifier和param_grid
我们再用训练集来拟合一下
然后我们就可以查看最好的拟合效果,等结果
再将knn_clf运用我们刚刚得到的最佳效果训练参数,就可以达到最好的训练效果
GridSearchCV中还可以传入不同的参数(n_jobs控制运行的核的个数,verbose决定是否显示运行过程中的结果)
; 数据归一化
解决实际问题时,如果不统一一个度量标准,得到的结果差距会十分大。
比如我们举出肿瘤大小与发现天数之间的关系,如果我们对肿瘤发现的时间用不同的单位来统计:
这样我们再套入到kNN算法中,计算出的结果会有很大的差距。前者的发现时间占主导而后者是肿瘤大小占主导。
那么有什么方法能够让我们统一不同的量度呢?
接下来我们引入两个不同的计算方法
最值归一化 Normalization
原理:将所有数据都映射到同一尺度
结果:把所有数据映射到0和1之间
计算公式:
; Notebook实现
首先引入我们需要使用的模块
我们先随机生成一个二维向量
由于numpy默认生成的是int类型,精度还不够,我们需要强行转换为float类型
套入上述公式进行计算(维度多的化可以用for循环来写)
计算结果
绘图查看一下结果
该方法适用于以下情况:分布有明显边界
均值方差归一化 Standardization
原理:将所有数据都映射到同一尺度
结果:把所有数据映射到均值0方差为1的分布中
公式:(S表示方差)
; Notebook实现
同样的,我们先随机生成一个二维随机数据集
套入公式
绘图查看一下
上述方法适用于以下情况:
1)分布没有明显的边界
2)有可能存在极端数据值
scikit-learn中的Scaler
Notebook实现
先加载鸢尾花的数据并保存到X,y中
进行测试集和训练集的分离
从库中加载进StandardScaler
输入X_train进行拟合
将归一好的X_train进行替换
替换好的结果
同样地,X_test也要进行归一
再通过KNN看一下精确度
Original: https://blog.csdn.net/qq_53421929/article/details/122473803
Author: 柴可拉夫斯基
Title: KNN-k近邻算法
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/664657/
转载文章受原作者版权保护。转载请注明原作者出处!