根据你的“邻居”来推断出你的类别
如果⼀个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的⼤多数属于某⼀个类别,则该样本也属于这个 类别。
两个样本的距离可以通过如下公式计算,⼜叫欧式距离 ,关于距离公式会在后⾯进⾏讨论
1)计算已知类别数据集中的点与当前点之间的距离
2)按距离递增次序排序
3)选取与当前点距离最⼩的k个点
4)统计前k个点所在的类别出现的频率
5)返回前k个点出现频率最⾼的类别作为当前点的预测分类
机器学习流程:
1.获取数据集
2.数据基本处理
3.特征⼯程
4.机器学习
5.模型评估
Python语⾔的机器学习⼯具
Scikit-learn包括许多知名的机器学习算法的实现
Scikit-learn⽂档完善,容易上⼿,丰富的API
⽬前稳定版本0.19.1
注:安装scikit-learn需要Numpy, Scipy等库
pip3 install scikit-learn==0.19.1
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)
n_neighbors:int,可选(默认= 5),k_neighbors查询默认使⽤的邻居数
在机器学习过程中,对于函数 dist(., .),若它是⼀"距离度量" (distance measure),则需满⾜⼀些基本性质:
在曼哈顿街区要从⼀个⼗字路⼝开⻋到另⼀个⼗字路⼝,驾驶距离显然不是两点间的直线距离。这个实际驾驶距离就是 “曼哈顿距离”。曼哈顿距离也称为“城市街区距离”(City Block distance)。
国际象棋中,国王可以直⾏、横⾏、斜⾏,所以国王⾛⼀步可以移动到相邻8个⽅格中的任意⼀个。国王从格⼦(x1,y1) ⾛到格⼦(x2,y2)最少需要多少步?这个距离就叫切⽐雪夫距离。
闵⽒距离不是⼀种距离,⽽是⼀组距离的定义,是对多个距离度量公式的概括性的表述。 两个n维变量a(x11,x12,…,x1n)与b(x21,x22,…,x2n)间的闵可夫斯基距离定义为:
根据p的不同,闵⽒距离可以表示某⼀类/种的距离
⼩结:
我们常将属性划分为 “连续属性” (continuous attribute)和 “离散属性” (categorical attribute),前者在定义域上有⽆穷多个 可能的取值,后者在定义域上是有限个取值.
根据KNN每次需要预测⼀个点时,我们都需要计算训练数据集⾥每个点到这个点的距离,然后选出距离最近的k个点进⾏投票。当数据集很⼤时,这个计算成本⾮常⾼,针对N个样本,D个特征的数据集,其算法复杂度为O(DN )。 kd树:为了避免每次都重新计算⼀遍距离,算法会把距离信息保存在⼀棵树⾥,这样在计算之前从树⾥查询距离信息, 尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息, 就可以在合适的时候跳过距离远的点。 这样优化后的算法复杂度可降低到O(DNlog(N))。感兴趣的读者可参阅论⽂:Bentley,J.L.,Communications of the ACM(1975)。 1989年,另外⼀种称为Ball Tree的算法,在kd Tree的基础上对性能进⼀步进⾏了优化。
⻩⾊的点作为根节点,上⾯的点归左⼦树,下⾯的点归右⼦树,接下来再不断地划分,分割的那条线叫做分割超平⾯ (splitting hyperplane),在⼀维中是⼀个点,⼆维中是线,三维的是⾯。
⻩⾊节点就是Root节点,下⼀层是红⾊,再下⼀层是绿⾊,再下⼀层是蓝⾊。
1.树的建⽴;
2.最近邻域搜索(Nearest-Neighbor Lookup)
kd树(K-dimension tree)是⼀种对k维空间中的实例点进⾏存储以便对其进⾏快速检索的树形数据结构。kd树是⼀种⼆叉 树,表示对k维空间的⼀个划分,构造kd树相当于不断地⽤垂直于坐标轴的超平⾯将K维空间切分,构成⼀系列的K维超 矩形区域。kd树的每个结点对应于⼀个k维超矩形区域。利⽤kd树可以省去对⼤部分数据点的搜索,从⽽减少搜索的计
类⽐“⼆分查找”:给出⼀组数据:[9 1 4 7 2 5 0 3 8],要查找8。如果挨个查找(线性扫描),那么将会把数据集都遍历 ⼀遍。⽽如果排⼀下序那数据集就变成了:[0 1 2 3 4 5 6 7 8 9],按前⼀种⽅式我们进⾏了很多没有必要的查找,现在 如果我们以5为分界点,那么数据集就被划分为了左右两个“簇” [0 1 2 3 4]和[6 7 8 9]。 因此,根本就没有必要进⼊第⼀个簇,可以直接进⼊第⼆个簇进⾏查找。把⼆分查找中的数据点换成k维数据点,这样 的划分就变成了⽤超平⾯对k维空间的划分。空间划分就是对数据点进⾏分类,“挨得近”的数据点就在⼀个空间⾥⾯。
(1)构造根结点,使根结点对应于K维空间中包含所有实例点的超矩形区域;
(2)通过递归的⽅法,不断地对k维空间进⾏切分,⽣成⼦结点。在超矩形区域上选择⼀个坐标轴和在此坐标轴上的⼀ 个切分点,确定⼀个超平⾯,这个超平⾯通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个 ⼦区域(⼦结点);这时,实例被分到两个⼦区域。
(3)上述过程直到⼦区域内没有实例时终⽌(终⽌时的结点为叶结点)。在此过程中,将实例保存在相应的结点上。
(4)通常,循环的选择坐标轴对空间切分,选择训练实例点在坐标轴上的中位数为切分点,这样得到的kd树是平衡的 (平衡⼆叉树:它是⼀棵空树,或其左⼦树和右⼦树的深度之差的绝对值不超过1,且它的左⼦树和右⼦树都是平衡⼆ 叉树)。
KD树中每个节点是⼀个向量,和⼆叉树按照数的⼤⼩划分不同的是,KD树每层需要选定向量中的某⼀维,然后根据这 ⼀维按左⼩右⼤的⽅式划分数据。在构建KD树时,关键需要解决2个问题:
(1)选择向量的哪⼀维进⾏划分;
(2)如何划分数据;
第⼀个问题简单的解决⽅法可以是随机选择某⼀维或按顺序选择,但是更好的⽅法应该是在数据⽐较分散的那⼀维进⾏ 划分(分散的程度可以根据⽅差来衡量)。
第⼆个问题中,好的划分⽅法可以使构建的树⽐较平衡,可以每次选择中位数来进⾏划分。
(1)思路引导:
根结点对应包含数据集T的矩形,选择x(1)轴,6个数据点的x(1)坐标中位数是6,这⾥选最接近的(7,2)点,以平⾯x(1)=7 将空间分为左、右两个⼦矩形(⼦结点);接着左矩形以x(2)=4分为两个⼦矩形(左矩形中{(2,3),(5,4),(4,7)}点的x(2)坐 标中位数正好为4),右矩形以x(2)=6分为两个⼦矩形,如此递归,最后得到如下图所示的特征空间划分和kd树。
假设标记为星星的点是 test point, 绿⾊的点是找到的近似点,在回溯过程中,需要⽤到⼀个队列,存储需要回溯的点,在判断其他⼦节点空间中是否有可能有距离查询点更近的数据点时,做法是以查询点为圆⼼,以当前的最近距离为 半径画圆,这个圆称为候选超球(candidate hypersphere),如果圆与回溯点的轴相交,则需要将轴另⼀边的节点都放 到回溯队列⾥⾯来。
样本集{(2,3),(5,4), (9,6), (4,7), (8,1), (7,2)}
在(7,2)点测试到达(5,4),在(5,4)点测试到达(2,3),然后search_path中的结点为<(7,2),(5,4), (2,3)>,从search_path中 取出(2,3)作为当前最佳结点nearest, dist为0.141; 然后回溯⾄(5,4),以(2.1,3.1)为圆⼼,以dist=0.141为半径画⼀个圆,并不和超平⾯y=4相交,如上图,所以不必跳到结 点(5,4)的右⼦空间去搜索,因为右⼦空间中不可能有更近样本点了。
于是再回溯⾄(7,2),同理,以(2.1,3.1)为圆⼼,以dist=0.141为半径画⼀个圆并不和超平⾯x=7相交,所以也不⽤跳到结 点(7,2)的右⼦空间去搜索。 ⾄此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2.1,3.1)的最近邻点,最近距离为0.141。
在(7,2)处测试到达(5,4),在(5,4)处测试到达(4,7)【优先选择在本域搜索】,然后search_path中的结点为<(7,2),(5,4), (4,7)>,从search_path中取出(4,7)作为当前最佳结点nearest, dist为3.202;
然后回溯⾄(5,4),以(2,4.5)为圆⼼,以dist=3.202为半径画⼀个圆与超平⾯y=4相交,所以需要跳到(5,4)的左⼦空间去 搜索。所以要将(2,3)加⼊到search_path中,现在search_path中的结点为<(7,2),(2, 3)>;另外,(5,4)与(2,4.5)的距离为 3.04 < dist = 3.202,所以将(5,4)赋给nearest,并且dist=3.04。
回溯⾄(2,3),(2,3)是叶⼦节点,直接平判断(2,3)是否离(2,4.5)更近,计算得到距离为1.5,所以nearest更新为(2,3), dist更新为(1.5)
回溯⾄(7,2),同理,以(2,4.5)为圆⼼,以dist=1.5为半径画⼀个圆并不和超平⾯x=7相交, 所以不⽤跳到结点(7,2)的右⼦ 空间去搜索。
⾄此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2,4.5)的最近邻点,最近距离为1.5。
https://blog.csdn.net/qq_39759664/article/details/123459250
https://blog.csdn.net/qq_39759664/article/details/123459848
https://blog.csdn.net/qq_39759664/article/details/123461741
https://blog.csdn.net/qq_39759664/article/details/123462661
https://blog.csdn.net/qq_39759664/article/details/123469674
通常我们假设测试样本也是从样本真实分布中独⽴同分布采样⽽得。但需注意的是,测试集应该尽可能与训练集互斥。
互斥,即测试样本尽量不在训练集中出现、未在训练过程中使⽤过。
测试样本为什么要尽可能不出现在训练集中呢?为理解这⼀点,不妨考虑这样⼀个场景:。
⽼师出了10道习题供同学们练习,考试时⽼师⼜⽤同样的这10道题作为试题,这个考试成绩能否有效反映出同学 们学得好不好呢?
答案是否定的,可能有的同学只会做这10道题却能得⾼分。
通过对D进⾏适当的处理,从中产⽣出训练集S和测试集T
“留出法”(hold-out)直接将数据集D划分为两个互斥的集合,其中⼀个集合作为训练集S,另⼀个作为测试集T
在S上训练出模型后,⽤T来评估其测试误差,作为对 泛化误差的估计。
需注意的是,训练/测试集的划分要尽可能保持数据分布的⼀致性,避免因数据划分过程引⼊额 外的偏差⽽对最终结果产⽣影响,例如在分类任务中⾄少要保持样本的类别⽐例相似。
如果从采样( sampling)的⻆度来看待数据集的划分过程,则保留类别⽐例的采样⽅式通常称为“分层采样”( stratified sampling)。
例如通过对D进⾏分层样⽽获得含70%样本的训练集S和含30%样本的测试集T,
若D包含500个正例、500个反例,则分层采样得到的S应包含350个正例、
350个反例,⽽T则包含150个正例和 150个反例;
若S、T中样本类别⽐例差别很⼤,则误差估计将由于训练/测试数据分布的差异⽽产⽣偏差。
另⼀个需注意的问题是,即便在给定训练测试集的样本⽐例后,仍存在多种划分⽅式对初始数据集D进⾏分割。 例如在上⾯的例⼦中,可以把D中的样本排序,然后把前350个正例放到训练集中,也可以把最后350个正例放到训练集 中,这些不同的划分将导致不同的训练/测试集,相应的,模型评估的结果也会有差别。
因此,单次使⽤留出法得到的估计结果往往不够稳定可靠,在使⽤留出法时,⼀般要采⽤若⼲次随机划分、重复进⾏实 验评估后取平均值作为留出法的评估结果。
此外,我们希望评估的是⽤D训练出的模型的性能,但留出法需划分训练/测试集,这就会导致⼀个窘境:
from sklearn.model_selection
import train_test_split
#使⽤train_test_split划分训练集和测试集
train_X , test_X, train_Y ,test_Y = train_test_split( X, Y, test_size=0.2,random_state=0)
留⼀法( Leave-One-Out,简称LOO),即每次抽取⼀个样本做为测试集。 显然,留⼀法不受随机样本划分⽅式的影响,因为m个样本只有唯⼀的⽅式划分为m个⼦集⼀每个⼦集包含个样本; 使⽤Python实现留⼀法:
import numpy as np
from sklearn.model_selection import LeaveOneOut,KFold,StratifiedKFold
#留一法
data=[1,2,3,4]
loo=LeaveOneOut()
for train,test in loo.split(data):
print("%s,%s" % (train,test))
留⼀法优缺点:
“交叉验证法”( cross validation)先将数据集D划分为k个⼤⼩相似的互斥⼦集,每个⼦集Di都尽可能保持数据分布的⼀致性,即从D中通过分层抽样得到。
然后,每次⽤k-1个⼦集的并集作为训练集,余下的那个⼦集作为测试集;这样就可获得k组训练/测试集,从⽽可进⾏k 次训练和测试,最终返回的是这k个测试结果的均值。
显然,交叉验证法评估结果的稳定性和保真性在很⼤程度上取决于k的取值,为强调这⼀点,通常把交叉验证法称为“k 折交叉验证”(k- fold cross validation)。k最常⽤的取值是10,此时称为10折交叉验证;其他常⽤的k值有5、20等。下图 给出了10折交叉验证的示意图。
与留出法相似,将数据集D划分为k个⼦集同样存在多种划分⽅式。为减⼩因样本划分不同⽽引⼊的差别,k折交叉验证 通常要随机使⽤不同的划分重复p次,最终的评估结果是这p次k折交叉验证结果的均值,例如常⻅的有 “10次10折交叉 验证”。
交叉验证实现⽅法,除了咱们前⾯讲的GridSearchCV之外,还有KFold, StratifiedKFold
KFold和StratifiedKFold
from sklearn.model_selection import KFold,StratifiedKFold
import numpy as np
from sklearn.model_selection import LeaveOneOut,KFold,StratifiedKFold
#留一法
# data=[1,2,3,4]
# loo=LeaveOneOut()
# for train,test in loo.split(data):
# print("%s,%s" % (train,test))
#交叉验证---KFlod StratifiedKFold
X = np.array([
[1,2,3,4],
[11,12,13,14],
[21,22,23,24],
[31,32,33,34],
[41,42,43,44],
[51,52,53,54],
[61,62,63,64],
[71,72,73,74]])
y = np.array([1,1,0,0,1,1,0,0])
folder=KFold(n_splits=4,random_state=None,shuffle=False)
sfolder=StratifiedKFold(n_splits=4,random_state=None,shuffle=False)
#KFold:
print("KFold的结果:")
for train,test in folder.split(X,y):
print("train:%s,test:%s"% (train,test))
#StratifiedKFold:
print("StratifiedKFold的结果:")
for train,test in sfolder.split(X,y):
print("train:%s,test:%s"% (train,test))
我们希望评估的是⽤D训练出的模型。但在留出法和交叉验证法中,由于保留了⼀部分样本⽤于测试,因此实际评估的 模型所使⽤的训练集⽐D⼩,这必然会引⼊⼀些因训练样本规模不同⽽导致的估计偏差。留⼀法受训练样本规模变化的 影响较⼩,但计算复杂度⼜太⾼了。
“⾃助法”( bootstrapping)是⼀个⽐较好的解决⽅案,它直接以⾃助采样法( bootstrap sampling)为基础。给定包含m个 样本的数据集D,我们对它进⾏采样产⽣数据集D:
⾃助法优缺点: