KNN算法

K-近邻算法简介

什么是K-近邻算法
  1. K-近邻算法(KNN)概念
    1. K Nearest Neighbor算法又叫KNN算法,这个算法是机器学习里面一个比较经典的算法, 总体来说KNN算法是相对比较容易理解的算法
    2. 定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,来源:KNN算法最早是由Cover和Hart提出的一种分类算法
    3. 距离公式:两个样本的距离可以通过欧式距离公式计算,又叫欧式距离
K-近邻算法流程
  1. 实现流程
    1. 计算已知类别数据集中的点与当前点之间的距离
    2. 按距离递增次序排序
    3. 选取与当前点距离最小的k个点
    4. 统计前k个点所在的类别出现的频率
    5. 返回前k个点出现频率最高的类别作为当前点的预测分类
  2. 该算法的思想是:一个样本与数据集中的k个样本最相似,如果这k个样本中的大多数属于某一个类别
K-近邻算法应用场景
  1. 最近邻 (k-Nearest Neighbors,KNN) 算法是一种分类算法
  2. 1968年由 Cover 和 Hart 提出,应用场景有字符识别、文本分类、图像识别等领域

k-近邻算法api初步使用

Scikit-learn工具介绍
  1. 优点

    1. Python语言的机器学习工具
    2. Scikit-learn包括许多知名的机器学习算法的实现
    3. Scikit-learn文档完善,容易上手,丰富的API
  2. 安装

    pip3 install scikit-learn==0.19.1
    #安装好之后可以通过以下命令查看是否安装成功
    import sklearn
    #安装scikit-learn需要Numpy, Scipy等库
  3. Scikit-learn包含的内容

    1. 分类、聚类、回归
    2. 特征工程
    3. 模型选择、调优
K-近邻算法API
  1. sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)

    1. n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
  2. 案例

    1. 代码过程

      #导入模块
      from sklearn.neighbors import KNeighborsClassifier
      #构造数据集
      x = [[0], [1], [2], [3]]
      y = [0, 0, 1, 1]
      #机器学习 -- 模型训练
      # 实例化API
      estimator = KNeighborsClassifier(n_neighbors=2)
      # 使用fit方法进行训练
      estimator.fit(x, y)
      #预测
      estimator.predict([[1]])

距离度量

欧式距离(Euclidean Distance)
  1. 欧氏距离是最容易直观理解的距离度量方法,我们小学、初中和高中接触到的两个点在空间中的距离一般都是指欧氏距离

  2. 距离公式

    \[n维空间点a(x_{11},x_{12},...,x_{1n})到点b(x_{21},x_{22},...,x_{2n})的欧氏距离(两个n维向量) \\ d=\sqrt{\sum^n_{k=1}(x_{1k}-x_{2k})^2}\]

  3. 举例

    X=[[1,1],[2,2],[3,3],[4,4]];
    #经计算得:
    #d = 1.4142 2.8284 4.2426 1.4142 2.8284 1.4142
曼哈顿距离(Manhattan Distance)
  1. 在曼哈顿街区要从一个十字路口开车到另一个十字路口,驾驶距离显然不是两点间的直线距离。这个实际驾驶距离就是“曼哈顿距离”。曼哈顿距离也称为“城市街区距离”(City Block distance)

  2. 距离公式

    \[n维空间点a(x_{11},x_{12},...,x_{1n})到点b(x_{21},x_{22},...,x_{2n})的曼哈顿距离(两个n维向量) \\ d=\sum^n_{k=1}|x_{1k}-x_{2k}|\]

  3. 举例

    X=[[1,1],[2,2],[3,3],[4,4]];
    #经计算得:
    #d = 2 4 6 2 4 2
切比雪夫距离 (Chebyshev Distance)
  1. 国际象棋中,国王可以直行、横行、斜行,所以国王走一步可以移动到相邻8个方格中的任意一个。国王从格子(x1,y1)走到格子(x2,y2)最少需要多少步?这个距离就叫切比雪夫距离

  2. 距离公式

    \[n维空间点a(x_{11},x_{12},...,x_{1n})到点b(x_{21},x_{22},...,x_{2n})的切比雪夫距离(两个n维向量) \\ d=max(|x_{1k}-x_{2k}|)\]

  3. 举例

    X=[[1,1],[2,2],[3,3],[4,4]];
    #经计算得:
    #d = 1 2 3 1 2 1
闵可夫斯基距离(Minkowski Distance)
  1. 闵氏距离不是一种距离,而是一组距离的定义,是对多个距离度量公式的概括性的表述

  2. 距离公式

    \[n维空间点a(x_{11},x_{12},...,x_{1n})到点b(x_{21},x_{22},...,x_{2n})的闵可夫斯基距离(两个n维向量) \\ d=\sqrt[p]{\sum^n_{k=1}|x_{1k}-x_{2k}|^p}\]

  3. 其中p是一个变参数

    1. 当p=1时,就是曼哈顿距离
    2. 当p=2时,就是欧氏距离
    3. 当p→∞时,就是切比雪夫距离
    4. 根据p的不同,闵氏距离可以表示某一类/种的距离
  4. 闵氏距离,包括曼哈顿距离、欧氏距离和切比雪夫距离都存在明显的缺点

    1. 二维样本(身高[单位:cm],体重[单位:kg]),现有三个样本:a(180,50),b(190,50),c(180,60)。

      a与b的闵氏距离(无论是曼哈顿距离、欧氏距离或切比雪夫距离)等于a与c的闵氏距离。但实际上身高的10cm并不能和体重的10kg划等号

    2. 闵氏距离的缺点

      1. 将各个分量的量纲(scale),也就是“单位”相同的看待了
      2. 未考虑各个分量的分布(期望,方差等)可能是不同的
标准化欧氏距离 (Standardized EuclideanDistance)
  1. 标准化欧氏距离是针对欧氏距离的缺点而作的一种改进

  2. 思路:既然数据各维分量的分布不一样,那先将各个分量都“标准化”到均值、方差相等。假设样本集X的均值(mean)为m,标准差(standard deviation)为s

  3. 距离公式

    \[标准化变量表示为: X^* = \frac{X - m}{s} \\n维空间点a(x_{11},x_{12},...,x_{1n})到点b(x_{21},x_{22},...,x_{2n})的标准化欧式距离(两个n维向量)\\ 标准化欧式距离公式:d = \sqrt{\sum^n_{k=1}(\frac{x_{1k} - x_{2k}}{S_k})^2},S_k为分量的标准差\]

  4. 如果将方差的倒数看成一个权重,也可称之为加权欧氏距离(Weighted Euclidean distance)

  5. 举例

    X=[[1,1],[2,2],[3,3],[4,4]];(假设两个分量的标准差分别为0.5和1)
    #经计算得:
    #d = 2.2361 4.4721 6.7082 2.2361 4.4721 2.2361
余弦距离(Cosine Distance)
  1. 几何中,夹角余弦可用来衡量两个向量方向的差异;机器学习中,借用这一概念来衡量样本向量之间的差异

  2. 距离公式

    \[n维空间点a(x_{11},x_{12},...,x_{1n})到点b(x_{21},x_{22},...,x_{2n})的夹角余弦为 cos(\theta) = \frac{a \cdot b}{|a||b|} \\ 即 cos(\theta) = \frac{\sum^n_{k=1}x_{1k}x_{2k}}{\sqrt{\sum^n_{k=1}x_{1k}^2}\sqrt{\sum^n_{k=1}x_{2k}^2}}\]

  3. 夹角余弦取值范围为[-1,1]。余弦越大表示两个向量的夹角越小,余弦越小表示两向量的夹角越大。当两个向量的方向重合时余弦取最大值1,当两个向量的方向完全相反余弦取最小值-1

  4. 举例

    X=[[1,1],[1,2],[2,5],[1,-4]]
    #经计算得:
    #d = 0.9487 0.9191 -0.5145 0.9965 -0.7593 -0.8107
汉明距离(Hamming Distance)
  1. 两个等长字符串s1与s2的汉明距离为:将其中一个变为另外一个所需要作的最小字符替换次数

  2. 举例

    The Hamming distance between "1011101" and "1001001" is 2. 
    The Hamming distance between "2143896" and "2233796" is 3. 
    The Hamming distance between "toned" and "roses" is 3.
    #随堂练习:
    #求下列字符串的汉明距离:
    # 1011101与 1001001  
    # 2143896与 2233796
    # irie与 rise
    X=[[0,1,1],[1,1,2],[1,5,2]]
    #注:以下计算方式中,把2个向量之间的汉明距离定义为2个向量不同的分量所占的百分比
    #经计算得:
    #d = 0.6667 1.0000 0.3333
  3. 汉明重量:是字符串相对于同样长度的零字符串的汉明距离,也就是说,它是字符串中非零的元素个数:对于二进制字符串来说,就是 1 的个数,所以 11101 的汉明重量是 4。因此,如果向量空间中的元素a和b之间的汉明距离等于它们汉明重量的差a-b

  4. 应用:汉明重量分析在包括信息论、编码理论、密码学等领域都有应用。比如在信息编码过程中,为了增强容错性,应使得编码间的最小汉明距离尽可能大。但是,如果要比较两个不同长度的字符串,不仅要进行替换,而且要进行插入与删除的运算,在这种场合下,通常使用更加复杂的编辑距离等算法

杰卡德距离(Jaccard Distance)
  1. 杰卡德相似系数(Jaccard similarity coefficient):两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数,用符号J(A,B)表示

    \[J(A,B)=\frac{A \bigcap B}{A \bigcup B}\]

  2. 杰卡德距离(Jaccard Distance):与杰卡德相似系数相反,用两个集合中不同元素占所有元素的比例来衡量两个集合的区分度

    \[J_\delta(A,B) = 1-J(A,B) = \frac{A \bigcup B - A \bigcap B}{A \bigcup B}\]

  3. 举例

    X=[[1,1,0][1,-1,0],[-1,1,0]]
    #注:以下计算中,把杰卡德距离定义为不同的维度的个数占“非全零维度”的比例
    #经计算得:
    #d = 0.5000 0.5000 1.0000
马氏距离(Mahalanobis Distance)
  1. 马氏距离是基于样本分布的一种距离,马氏距离的目的就是把方差归一化,使得特征之间的关系更加符合实际情况

  2. 马氏距离是由印度统计学家马哈拉诺比斯提出的,表示数据的协方差距离。它是一种有效的计算两个位置样本集的相似度的方法,与欧式距离不同的是,它考虑到各种特性之间的联系,即独立于测量尺度

  3. 在介绍马氏距离之前,我们先来看如下几个概念

    1. 方差:方差是标准差的平方,而标准差的意义是数据集中各个点到均值点距离的平均值,反应的是数据的离散程度

    2. 协方差: 标准差与方差是描述一维数据的,当存在多维数据时,我们通常需要知道每个维数的变量中间是否存在关联。协方差就是衡量多维数据集中,变量之间相关性的统计量。比如说,一个人的身高与他的体重的关系,这就需要用协方差来衡量。如果两个变量之间的协方差为正值,则这两个变量之间存在正相关,若为负值,则为负相关

    3. 协方差矩阵: 当变量多了,超过两个变量了。那么,就用协方差矩阵来衡量这么多变量之间的相关性。假设 \[X\] 是以 \[n\]个随机变数(其中的每个随机变数是也是一个向量,当然是一个行向量)组成的列向量

      \[X = \left[\begin{matrix} X_1\\X_2\\...\\X_n \end{matrix}\right]\\\]

      其中\[μ_i\]是第i个元素的期望值,即\[μ_i=E(X_i)\],协方差矩阵的第\[i,j\]项(第\[i,j\]项是一个协方差)被定义为如下形式

      \[\sum_{ij} = cov(X_i,X_j = E[(X_i - \mu_i)(X_j-\mu_j)])\]

      即:

      \[\sum=X = \left[\begin{matrix} E[(X_1 - \mu_1)(X_1-\mu_1)]&E[(X_1 - \mu_1)(X_2-\mu_2)]&...&E[(X_1 - \mu_1)(X_n-\mu_n)]\\E[(X_2 - \mu_2)(X_1-\mu_1)]&E[(X_2 - \mu_2)(X_2-\mu_2)]&...&E[(X_2 - \mu_2)(X_n-\mu_n)]\\...&...&...&...\\E[(X_n - \mu_n)(X_1-\mu_1)]&E[(X_n - \mu_n)(X_2-\mu_2)]&...&E[(X_n - \mu_n)(X_n-\mu_n)] \end{matrix}\right]\\\]

      矩阵中的第\[(i,j)\]个元素是\[X_i与X_j\]的协方差

    4. 马氏距离(Mahalanobis Distance)是由马哈拉诺比斯(P. C. Mahalanobis)提出的,表示数据的协方差距离。它是一种有效的计算两个未知样本集的相似度的方法。与欧氏距离不同的是它考虑到各种特性之间的联系(例如:一条关于身高的信息会带来一条关于体重的信息,因为两者是有关联的)并且是尺度无关的(scale-invariant),即独立于测量尺度

    5. 马氏距离公式

      \[对于一个均值为\mu=(\mu_1,\mu_2,...,\mu_p)^T,协方差矩阵为S的多变量x=(x_1,x_2,...,x_p)^T,\\其马氏距离为: D_M(x)=\sqrt{(x-\mu)^TS^{-1}(x-\mu)}\]

      我们可以发现如果\[S^{-1} \]是单位阵的时候,马氏距离简化为欧氏距离

  4. 马氏距离特性

    1. 量纲无关,排除变量之间的相关性的干扰
    2. 马氏距离的计算是建立在总体样本的基础上的,如果拿同样的两个样本,放入两个不同的总体中,最后计算得出的两个样本间的马氏距离通常是不相同的,除非这两个总体的协方差矩阵碰巧相同
    3. 计算马氏距离过程中,要求总体样本数大于样本的维数,否则得到的总体样本协方差矩阵逆矩阵不存在,这种情况下,用欧式距离计算即可
    4. 还有一种情况,满足了条件总体样本数大于样本的维数,但是协方差矩阵的逆矩阵仍然不存在,比如三个样本点(3,4),(5,6),(7,8),这种情况是因为这三个样本在其所处的二维空间平面内共线。这种情况下,也采用欧式距离计算
  5. 马氏距离与欧式距离

    马氏距离也可以定义为两个服从同一分布并且其协方差矩阵的随机变量的差异程度:如果协方差矩阵为单位矩阵,马氏距离就简化为欧式距离;如果协方差矩阵为对角矩阵,则其也可称为正规化的欧式距离

k值的选择

K值问题选择
  1. K值过小:

    容易受到异常点的影响

    k值过大:

    受到样本均衡的问题

  2. K值选择问题,李航博士的一书「统计学习方法」上所说

    1. 选择较小的K值,就相当于用较小的领域中的训练实例进行预测,“学习”近似误差会减小,只有与输入实例较近或相似的训练实例才会对预测结果起作用,与此同时带来的问题是“学习”的估计误差会增大,换句话说,K值的减小就意味着整体模型变得复杂,容易发生过拟合
    2. 选择较大的K值,就相当于用较大领域中的训练实例进行预测,其优点是可以减少学习的估计误差,但缺点是学习的近似误差会增大。这时候,与输入实例较远(不相似的)训练实例也会对预测器作用,使预测发生错误,且K值的增大就意味着整体的模型变得简单
    3. K=N(N为训练样本个数),则完全不足取,因为此时无论输入实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单,忽略了训练实例中大量有用信息
    4. 在实际应用中,K值一般取一个比较小的数值,例如采用交叉验证法(简单来说,就是把训练数据在分成两组:训练集和验证集)来选择最优的K值。对这个简单的分类器进行泛化,用核方法把这个线性模型扩展到非线性的情况,具体方法是把低维数据集映射到高维特征空间
近似误差与估计误差
  1. 近似误差:对现有训练集的训练误差,关注训练集,如果近似误差过小可能会出现过拟合的现象,对现有的训练集能有很好的预测,但是对未知的测试样本将会出现较大偏差的预测。模型本身不是最接近最佳模型
  2. 估计误差:可以理解为对测试集的测试误差,关注测试集,估计误差小说明对未知数据的预测能力好,模型本身最接近最佳模型

kd树

kd树简介
  1. 什么是kd树
    1. 根据KNN每次需要预测一个点时,我们都需要计算训练数据集里每个点到这个点的距离,然后选出距离最近的k个点进行投票。当数据集很大时,这个计算成本非常高,针对N个样本,D个特征的数据集,其算法复杂度为O(DN2)
    2. kd树:为了避免每次都重新计算一遍距离,算法会把距离信息保存在一棵树里,这样在计算之前从树里查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息,就可以在合适的时候跳过距离远的点
    3. 这样优化后的算法复杂度可降低到O(DNlog(N))。感兴趣的读者可参阅论文:Bentley,J.L.,Communications of the ACM(1975)
    4. 1989年,另外一种称为Ball Tree的算法,在kd Tree的基础上对性能进一步进行了优化。感兴趣的读者可以搜索Five balltree construction algorithms来了解详细的算法信息
  2. 原理
    1. 树的建立
    2. 最近邻域搜索(Nearest-Neighbor Lookup)
    3. kd树(K-dimension tree)是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是一种二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将K维空间切分,构成一系列的K维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量
    4. 类比“二分查找”:给出一组数据:[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]
    5. 因此,根本就没有必要进入第一个簇,可以直接进入第二个簇进行查找。把二分查找中的数据点换成k维数据点,这样的划分就变成了用超平面对k维空间的划分。空间划分就是对数据点进行分类,“挨得近”的数据点就在一个空间里面
构造方法
  1. 构造根结点,使根结点对应于K维空间中包含所有实例点的超矩形区域
  2. 通过递归的方法,不断地对k维空间进行切分,生成子结点。在超矩形区域上选择一个坐标轴和在此坐标轴上的一个切分点,确定一个超平面,这个超平面通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个子区域(子结点);这时,实例被分到两个子区域
  3. 上述过程直到子区域内没有实例时终止(终止时的结点为叶结点)。在此过程中,将实例保存在相应的结点上
  4. 通常,循环的选择坐标轴对空间切分,选择训练实例点在坐标轴上的中位数为切分点,这样得到的kd树是平衡的(平衡二叉树:它是一棵空树,或其左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是平衡二叉树)
  5. KD树中每个节点是一个向量,和二叉树按照数的大小划分不同的是,KD树每层需要选定向量中的某一维,然后根据这一维按左小右大的方式划分数据。在构建KD树时,关键需要解决2个问题
    1. 选择向量的哪一维进行划分
    2. 如何划分数据
    3. 第一个问题简单的解决方法可以是随机选择某一维或按顺序选择,但是更好的方法应该是在数据比较分散的那一维进行划分(分散的程度可以根据方差来衡量)。好的划分方法可以使构建的树比较平衡,可以每次选择中位数来进行划分,这样问题2也得到了解决
最近领域的搜索
  1. 基本思想:
    1. 首先通过二叉树搜索(比较待查询节点和分裂节点的分裂维的值,小于等于就进入左子树分支,大于就进入右子树分支直到叶子结点),顺着“搜索路径”很快能找到最近邻的近似点,也就是与待查询点处于同一个子空间的叶子结点
    2. 然后再回溯搜索路径,并判断搜索路径上的结点的其他子结点空间中是否可能有距离查询点更近的数据点,如果有可能,则需要跳到其他子结点空间中去搜索(将其他子结点加入到搜索路径)
    3. 重复这个过程直到搜索路径为空

数据集介绍

案例:鸢尾花种类预测
  1. Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集
scikit-learn中数据集介绍
  1. scikit-learn数据集API介绍

    1. sklearn.datasets,加载获取流行数据集
      1. datasets.load_*(),获取小规模数据集,数据包含在datasets里
      2. datasets.fetch_*(data_home=None),获取大规模数据集,需要从网络上下载,函数的第一个参数是data_home,表示数据集下载的目录,默认是 ~/scikit_learn_data/
    2. sklearn小数据集
      1. sklearn.datasets.load_iris(),加载并返回鸢尾花数据集
    3. sklearn大数据集
      1. sklearn.datasets.fetch_20newsgroups(data_home=None,subset=‘train’)
      2. subset:'train'或者'test','all',可选,选择要加载的数据集
      3. 训练集的“训练”,测试集的“测试”,两者的“全部”
  2. sklearn数据集返回值介绍

    1. load和fetch返回的数据类型datasets.base.Bunch(字典格式)

      1. data:特征数据数组,是 [n_samples * n_features] 的二维 numpy.ndarray 数组
      2. target:标签数组,是 n_samples 的一维 numpy.ndarray 数组
      3. DESCR:数据描述
      4. feature_names:特征名,新闻数据,手写数字、回归数据集没有
      5. target_names:标签名
    2. 代码

      from sklearn.datasets import load_iris
      # 获取鸢尾花数据集
      iris = load_iris()
      print("鸢尾花数据集的返回值:\n", iris)
      # 返回值是一个继承自字典的Bench
      print("鸢尾花的特征值:\n", iris["data"])
      print("鸢尾花的目标值:\n", iris.target)
      print("鸢尾花特征的名字:\n", iris.feature_names)
      print("鸢尾花目标值的名字:\n", iris.target_names)
      print("鸢尾花的描述:\n", iris.DESCR)
  3. 查看数据分布

    1. 通过创建一些图,以查看不同类别是如何通过特征来区分的。 在理想情况下,标签类将由一个或多个特征对完美分隔。 在现实世界中,这种理想情况很少会发生

    2. seaborn介绍

      1. Seaborn 是基于 Matplotlib 核心库进行了更高级的 API 封装,可以让你轻松地画出更漂亮的图形。而 Seaborn 的漂亮主要体现在配色更加舒服、以及图形元素的样式更加细腻。

      2. 安装 pip3 install seaborn

      3. seaborn.lmplot() 是一个非常有用的方法,它会在绘制二维散点图时,自动完成回归拟合

        1. sns.lmplot() 里的 x, y 分别代表横纵坐标的列名,
        2. data= 是关联到数据集,
        3. hue=*代表按照 species即花的类别分类显示,
        4. fit_reg=是否进行线性拟合
      4. 代码

        %matplotlib inline  
        # 内嵌绘图
        import seaborn as sns
        import matplotlib.pyplot as plt
        import pandas as pd
        # 把数据转换成dataframe的格式
        iris_d = pd.DataFrame(iris['data'], columns = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width'])
        iris_d['Species'] = iris.target
        def plot_iris(iris, col1, col2):
        sns.lmplot(x = col1, y = col2, data = iris, hue = "Species", fit_reg = False)
        plt.xlabel(col1)
        plt.ylabel(col2)
        plt.title('鸢尾花种类分布图')
        plt.show()
        plot_iris(iris_d, 'Petal_Width', 'Sepal_Length')
  4. 数据集的划分

    1. 机器学习一般的数据集会划分为两个部分

      1. 训练数据:用于训练,构建模型
      2. 测试数据:在模型检验时使用,用于评估模型是否有效
    2. 划分比例

      1. 训练集:70% 80% 75%
      2. 测试集:30% 20% 25%
    3. 数据集划分api

      1. sklearn.model_selection.train_test_split(arrays, *options)

        1. x 数据集的特征值
        2. y 数据集的标签值
        3. test_size 测试集的大小,一般为float
        4. random_state 随机数种子,不同的种子会造成不同的随机采样结果。相同的种子采样结果相同
        5. return 测试集特征训练集特征值值,训练标签,测试标签(默认随机取)
      2. 代码

        from sklearn.datasets import load_iris
        from sklearn.model_selection import train_test_split
        # 1、获取鸢尾花数据集
        iris = load_iris()
        # 对鸢尾花数据集进行分割
        # 训练集的特征值x_train 测试集的特征值x_test 训练集的目标值y_train 测试集的目标值y_test
        x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
        print("x_train:\n", x_train.shape)
        # 随机数种子
        x_train1, x_test1, y_train1, y_test1 = train_test_split(iris.data, iris.target, random_state=6)
        x_train2, x_test2, y_train2, y_test2 = train_test_split(iris.data, iris.target, random_state=6)
        print("如果随机数种子不一致:\n", x_train == x_train1)
        print("如果随机数种子一致:\n", x_train1 == x_train2)

特征工程-特征预处理

什么是特征预处理
  1. 特征预处理定义
    1. 通过一些转换函数将特征数据转换成更加适合算法模型的特征数据过程
  2. 为什么我们要进行归一化/标准化
    1. 特征的单位或者大小相差较大,或者某特征的方差相比其他的特征要大出几个数量级容易影响(支配)目标结果,使得一些算法无法学习到其它的特征
    2. 我们需要用到一些方法进行无量纲化,使不同规格的数据转换到同一规格
  3. 包含内容(数值型数据的无量纲化)
    1. 归一化
    2. 标准化
  4. 特征预处理API
    1. sklearn.preprocessing
归一化
  1. 定义

    1. 通过对原始数据进行变换把数据映射到(默认为[0,1])之间
  2. 公式

    \[X'=\frac{x-min}{max-min}\ , X''=X'*(mx-mi)+mi\]

    作用于每一列,max为一列的最大值,min为一列的最小值,那么X’’为最终结果,mx,mi分别为指定区间值,默认mx为1,mi为0

  3. API

    1. sklearn.preprocessing.MinMaxScaler (feature_range=(0,1)… )
      1. MinMaxScalar.fit_transform(X)
        1. X:numpy ,array格式的数据[n_samples,n_features]
      2. 返回值:转换后的形状相同的array
  4. 数据计算,代码

    #milage,Liters,Consumtime,target
    #40920,8.326976,0.953952,3
    #14488,7.153469,1.673904,2
    #26052,1.441871,0.805124,1
    #75136,13.147394,0.428964,1
    #38344,1.669788,0.134296,1
    #1、实例化MinMaxScalar
    #2、通过fit_transform转换
    import pandas as pd
    from sklearn.preprocessing import MinMaxScaler
    def minmax_demo():
    data = pd.read_csv("dating.txt")
    print(data)
    # 1、实例化一个转换器类
    transfer = MinMaxScaler(feature_range=(2, 3))
    # 2、调用fit_transform
    data = transfer.fit_transform(data[['milage','Liters','Consumtime']])
    print("最小值最大值归一化处理的结果:\n", data)
    return None
    #返回结果
    # milage Liters Consumtime target
    #0 40920 8.326976 0.953952 3
    #1 14488 7.153469 1.673904 2
    #2 26052 1.441871 0.805124 1
    #3 75136 13.147394 0.428964 1
    #.. ... ... ... ...
    #998 48111 9.134528 0.728045 3
    #999 43757 7.882601 1.332446 3
    #
    #[1000 rows x 4 columns]
    #最小值最大值归一化处理的结果:
    # [[ 2.44832535 2.39805139 2.56233353]
    # [ 2.15873259 2.34195467 2.98724416]
    # [ 2.28542943 2.06892523 2.47449629]
    # ...,
    # [ 2.29115949 2.50910294 2.51079493]
    # [ 2.52711097 2.43665451 2.4290048 ]
    # [ 2.47940793 2.3768091 2.78571804]]
  5. 如果数据中异常点较多,会有什么影响

    1. 归一化总结
    2. 注意最大值最小值是变化的,另外,最大值与最小值非常容易受异常点影响,所以这种方法鲁棒性较差,只适合传统精确小数据场景
标准化
  1. 定义

    1. 通过对原始数据进行变换把数据变换到均值为0,标准差为1范围内
  2. 公式

    \[X'=\frac{x-mean}{\sigma}\]

    作用于每一列,mean为平均值,σ为标准差

  3. 比较归一化和标准化

    1. 对于归一化来说:如果出现异常点,影响了最大值和最小值,那么结果显然会发生改变
    2. 对于标准化来说:如果出现异常点,由于具有一定数据量,少量的异常点对于平均值的影响并不大,从而方差改变较小
  4. API

    1. sklearn.preprocessing.StandardScaler( )
      1. 处理之后每列来说所有数据都聚集在均值0附近标准差差为1
      2. StandardScaler.fit_transform(X)
        1. X:numpy array格式的数据[n_samples,n_features]
      3. 返回值:转换后的形状相同的array
  5. 数据计算,代码

    #1、实例化StandardScaler
    #2、通过fit_transform转换
    def stand_demo():
    data = pd.read_csv("dating.txt")
    print(data)
    # 1、实例化一个转换器类
    transfer = StandardScaler()
    # 2、调用fit_transform
    data = transfer.fit_transform(data[['milage','Liters','Consumtime']])
    print("标准化的结果:\n", data)
    print("每一列特征的平均值:\n", transfer.mean_)
    print("每一列特征的方差:\n", transfer.var_)
    return None
    #返回结果
    # milage Liters Consumtime target
    #0 40920 8.326976 0.953952 3
    #1 14488 7.153469 1.673904 2
    #2 26052 1.441871 0.805124 1
    #.. ... ... ... ...
    #997 26575 10.650102 0.866627 3
    #998 48111 9.134528 0.728045 3
    #999 43757 7.882601 1.332446 3
    #
    #[1000 rows x 4 columns]
    #标准化的结果:
    # [[ 0.33193158 0.41660188 0.24523407]
    # [-0.87247784 0.13992897 1.69385734]
    # [-0.34554872 -1.20667094 -0.05422437]
    # ...,
    # [-0.32171752 0.96431572 0.06952649]
    # [ 0.65959911 0.60699509 -0.20931587]
    # [ 0.46120328 0.31183342 1.00680598]]
    #每一列特征的平均值:
    # [ 3.36354210e+04 6.55996083e+00 8.32072997e-01]
    #每一列特征的方差:
    # [ 4.81628039e+08 1.79902874e+01 2.46999554e-01]
  6. 标准化总结

    1. 在已有样本足够多的情况下比较稳定,适合现代嘈杂大数据场景

流程实现-鸢尾花种类预测

数据集介绍
  1. Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理。Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集
步骤分析
  1. 获取数据集
  2. 数据基本处理
  3. 特征工程
  4. 机器学习(模型训练)
  5. 模型评估
代码过程
  1. 导入模块

    from sklearn.datasets import load_iris
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import StandardScaler
    from sklearn.neighbors import KNeighborsClassifier
  2. 先从sklearn当中获取数据集,然后进行数据集的分割

    # 1.获取数据集
    iris = load_iris()
    # 2.数据基本处理
    # x_train,x_test,y_train,y_test为训练集特征值、测试集特征值、训练集目标值、测试集目标值
    x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)
  3. 进行数据标准化,特征值的标准化

    # 3、特征工程:标准化
    transfer = StandardScaler()
    x_train = transfer.fit_transform(x_train)
    x_test = transfer.transform(x_test)
  4. 模型进行训练预测

    # 4、机器学习(模型训练)
    estimator = KNeighborsClassifier(n_neighbors=9)
    estimator.fit(x_train, y_train)
    # 5、模型评估
    # 方法1:比对真实值和预测值
    y_predict = estimator.predict(x_test)
    print("预测结果为:\n", y_predict)
    print("比对真实值和预测值:\n", y_predict == y_test)
    # 方法2:直接计算准确率
    score = estimator.score(x_test, y_test)
    print("准确率为:\n", score)

k近邻算法总结

K-近邻算法API
  1. sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm='auto')
    1. n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
    2. algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’}
      1. 快速k近邻搜索算法,默认参数为auto,可以理解为算法自己决定合适的搜索算法。除此之外,用户也可以自己指定搜索算法ball_tree、kd_tree、brute方法进行搜索
      2. brute是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时
      3. kd_tree,构造kd树存储数据以便对其进行快速检索的树形数据结构,kd树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于20时效率高
      4. ball tree是为了克服kd树高纬失效而发明的,其构造过程是以质心C和半径r分割样本空间,每个节点是一个超球体
优点
  1. 简单有效
  2. 重新训练的代价低
  3. 适合类域交叉样本
    1. KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
  4. 适合大样本自动分类
    1. 该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分
缺点
  1. 惰性学习
    1. KNN算法是懒散学习方法(lazy learning,基本上不学习),一些积极学习的算法要快很多
  2. 类别评分不是规格化
    1. 不像一些通过概率评分的分类
  3. 输出可解释性不强
    1. 例如决策树的输出可解释性就较强
  4. 对不均衡的样本不擅长
    1. 当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进
  5. 计算量较大
    1. 目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本

交叉验证,网格搜索

什么是交叉验证(cross validation)
  1. 交叉验证:将拿到的训练数据,分为训练和验证集。将数据分成4份,其中一份作为验证集。然后经过4次(组)的测试,每次都更换不同的验证集。即得到4组模型的结果,取平均值作为最终结果。又称4折交叉验证
  2. 分析,我们之前知道数据分为训练集和测试集,但是为了让从训练得到模型结果更加准确。做以下处理
    1. 训练集:训练集+验证集
    2. 测试集:测试集
  3. 为什么需要交叉验证
    1. 交叉验证目的:为了让被评估的模型更加准确可信
什么是网格搜索(Grid Search)
  1. 通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用网格搜索来进行评估。最后选出最优参数组合建立模型
