机器学习笔记3-k近邻法
k近邻法输入为实例的特征向量,对应于特征空间中的点;输出为实例的类别。分类时,会根据其k个最近邻的训练实例的类别,通过多数表决等方式进行预测。因此,k近邻法不具有显示的学习过程。它包括三个基本要素:k值的选择、距离度量以及分类决策规则。
- k值的选择会对算法结果产生重要影响。当选择较小的k值时,只有与实例点较近的训练实例会对预测结果起作用,近似误差会减小。但预测结果会对近邻的训练实例非常敏感,估计误差会增大。如果邻近训练实例恰好是噪声,预测就会出错。换句话说,k值的减小就意味着整体模型变得复杂,容易发生过拟合;如果选择较大的k值,可以减少学习的估计误差,但近似误差会增大。这时与实例较远的训练实例也会对预测起作用。k值的增大意味着模型变得简单。在应用中,k值一般取一个比较小的数值,可以用grid_search来找一个最佳k值。
- 距离度量,能反映出特征空间中两个实例点的相似程度。这里距离一般用欧式距离,也可以用更一般的 L p L_p Lp距离。 L p ( x i , x j ) = ( ∑ l = 1 n ∣ x i ( l ) − x j ( l ) ∣ p ) 1 p {L_p}({x_i},{x_j}) = {\left( {\sum\limits_{l = {\rm{1}}}^n {{{\left| {x_i^{{\rm{(}}l{\rm{)}}} - x_j^{{\rm{(}}l{\rm{)}}}} \right|}^p}} } \right)^{\frac{{\rm{1}}}{p}}} Lp(xi,xj)=(l=1∑n∣∣∣xi(l)−xj(l)∣∣∣p)p1这里 p ≥ 1 p \ge 1 p≥1。当 p = 2 p=2 p=2时,为欧氏距离;当 p = 1 p=1 p=1时,为曼哈顿距离;当 p = ∞ p=\infty p=∞时,为各个坐标距离的最大值。
- 分类决策规则一般会采用多数表决
k近邻法的实现:kd树
k近邻法最简单的实现方法是线性扫描,这时要计算实例与每个训练实例的距离。这种方法非常耗时。为了提高算法效率,可以用特殊的结构存储训练数据,进而基于这个数据结构进行搜索。比如构建kd树的方法。kd树有点类似于二叉搜索树,两者都满足对于一个根节点,其值不小于左子树的任何节点,且不大于右子树的任何节点。所不同的是构造过程,kd树是基于
R
n
R^n
Rn空间,会直接对特征空间每一维进行划分;二叉搜索树是将元素一个一个添加的过程,与堆的构建一样。
- 如何构造kd树
输入:k维空间数据集T={ x 1 , x 2 . . . x n x_1,x_2...x_n x1,x2...xn},其中 x i = ( x i ( 1 ) , x i ( 2 ) , ⋯   , x i ( k ) ) T {x_i} = {(x_i^{(1)},x_i^{(2)}, \cdots ,x_i^{(k)})^T} xi=(xi(1),xi(2),⋯,xi(k))T
(1)选择 x ( 1 ) x^{(1)} x(1)为坐标轴,以T中所有实例的 x ( 1 ) x^{(1)} x(1)坐标的中位数为切分点(根结点),将 x ( 1 ) x^{(1)} x(1)这一维分成两个子树,左子树对应坐标 x ( 1 ) x^{(1)} x(1)小于根节点的坐标,右子树对应坐标 x ( 1 ) x^{(1)} x(1)大于根节点的坐标。
(2)重复:对深度为 j j j的结点,选择 x ( l ) x^{(l)} x(l)这一维进行切分, l = j / / k + 1 l=j//k+1 l=j//k+1,以 x ( l ) x^{(l)} x(l)这一维的中位数作为切分点(根结点),将该结点所包含的数据集划分到左右两个子树中。
(3)直到所有叶结点只包括一个样本,则停止划分。 - 如何搜索kd树
从根结点出发,向下访问kd树。若目标点当前维的坐标小于切分点的坐标,则移动到左子结点,否则移到右子结点,直到子结点为叶结点。以此叶结点为当前最近点,递归地向上回退,在每个结点进行以下操作:
(1)若该结点保存的实例点距离目标点更近,则以该实例点为当前最近点
(2)检查该结点的另一子结点所包含的数据集是否有更近的点。如果有,则移到该子结点区域递归地进行最近邻搜索,否则向上回退
(3)当回退到根结点,搜索结束。(此处写的比较粗糙,具体可参考李航《统计学习方法》,从划分区域和球体相交的角度更好理解)
在python的机器学习库scikit-learn中,有能直接拿来用的knn函数KNeighborsClassifier,其参数n_neighbors即为k值。以下是利用KNeighborsClassifier实现的一小段代码,数据集是sklearn中的内置数据集:
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.neighbors import KNeighborsClassifier
iris = load_iris()
x, y = iris.data, iris.target
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.25,random_state=33)
scaler=preprocessing.StandardScaler()
x_train=scaler.fit_transform(x_train)
x_test=scaler.transform(x_test)
knn=KNeighborsClassifier(n_neighbors=3)
knn.fit(x_train,y_train)
y_train_predict=knn.predict(x_train)
from sklearn import metrics
print(metrics.accuracy_score(y_train,y_train_predict))
y_predict=knn.predict(x_test)
print(metrics.accuracy_score(y_test,y_predict))
参考
李航《统计学习方法》
https://mlnote.wordpress.com/2015/12/16/python机器学习实践与kaggle实战-machine-learning-for-kaggle-competition-in-python/