Hi,大家好,我是晨曦
前面几期我们介绍了scRNA-seq相关的分析内容,这期我们继续介绍机器学习,来介绍一个新时代整合机器学习的R包——mlr3包
晨曦解读
我们完全可以把mlr3包当成是一个仓库,里面存放着一系列拥有统一端口的机器学习算法, 这样会大大降低R语言进行机器学习的成本,方便我们后续进行多模型性能的评估
这期的内容,我们将来阐释mlr3包的一些流程方面的宏观内容,这一部分内容是类似总纲的部分,如果各位小伙伴有不理解的地方,可以优先选择跳过~
简单理解:mlr3包针对不同情况开发出来了很多“对象”,这里的对象我们类比于scRNA-seq里的Seurat对象即可,尽管彼此的格式不同,但是作为医学生,了解到这里足够了
那么,我们开始吧
首先,我们来对mlr3包进行一个容易理解的定义:这个R包本质上来说就是一个整合各种机器学习算法的R包,它可以通过标准化的代码格式完成不同机器学习模型的构建,仅此而已
了解完这些,我们就继续往下探索,我们都知道,如果我们要构建模型,有的时候我们会采用留出交叉验证,也就是事先划分训练集和测试集,然后在训练集上训练模型,在测试集上测试模型,当我们进行这一系列操作的时候,我们在mlr3中就会生成一个对象,这个时候生成的就是一个R6对象
mlr3包走常规流程有一个对象,同时进行重抽样的时候还会有一个对象,虽然这两个对象之间的架构是一样的,但是彼此包含的信息确实有些不同,了解其中的差别就是本期推文需要掌握的内容
光讲概念可能比较空洞,我们看一下下面的代码
#示例代码
library("mlr3")#加载R包
task = tsk("iris")#选择mlr3包已经打包好的任务数据
learner = lrn("classif.rpart")#选择学习器(随机森林)
learner$train(task, row_ids = 1:120)#使用训练集训练模型(注意这个时候选择的是前120个观测)
learner$model#查看拟合模型
predictions = learner$predict(task, row_ids = 121:150)#使用拟合好的模型进行预测
predictions#查看预测结果
这个时候我们就可以看到,命令通过$进行连接,这个其实就是R6对象的特点,那么下面我们详细的阐释一下这个内容,方便大家后续进行学习
首先,我们对面向对象下一个简单的定义,究竟什么是面向对象呢?
面向对象其实就是一个把数据和任务进行打包的一个产物,简单理解其实就是露营的时候,我们需要准备帐篷、烤炉、睡袋等等,其中帐篷、烤炉、睡袋就好比是一个个数据框,那么打包起来这一个整体就是一个面向对象,这样的话极大的方便了使用者,因为压根就不需要去别的地方找数据,因为都打包好了
当然面向对象是一个大类,其中包含S3、S4、R6等等,mlr3包全面拥抱了R6对象,这里其实在网络上看到一个非常好的答案,这里我们也是做一下引用
所谓的面向对象指的是实际操作的对象,但是在进行R语言处理的时候我们针对的是类的概念,比如下面的例子
类class(人)=>子类class(学生)=>对象object(李小龙)
类包含两个特点
属性:一个物体具有的信息,在R语言中相当于变量(数据)
方法:类进行的操作,相当于R语言中的函数(函数)
封装:只需要了解接口,不需要对内部进行处理(简单化)
继承:学生继承了人的特点,学生为子类,人为父类(泛化)
多态:相同的函数可以作用不同的类,类的不同产生不同的结果
当然这些概念我们了解即可,因为再往下进行学习就已经涉及到了R语言编程方面相关的知识,在这里我们只需要知道,我们通过对mlr3包构建的R6对象进行访问的时候,使用的是$即可晨曦解读
其实理解完上面这个流程,mlr3包就已经理解了一半了,接下来我们将用通俗的语言来解析上面这个流程图
首先,我们把我们的数据封装成一个任务,这时候会有小伙伴问了,什么是任务?关于任务我们可以简单理解为就是一个对象,里面包含了数据以及我们感兴趣的响应变量,同时针对执行分类还是回归又会使用不同的函数来创建任务,所以任务应该是包含三种属性,分别是数据、响应变量、执行分类还是回归,示例代码如下:#示例代码_任务
data(diabetes,package = "mclust")#调用内置数据
diabetesTib
diabetesTask
#as_task_classif函数为转化已经存在的数据为分类任务
#target参数指定响应变量
diabetesTask#查看R6对象
# (145 x 4)
#* Target: class
#* Properties: multiclass
#* Features (3):
# - dbl (3): glucose, insulin, sspg
可以很清楚的看到,我们创建的这个diabetesTask对象里面包含的信息分别是一个145个观测,4个变量的数据,然后感兴趣的因变量为class变量,因变量的属性为多分类变量,自变量有三个分别是glucose, insulin, sspg,并且这三个自变量都是数值型变量了解完什么是任务后,我们继续看流程图,在构建完任务后,我们就可以运用留出交叉验证的思想,划分训练集和测试集,示例代码如下:#示例代码_划分数据集
train_set = sample(diabetesTask$nrow, 0.8 * diabetesTask$nrow)#划分训练集(获取划分后的观测编号)
test_set = setdiff(seq_len(diabetesTask$nrow), train_set)#划分测试集(获取划分后的观测编号)
diabetesTask$data(train_set)#查看划分数据后的训练集的形式
注意,我们如果根据流程图可能会有一个惯性思维,就是第一步创建任务、第二步划分数据集,第三步选择学习器等等,但是在mlr3包的整体流程中,这几步其实是有一个颠倒的,之所以还保留这个流程,是因为我们进行机器学习的常规流程就是这个样子,但是,我们在实际操作的时候,确是下面这个流程3. 如果要使用留出交叉验证(划分训练集和测试集)则需要优选选择好训练集和测试集的观测编号,如果使用K折交叉验证则需要选择进行多少折、多少重复的交叉验证#示例代码
library(mlr3verse)#安装R包
data(diabetes,package = "mclust")#调用内置数据
diabetesTib
diabetesTask
learner = lrn("classif.rpart" , cp=0.1, minsplit=10) #选择学习器
train_set = sample(diabetesTask$nrow, 0.8 * diabetesTask$nrow)#划分训练集(获取划分后的观测编号)
test_set = setdiff(seq_len(diabetesTask$nrow), train_set)#划分测试集(获取划分后的观测编号)
learner$train(diabetesTask, row_ids=train_set)#拟合模型
learner$model #查看训练好的模型
#n= 116
#node), split, n, loss, yval, (yprob)
# * denotes terminal node
#1) root 116 53 Normal (0.20689655 0.54310345 0.25000000)
# 2) insulin< 420.5 65 2 Normal (0.03076923 0.96923077 0.00000000) *
# 3) insulin>=420.5 51 22 Overt (0.43137255 0.00000000 0.56862745)
# 6) glucose< 117 22 0 Chemical (1.00000000 0.00000000 0.00000000) *
# 7) glucose>=117 29 0 Overt (0.00000000 0.00000000 1.00000000) *
这样我们就通过几行代码用训练集拟合了一个随机森林模型,那么这个时候,肯定会有小伙伴有一些问题,下面我们筛选了几个问题进行回答问题一:为什么需要安装mlr3verse包,这个包究竟是干什么的?
回答:我们可以简单理解,mlr3包是一个主体,但是毕竟我们机器学习是一个很广阔的领域,所以为了实现更多功能,作者把各种方法按照模块进行了打包,封装成了一个又一个R包,mlr3包作为主体,其周围还有很多补充的R包,下面这张图就展示了mlr3包的“生态环境”(Ps:如果运行代码其实没有任务或者没有学习器,那么大概率就是没有加载相应的R包)
总的来说,mlr3可以应对绝大多数机器学习算法,并且支持标准化、缺失值填补、特征选择等一些额外但是必须的建模预处理问题二:晨曦,基于mlr3包如何进行缺失值填补,与数据QC有什么区别呢?
回答:其实本质上来说没有什么区别,我们完全可以把数据清理好后再进行mlr3包的标准流程,但是如果想要用基于mlr3包的流程来进行缺失值填补也是可以的,示例代码如下:
#基于mlr3包进行缺失值填补
library(mlr3verse)#额外扩展工具
library(mlr3learners)#加载学习器的扩展R包
task = tsk("pima")#调用内置任务
learner = lrn("classif.ranger")#选择学习器
晨曦解读
注意,因为R语言的特性,有很多作者针对同一个算法写了不同的R包,比如说ranger包中执行随机森林就不能够处理缺失值,而raprt包执行随机森林算法就支持处理缺失值,所以这也是R语言在mlr3包之前无法高效适配机器学习的一方面原因
task$missings()#查看缺失情况
#diabetes age glucose insulin mass pedigree pregnant pressure triceps
# 0 0 5 374 11 0 0 35 227
learner$properties#查看学习器可以处理的数据类型
#[1] "hotstart_backward" "importance" "multiclass" "oob_error" "twoclass"
#[6] "weights"
lrn("classif.rpart")$properties#查看学习器可以处理的数据类型
#[1] "importance" "missings" "multiclass" "selected_features" "twoclass"
#[6] "weights"
接下来,我们既然已经有了前面的背景知识,我们再来看看目前mlr包支持进行插补的方法#查看支持的插补方法
as.data.table(mlr_pipeops)[tags %in% "missings", list(key)]
# key
#1: imputeconstant#插补特定数值
#2: imputehist#按照数据分布进行插补
#3: imputelearner#使用机器学习算法进行插补
#4: imputemean#平均值插补
#5: imputemedian#中位数插补
#6: imputemode
#7: imputeoor
#8: imputesample
task = tsk("pima")#调用内置任务
task$missings()#查看缺失值
#diabetes age glucose insulin mass pedigree pregnant pressure
0 0 5 374 11 0 0 35
#triceps
227
#针对glucose变量的缺失值赋值给-999
po = po("imputeconstant", param_vals = list(
constant = -999, affect_columns = selector_name("glucose"))
)#创建缺失值填补方案
new_task = po$train(list(task = task))[[1]]#执行缺失值填补
new_task$missings()#查看缺失值情况
#diabetes age insulin mass pedigree pregnant pressure triceps
# 0 0 374 11 0 0 35 227
#glucose
# 0
head(new_task$data(cols = "glucose")[[1]])#查看当前变量情况
task = tsk("pima")#调用内置任务
task$missings()#查看缺失值
# diabetes age glucose insulin mass pedigree pregnant pressure
# 0 0 5 374 11 0 0 35
# triceps
# 227
po = po("imputehist")#选择插补方法(针对数值型变量,因为我们的任务里面都是数值型,所以可以省略设置缺失值插补对象的参数即selector_name或者selector_type)
new_task = po$train(list(task = task))[[1]]
new_task$missings()
# diabetes age pedigree pregnant glucose insulin mass pressure
# 0 0 0 0 0 0 0 0
# triceps
# 0
晨曦解读
这个时候肯定会有小伙伴问,下面这句代码为什么要加上[[1]]这个操作?这里其实很简单,我们通过设定插补方法后应用在我们想要进行插补的任务上,生成的对象为是一个包含多类的对象,代码理解如下:
new_task = po$train(list(task = task))
#$output
# (768 x 9)
#* Target: diabetes
#* Properties: twoclass
#* Features (8):
# - dbl (8): age, glucose, insulin, mass, pedigree, pregnant,
# pressure, triceps
new_task = po$train(list(task = task))[[1]]
# (768 x 9)
#* Target: diabetes
#* Properties: twoclass
#* Features (8):
# - dbl (8): age, glucose, insulin, mass, pedigree, pregnant,
# pressure, triceps
前面我们讲过面向对象是一个包含层层类的对象,那么这个时候我们生成的完成插补的对象就是一个有着额外类别信息的对象,但是我们后续不管是拟合模型还是后面的模型评估,支持都是没有额外类别的对象,所以这个时候我们需要去掉额外类别也就是提取出来里面的对象,所以加了一个[[1]]#应用KNN进行插补
task = tsk("pima")
task$missings()
task$data()
# diabetes age glucose insulin mass pedigree pregnant pressure
# 0 0 5 374 11 0 0 35
# triceps
# 227
po = po("imputelearner",
po("imputehist") %>>% lrn("regr.kknn")
)
new_task = po$train(list(task = task))[[1]]
new_task$missings()
# diabetes age pedigree pregnant glucose insulin mass pressure
# 0 0 0 0 0 0 0 0
# triceps
# 0
#应用随机森林进行插补
task = tsk("pima")
task$missings()
task$data()
# diabetes age glucose insulin mass pedigree pregnant pressure
# 0 0 5 374 11 0 0 35
# triceps
# 227
po = po("imputelearner", lrn("regr.rpart"))
new_task = po$train(list(task = task))[[1]]
new_task$missings()
# diabetes age pedigree pregnant glucose insulin mass pressure
# 0 0 0 0 0 0 0 0
# triceps
# 0
晨曦解读
这个时候肯定会有小伙伴问,为什么在执行KNN的时候需要额外添加一个imputehist方法,因为KNN在执行的时候需要额外计算欧式距离,需要一个前期工作,所以需要添加这个方法,如果去掉,代码将运行报错,可以简单理解为就是一个预处理晨曦这里只是简单介绍了三种基于mlr3包缺失值填补的方法,但是后续的方法在构建形式上基本上都是类似的,其实就是分为两步:
1.选择插补方法
2.拟合数据(获取插补后的数据情况以任务的形式展现)
到这里,因为篇幅比较长了,我们总结一下,我们这期的内容mlr3包的整体流程为任务→学习器→模型,至于重抽样其实就是一种划分数据集的方式而已,最常见的交叉验证其实就是不同划分数据集的形式而已,所以我们可以看到流程图,重抽样是围绕在整个流程之外的,因为重抽样的性质原因,我们在重抽样的时候就会生成模型,所以就不会走常规的流程而是另外一种流程#标准流程
library("mlr3")#加载R包
task = tsk("iris")#调用内置任务
learner = lrn("classif.rpart")#选择学习器
learner$train(task, row_ids = 1:120)#学习器拟合任务并且选择训练集来构建模型
learner$model#查看模型
predictions = learner$predict(task, row_ids = 121:150)#用构建好的模型在测试集上进行预测
predictions$score(msr("classif.acc"))#查看模型的评价指标(acc代表准确性)
至此,我们今天的这期推文到这里就结束了,后期可能还会考虑更新一些基于mlr3包的细节内容,包括超参数的调节以及自动调参的部分,还有基于mlr3的workflows,大家如果感兴趣,可以在评论区留言哦~1.mlr3gallery: Impute Missing Variables (mlr-org.com)
晨曦单细胞文献阅读系列
晨曦单细胞笔记系列传送门
1. 首次揭秘!不做实验也能发10+SCI,CNS级别空间转录组套路全解析(附超详细代码!)
2. 过关神助!99%审稿人必问,多数据集联合分析,你注意到这点了吗?
3. 太猛了!万字长文单细胞分析全流程讲解,看完就能发文章!建议收藏!(附代码)
4. 秀儿!10+生信分析最大的难点在这里!30多种方法怎么选?今天帮你解决!
5. 图好看易上手!没有比它更适合小白入手的单细胞分析了!老实讲,这操作很sao!
6. 毕业救星!这个R包在高分文章常见,实用!好学!
7. 我就不信了,生信分析你能绕开这个问题!今天一次性帮你解决!
晨曦单细胞数据库系列传送门
1. 宝儿,5min掌握一个单细胞数据库,今年国自然就靠它了!(附视频)
2. 审稿人返修让我补单细胞数据咋办?这个神器帮大忙了!
3. 想白嫖、想高大上、想有高大上的SCI?这个单细胞数据库,你肯定用得上!(配视频)
4. What? 扎克伯格投资了这个数据库?炒概念?跨界生信?
5. 不同物种也能合并做生信?给你支个妙招,让数据起死回生!
6. 零成本装逼指南!单细胞时代,教你用单细胞数据库巧筛基因,做科研!
7. 大佬研发的单细胞数据库有多强? 别眼馋 CNS美图了!零基础的小白也能10分钟学会!
8. 纯生信发14分NC的单细胞测序文章,这个北大的发文套路,你可以试下!实在不行,拿来挖挖数据也行!
9. 如何最短时间极简白嫖单细胞分析?不只是肿瘤方向!十分钟教你学会!
10. 生信数据挖掘新风口!这个单细胞免疫数据库帮你一网打尽了!SCI的发文源头!
欢迎大家关注解螺旋生信频道-挑圈联靠公号~