交叉验证,网格搜索(模型选择与调优)API
  1. sklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)

    1. 对估计器的指定参数值进行详尽搜索
    2. estimator:估计器对象
    3. param_grid:估计器参数(dict){“n_neighbors”:[1,3,5]}
    4. cv:指定几折交叉验证
    5. fit:输入训练数据
    6. score:准确率
  2. 结果分析:

    1. bestscore__:在交叉验证中验证的最好结果
    2. bestestimator:最好的参数模型
    3. cvresults:每次交叉验证后的验证集准确率结果和训练集准确率结果
  3. 鸢尾花案例增加K值调优,使用GridSearchCV构建估计器

    1. 代码

      # 1、获取数据集
      iris = load_iris()
      # 2、数据基本处理 -- 划分数据集
      x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
      # 3、特征工程:标准化
      # 实例化一个转换器类
      transfer = StandardScaler()
      # 调用fit_transform
      x_train = transfer.fit_transform(x_train)
      x_test = transfer.transform(x_test)
      # 4、KNN预估器流程
      # 4.1 实例化预估器类
      estimator = KNeighborsClassifier()
      # 4.2 模型选择与调优——网格搜索和交叉验证
      # 准备要调的超参数
      param_dict = {"n_neighbors": [1, 3, 5]}
      estimator = GridSearchCV(estimator, param_grid=param_dict, cv=3)
      # 4.3 fit数据进行训练
      estimator.fit(x_train, y_train)
      # 5、评估模型效果
      # 方法a:比对预测结果和真实值
      y_predict = estimator.predict(x_test)
      print("比对预测结果和真实值:\n", y_predict == y_test)
      # 方法b:直接计算准确率
      score = estimator.score(x_test, y_test)
      print("直接计算准确率:\n", score)
    2. 然后进行评估查看最终选择的结果和交叉验证的结果

      print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
      print("最好的参数模型:\n", estimator.best_estimator_)
      print("每次交叉验证后的准确率结果:\n", estimator.cv_results_)
    3. 最终结果

      #比对预测结果和真实值:
      # [ True  True  True  True  True  True  True False  True  True  True  True
      # True True True True True True False True True True True True
      # True True True True True True True True True True True True
      # True True]
      #直接计算准确率:
      # 0.947368421053
      #在交叉验证中验证的最好结果:
      # 0.973214285714
      #最好的参数模型:
      # KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
      # metric_params=None, n_jobs=1, n_neighbors=5, p=2,
      # weights='uniform')
      #每次交叉验证后的准确率结果:
      # {'mean_fit_time': array([ 0.00114751, 0.00027037, 0.00024462]), 'std_fit_time': array([ 1.13901511e-03, 1.25300249e-05, 1.11011951e-05]), 'mean_score_time': array([ 0.00085751, 0.00048693, 0.00045625]), 'std_score_time': array([ 3.52785082e-04, 2.87650037e-05, 5.29673344e-06]), 'param_n_neighbors': masked_array(data = [1 3 5],
      # mask = [False False False],
      # fill_value = ?)
      #, 'params': [{'n_neighbors': 1}, {'n_neighbors': 3}, {'n_neighbors': 5}], 'split0_test_score': array([ 0.97368421, 0.97368421, 0.97368421]), 'split1_test_score': array([ 0.97297297, 0.97297297, 0.97297297]), 'split2_test_score': array([ 0.94594595, 0.89189189, 0.97297297]), 'mean_test_score': array([ 0.96428571, 0.94642857, 0.97321429]), 'std_test_score': array([ 0.01288472, 0.03830641, 0.00033675]), 'rank_test_score': array([2, 3, 1], dtype=int32), 'split0_train_score': array([ 1. , 0.95945946, 0.97297297]), 'split1_train_score': array([ 1. , 0.96 , 0.97333333]), 'split2_train_score': array([ 1. , 0.96, 0.96]), 'mean_train_score': array([ 1. , 0.95981982, 0.96876877]), 'std_train_score': array([ 0. , 0.00025481, 0.0062022 ])}

