R语言机器学习之KNN

1,253 阅读7分钟

前言

这里是林小编的新模块 ~ 寒假到了,小编一直想系统学习一下 R 语言关于机器学习的应用,主要从算法和 R 包的角度出发,并把自己的学习笔记分享出来,希望大家可以批评指正,一起交流,主要参考书是 《Machine Learning with R, tidyverse, and mlr》,本书涉及两个非常重要的 R 包为 mlrtidyverse,感兴趣的读者可以先行安装:

install.packages("mlr", dependencies = TRUE)
install.packages("tidyverse")

其中,mlr 包含了数量惊人的机器学习算法,并极大地简化了我们所有的机器学习任务。tidyverse 是一个 “专为数据科学设计的 R 包集合”,创建的目的是让 R 中的数据科学任务更简单、更人性化、更可复制。

本期将先从常用的 k 近邻算法 出发!

1. k 近邻算法简介

k 近邻 (k-Nearest Neighbor,KNN)算法,是一个理论上比较成熟的分类算法,也是最简单的 机器学习算法 之一。该方法的思路是:在特征空间中,如果一个样本附近的 k 个最近 (即特征空间中最邻近) 样本的大多数属于某一个类别,则该样本也属于这个类别。

即给定一个训练数据集,对新输入的样本,在训练数据集中找到与该样本最邻近的 k 个实例, 这 k 个实例中的多数属于哪个类,则新输入的样本也属于哪个类。

2. KNN 算法基本要素

KNN 算法中,所选择的邻近实例都是已经正确分类的对象,该算法只依赖于最邻近的一个或者几个实例的类别来决定待分样本所属的类别,分类器不需要使用训练集进行训练,训练时间复杂度为 0,即若训练集中文档总数为 n,那么 KNN 的分类时间复杂度为 O(n)。k 值的选择、距离度量和分类决策规则是该算法的三个基本要素:

2.1 k 值的选择

易知,k 值的选择会对算法的结果产生重大影响。k 值较小意味着只有与待分样本较近的训练实例才会对预测结果起作用,但容易发生过拟合;若 k 值较大,这时与待分样本距离较远的训练实例也会对预测起作用,可能使预测发生错误。在实际应用中,k 值一般选择一个较小的数值 (通常小于 20),实际中常采用 交叉验证 的方法来选择最优的 k 值。

2.2 距离度量

距离度量方法有 Euclidean(欧氏距离)、Minkowski(闵可夫斯基距离)和 Mahalanobis(马氏距离)等,而由分析学可知 RnR^n 上范数之间是等价的,故不必过度纠结选谁。在度量之前,应该将每个属性的值规范化,这样有助于防止具有较大初始值域的属性比具有较小初始值域的属性的权重过大。

2.3 分类决策规则

该算法中的分类决策规则往往是多数表决,即由输入实例的 k 个最邻近的训练实例中的多数类决定待分样本的类别。

3. 应用举例

本文将先介绍 mlr 包中 KNN 算法的使用方法,以 mclust 包中的 diabetes 数据集为例。

3.1 加载数据

library(mclust)
library(tibble)#属于 tidyverse,以合理的方式组织和显示数据
data(diabetes, package = "mclust")#加载数据
diabetesTib <- as_tibble(diabetes)#转换为 tibble 形式
diabetesTib
# A tibble: 145 x 4
   class  glucose insulin  sspg
   <fct>    <dbl>   <dbl> <dbl>
 1 Normal      80     356   124
 2 Normal      97     289   117
 3 Normal     105     319   143
 4 Normal      90     356   199
 5 Normal      90     323   240
 6 Normal      86     381   157
 7 Normal     100     350   221
 8 Normal      85     301   186
 9 Normal      97     379   142
10 Normal      97     296   131
# ... with 135 more rows

该数据集有 145 个实例和 4 个变量。class 因子显示,76 例为非糖尿病(Normal),36 例为化学糖尿病(Chemical),33 例为明显糖尿病(Overt)。另外三个变量是连续测量血糖水平的显性和胰岛素后的葡萄糖耐量测试(分别为glucoseinsulin) 以及稳态血糖水平(sspg)。

tibble 包引入了一种新的数据结构,关于该包和此新的数据结构的更多内容读者可参见参考书的第 2 章或该包的官方帮助说明

3.2 作图分析

为了理解这些变量之间的关系,使用 R 中常用的 ggplot2 包绘制图。

