本文github 代码:https://github.com/crystal30/KNN
一. # KNN algorithm 原理
KNN:k-nearest neighbors algorithm,
(1)用途:分类,且天然的可以解决多分类问题(sklearn.neighbors.KNeighborsClassifier)
回归(sklearn.neighbors.KNeighborsRegressor)
(2)优点:实现简单,易于理解
(3)缺点:
效率低下:训练集共有m个样本,每个样本有n个特征,则预测每一个新的数据需要O(m*n)
优化:使用树的结构:KD-Tree,Ball-Tree
高度数据相关:
预测的结果不具有可解释性,其只是根据距离来判断
维数灾难:
解决方法:降维(后续后涉及)。
1分类的原理:
1.1
如上图所示:
场景描述:数据有红、蓝两类,此时进来一个新的数据(绿色表示),用KNN算法来预测绿色属于哪一类,其中K=3
假设:上图的点的坐标都是已知的,
算法步骤:(1)计算绿点到各个点(所有黄点和蓝点,我们也称为训练数据)的距离(一般使用欧式距离)
(2)K=3,根据距离得到离绿点最近的3个点
(3)统计这三个点中,哪一类的数据最多,则绿点就属于哪一类(如上图,离绿点最近的三个点中,有两个是红点,一个点是蓝点,则可以判定绿点属于红色的那一类)
1.2 利用 sklearn datasets中鸢尾花的数据集 KNN的简单的实现
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
import math
from collections import Counter
iris = datasets.load_iris() #加载鸢尾花的数据到 iris中
iris.keys()
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])
Row_ind = np.arange(0,100,10) #array([ 0, 10, 20, 30, 40, 50, 60, 70, 80,90])
#为简单起见,我们从第一类和第二类鸢尾花中各取出5个数据
X_train = iris.data[Row_ind,2:]
y_train = iris.target[Row_ind]
#绘图
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color = 'r')
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color = 'b')
plt.show()
new = iris.data[56,2:] #新进入一个点,预测这个点属于哪一类
distances = np.array([math.sqrt(i) for i in np.sum((X_train -new)**2,axis=1)]) # 计算出new 到每个点的欧式距离
#distances 的输出: array([3.58468967, 3.49284984,3.31058907, 3.40147027, 3.64005494,
0.2 , 1.34164079, 0.2236068 , 1.02956301,0.5 ])
nearest = distances.argsort() #将距离从小大大排序,取出arg
k=7 #取最临近的7个点
top_y = y_train[nearest[:k]]
votes = Counter(top_y) #统计top_y 中的数据
#votes输出: Counter({0: 2, 1: 5})
predict_newy = votes.most_common(1)[0][0] #输出统计结果中最common的lable
二 KNN算法的封装
1.将上述的实现过程整理为一个函数 KNN(X_train,y_train,newx,k),并保存在KNN.py文件中,以便我们后续调用。
2.sklearn中KNeighborsClassifier 算法的使用
一般,sklearn 中的机器学习算法,都是以面向对象的方式进行封装。以KNeighborsClassifier 为例子,均有以下几个步骤
(1) 创建实例 knn_clf = KNeighborsClassifier(n_neighbors=7)
(2) 拟合:knn_clf.fit(X_train,y_train)
(3)预测:knn_clf.predict(new)
3.将我们编写的KNN算法也以面向对象的方式进行封装,即创建类:KNNClf, 并将其存放在KNNClf.py中
具体的代码见github:https://github.com/crystal30/KNN
三. KNN根据算法的性能 设置超参数
1.性能一般用Accuracy来测量:
(2)假设:我们的测试数据为:X_test, 测试数据的标签为 y_test, 用算法预测的标签为y_predict
accuracy = 预测的正确的数量/总的测试样本数
用程序来写就是 accuracy = sum(y_predict == y_test)/len(y_test)
代码:test accuracy1.ipynb 以及代码的封装: accaracy 封装_load digits2.ipynb
Q:在模型进入真实环境前,怎样改进模型呢?
可以通过设置合理的超参数 来是模型的测试误差最小
2.超参数:
超参数:即指 在运行机器学习算法之前需要指定的参数,KNN中的K即为典型的超参数。
调参:这里的“参”,一般指的就是超参数
(更优)超参数的寻找:领域知识
经验数值(算法中默认的值一般可认为是经验数值)
实验搜索
1.1KNN模型中超参数的设置
(1)KNN算法没有模型参数,KNN算法中的K是典型的超参数
根据
class sklearn.neighbors.
KNeighborsClassifier
(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=1, **kwargs)
我们初步的来设置n_neighbors(即KNN中的K),weights,p 这几个超参数
具体的每个参数的含义可查询document
http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier
[source]
(2)weights:考虑距离权重,即把每个点到要预测数据的距离的倒数作为权重。某个点距离预测点越近,则权重越大。
(3)p:距离算法的选择,上面我们一直用的是欧式距离,用曼哈顿距离,性能会不会更好呢?
欧式距离:,变形
曼哈顿距离: , 变形
明可夫斯基距离:
这样我们对使用哪种距离的选择就变成了对超参数p的选择。
注意:欧式距离和曼哈顿距离都是明可夫斯基距离的一种,KNeighborsClassifier 中metric 默认的是’minkowski’。
KNeighborsClassifier
(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=1, **kwargs)
当然还有很多其他的距离,如
在这里,我们不予考虑。
代码:search Hyper-parameter 3.ipynb
1.2 利用sklearn中的模块对KNN模型中超参数的选择——网格搜索
from sklearn.model_selection import GridSearchCV
详见github代码:Grid_Search_HyperParameters4
四.训练数据和测试数据的归一化
Q:为什么我们要对数据归一化呢?下面我们来做一个简单的示例。
原始数据:
如上图所示:假设我们有两个样本,每个样本有两个特征:肿瘤大小和发现时间。
如果再来一个新的测试数据,计算其到样本1,样本2的距离,则特征肿瘤大小的贡献微乎其微,但特征肿瘤大小对 判断是否是恶性肿瘤是一个很重要的因素,此时,就需要我们对数据归一化,即将肿瘤大小和发现时间归一化在同 一个量级,而样本间同一个特征值的相对大小不变。
数据归一化后:
这样再算测试数据到样本间距离时,将不由某一个特征的主导。
1.数据标准化的理论知识
数据归一化,即将所有的数据映射到同一尺度。如上实例所示,我们是将肿瘤大小和发现时间映射到了同一尺度。
1.1 将特征数据缩放到一个范围 scale to a range
使用这一方法的情况一般有两种:
- 特征的标准差较小
- 可以使稀疏数据集中的0值继续为0,特别适用于缩放稀疏数据。
(1)最值归一化(MinMaxScaler):把所有的数据映射到0-1之间。适用于分布有明显边界的情况(如:考试成绩)
缺点:容易受边界极端值的影响。
(2) MaxAbsScaler,标准化后的数据的取值范围为[-1, 1]
1.2 均值方差归一化(standardization)
把所有的数据归一到均值为0,方差为1的分布中。数据分布没有明显的边界,有可能存在极端数据值。
均值方差归一化,通常也称z-score标准化,即将数据转化成均值为0方差为1的高斯分布,但是对于不服从标准正态分布的特征,这样做效果会很差。
1.3 RobustScaler
Scale features using statistics that are robust to outliers.
2. 标准化与归一化的区别
标准化是依照特征矩阵的列处理数据,比如通过求z-score的方法,将样本的特征值转换到同一量纲下。归一化是依照特征矩阵的行处理数据,其目的在于样本向量在点乘运算或其他核函数计算相似性时,拥有统一的标准,也就是说都转化为“单位向量”。常用于文本分类和内容聚类的向量空间模型。
规则为l2的归一化公式如下:
可使用sklearn.preprocessing库的Normalizer类对数据进行归一化。
3.数据归一化的简单程序实现
见github代码:data_X_train_Normalize5
4. 对sklearn.datasets 中的数据进行归一化
from sklearn.preprocessing import StandardScaler 实现数据归一化
from KNNProject.preprocessing import StandardScaler 实现数据归一化(KNNProject 为本地自己编写的包)
(1)一般,我们有训练数据集,和测试数据集,对这两个数据集都需要归一化。而我们往往不知道下一个测试数据会是什么,即测试数据我们不能完整的获得。
所以:
归一化的过程:
github代码:skl_local_normalize6
标准化后与标准化之前的预测准确率相比,有明显的改进。