预测facebook签到位置

数据集介绍
  1. 数据介绍:将根据用户的位置,准确性和时间戳预测用户正在查看的业务

    train.csv,test.csv 
    row_id:登记事件的ID
    xy:坐标
    准确性:定位准确性
    时间:时间戳
    place_id:业务的ID,这是您预测的目标
步骤分析
  1. 对于数据做一些基本处理(这里所做的一些处理不一定达到很好的效果,我们只是简单尝试,有些特征我们可以根据一些特征选择的方式去做处理)
    1. 缩小数据集范围 DataFrame.query()
    2. 2选取有用的时间特征
    3. 将签到位置少于n个用户的删除
  2. 分割数据集
  3. 标准化处理
  4. k-近邻预测
代码过程
  1. 获取数据集

    # 1、获取数据集
    facebook = pd.read_csv("./data/FBlocation/train.csv")
    
  2. 基本数据处理

    # 2.基本数据处理
    # 2.1 缩小数据范围
    facebook_data = facebook.query("x>2.0 & x<2.5 & y>2.0 & y<2.5")
    # 2.2 选择时间特征
    time = pd.to_datetime(facebook_data["time"], unit="s")
    time = pd.DatetimeIndex(time)
    facebook_data["day"] = time.day
    facebook_data["hour"] = time.hour
    facebook_data["weekday"] = time.weekday
    # 2.3 去掉签到较少的地方
    place_count = facebook_data.groupby("place_id").count()
    place_count = place_count[place_count["row_id"]>3]
    facebook_data = facebook_data[facebook_data["place_id"].isin(place_count.index)]
    # 2.4 确定特征值和目标值
    x = facebook_data[["x", "y", "accuracy", "day", "hour", "weekday"]]
    y = facebook_data["place_id"]
    # 2.5 分割数据集
    x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22)
  3. 特征工程--特征预处理(标准化)

    # 3.特征工程--特征预处理(标准化)
    # 3.1 实例化一个转换器
    transfer = StandardScaler()
    # 3.2 调用fit_transform
    x_train = transfer.fit_transform(x_train)
    x_test = transfer.fit_transform(x_test)
  4. 机器学习--knn+cv

    # 4.机器学习--knn+cv
    # 4.1 实例化一个估计器
    estimator = KNeighborsClassifier()
    # 4.2 调用gridsearchCV
    param_grid = {"n_neighbors": [1, 3, 5, 7, 9]}
    estimator = GridSearchCV(estimator, param_grid=param_grid, cv=5)
    # 4.3 模型训练
    estimator.fit(x_train, y_train)
  5. 模型评估

    # 5.模型评估
    # 5.1 基本评估方式
    score = estimator.score(x_test, y_test)
    print("最后预测的准确率为:\n", score)
    y_predict = estimator.predict(x_test)
    print("最后的预测值为:\n", y_predict)
    print("预测值和真实值的对比情况:\n", y_predict == y_test)
    # 5.2 使用交叉验证后的评估方式
    print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
    print("最好的参数模型:\n", estimator.best_estimator_)
    print("每次交叉验证后的验证集准确率结果和训练集准确率结果:\n",estimator.cv_results_)
posted on 2021-04-12 21:29  Darren_la  阅读(391)  评论(0)    收藏  举报