library(ggplot2)
ggplot(diabetesTib, aes(glucose, insulin, col = class)) +
  geom_point() +
  theme_bw()#变量为 glucose 和 insulin
  
ggplot(diabetesTib, aes(sspg, insulin, col = class)) +
  geom_point() +
  theme_bw()#变量为 sspg 和 insulin
  
ggplot(diabetesTib, aes(sspg, glucose, col = class)) +
  geom_point() +
  theme_bw()#变量为 sspg 和 glucose

Fig 1. 变量为 glucose 和 insulin

Fig 2. 变量为 sspg 和 insulin

Fig 3. 变量为 sspg 和 glucose

从图中可以看出,在这三个类别之间,连续变量存在差异,接下来将构建一个 KNN 分类器,并用来预测未来患者的糖尿病状况。

3.3 使用 mlr 训练 KNN 模型

用这个包构建机器学习模型有三个主要阶段:

  • 定义任务。任务包括数据以及想要对数据做什么。在本例中,数据是 diabetesTib,我们想用变量 class 作为目标变量对数据进行分类。

  • 定义 learnerlearner 只是计划使用的算法的名称,以及该算法接受的任何其他参数。

  • 训练模型。这个阶段就是把任务交给 learnerlearner 生成一个模型,你可以用它来预测未来。

3.3.1 定义任务

定义任务所需的部分有:

  • 包含预测变量的数据 (我们希望这些变量包含进行预测/解决问题所需的信息)。

  • 想要预测的目标变量 (target variable)。

即:

Fig 4. 在 mlr 中定义任务

因为要构建一个分类模型,故使用 makeClassifTask() 函数来定义一个分类任务,当构建回归和聚类模型时,将分别使用 makeRegrTask()makeClusterTask()

library(mlr)
#定义分类任务
diabetesTask <- makeClassifTask(data = diabetesTib, target = "class")

3.3.2 定义 learner

定义 learner 所需的部分有:

  • 使用的算法类别: "classif." 分类;"regr." 回归;"cluster." 聚类;"surv." 和 "multilabel." 用于预测生存和多标签分类。

  • 使用的算法。

  • 用来控制算法的其他选项。

即:

Fig 5. 在 mlr 中定义 learner

使用 makeLearner() 函数来定义 learnermakeLearner() 函数的第一个参数是用来训练模型的算法,在本例中,是使用 KNN 算法,因此指定参数为 "classif.knn"。第二个参数 par.vals 表示参数值,用来指定希望算法使用的 k 个最近邻的数量。

#定义 learner
knn <- makeLearner("classif.knn", par.vals = list("k" = 2))# k 先设定为 2,后续会讨论如何选择 k

3.3.3 训练模型

训练模型所需的部分是我们之前定义的任务和 learner,定义任务、learner 并将其结合起来就是训练模型的整个过程。

即:

Fig 6. 在 mlr 中训练模型

这个过程通过 train() 函数实现,它将 learner 作为第一个参数,而任务作为第二个参数。

#训练模型
knnModel <- train(knn, diabetesTask)

3.4 预测和评估模型

现在我们有了模型,再将数据传回模型,看看它是如何执行的。predict() 函数接受未标记的数据,并将其传递给模型以获得它们的预测类,该函数第一个参数是模型,传递给它的数据由第二个参数 newdata 给出。

knnPred <- predict(knnModel, newdata = diabetesTib)

可再将这些预测作为 performance() 函数的第一个参数传递。该函数将模型预测的类与真实的类进行比较,并返回预测值与真实值之间匹配程度的性能指标。

Fig 7. mlr 中 predict()和 performance()函数的过程

performance(knnPred, measures = list(mmce, acc))
     mmce       acc 
0.0137931 0.9862069 

这里指定的性能指标为 mmce (mean misclassifcation error) 和 acc (accuracy)。 mmce 是被分类为其他类别而不是真实类别的实例所占的比例,acc 与此相反,是模型正确分类的实例比例。

由此可见,模型对 98.62% 的实例都进行了正确的分类。

这是否意味着我们的模型将在新的、未见过的病人身上表现良好? 事实上我们并不知道。使用最初用来训练模型的数据进行预测来评估模型性能,几乎不能说明在对完全看不见的数据进行预测时模型将如何运行。 因此,用这种方式评估模型性能是不合理的。

小编有话说

本期关于 KNN 算法的内容就先介绍到这里啦,下期将继续介绍交叉验证、如何选择参数 k 来优化模型以及使用 R 语言里的 knnkknn 函数实现 k 近邻分类和有权重的 k 近邻分类等内容。