前言
这里是林小编的新模块 ~ 一直想系统学习一下 R
语言关于机器学习的应用,主要从算法和 R 包的角度出发,并把自己的学习笔记分享出来,希望大家可以批评指正,一起交流,主要参考书是 《Machine Learning with R, tidyverse, and mlr[1]》,本书涉及两个非常重要的 R
包为 mlr
和 tidyverse
,感兴趣的读者可以先行安装:
install.packages("mlr", dependencies = TRUE) install.packages("tidyverse")
其中,mlr
包含了数量惊人的机器学习算法,并极大地简化了我们所有的机器学习任务。tidyverse
是一个 “专为数据科学设计的 R 包集合”,创建的目的是让 R
中的数据科学任务更简单、更人性化、更可复制。
本期将先从常用的 k 近邻算法 出发!
1. k 近邻算法简介
k 近邻 (k-Nearest Neighbor,KNN)[2]算法,是一个理论上比较成熟的分类算法,也是最简单的 机器学习算法 之一。该方法的思路是:在特征空间中,如果一个样本附近的 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
(马氏距离)等,而由分析学可知 上范数之间是等价的,故不必过度纠结选谁。在度量之前,应该将每个属性的值规范化,这样有助于防止具有较大初始值域的属性比具有较小初始值域的属性的权重过大。
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
)。另外三个变量是连续测量血糖水平的显性和胰岛素后的葡萄糖耐量测试(分别为glucose
和 insulin
) 以及稳态血糖水平(sspg
)。
注:tibble
包引入了一种新的数据结构,关于该包和此新的数据结构的更多内容读者可参见参考书的第 2 章或该包的官方帮助说明[3]。
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 和 insuli
Fig 2. 变量为 sspg 和 insulin
Fig 3. 变量为 sspg 和 glucose
从图中可以看出,在这三个类别之间,连续变量存在差异,接下来将构建一个 KNN 分类器,并用来预测未来患者的糖尿病状况。
3.3 使用 mlr 训练 KNN 模型
用这个包构建机器学习模型有三个主要阶段:
- 定义任务。任务包括数据以及想要对数据做什么。在本例中,数据是
diabetesTib
,我们想用变量class
作为目标变量对数据进行分类。 - 定义
learner
。learner
只是计划使用的算法的名称,以及该算法接受的任何其他参数。 - 训练模型。这个阶段就是把任务交给
learner
,learner
生成一个模型,你可以用它来预测未来。
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()
函数来定义 learner
。makeLearner()
函数的第一个参数是用来训练模型的算法,在本例中,是使用 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% 的实例都进行了正确的分类。
这是否意味着我们的模型将在新的、未见过的病人身上表现良好?事实上我们并不知道。使用最初用来训练模型的数据进行预测来评估模型性能,几乎不能说明在对完全看不见的数据进行预测时模型将如何运行。因此,用这种方式评估模型性能是不合理的。