扩展机器学习工具:自定义度量与管道操作
立即解锁
发布时间: 2025-08-31 00:28:35 阅读量: 7 订阅数: 11 AIGC 

### 扩展机器学习工具:自定义度量与管道操作
在机器学习的实践中,我们常常需要根据具体的需求扩展现有的工具和库。本文将介绍如何自定义性能度量和管道操作(PipeOps),以满足特定的任务需求。
#### 1. 自定义性能度量
在某些情况下,现有的性能度量可能无法满足我们的需求,这时就需要自定义性能度量。下面以回归问题的均方根误差(Root Mean Squared Error, RMSE)为例,介绍如何自定义性能度量。
##### 1.1 定义损失函数
首先,我们独立于 `mlr3` 库定义损失函数,用于计算均方根误差:
```R
root_mse = function(truth, response) {
mse = mean((truth - response)^2)
sqrt(mse)
}
root_mse(c(0, 0.5, 1), c(0.5, 0.5, 0.5))
# [1] 0.4082
```
##### 1.2 创建 R6 类
接下来,我们将 `root_mse()` 函数嵌入到一个新的 R6 类中,该类继承自 `mlr3::MeasureRegr`:
```R
MeasureRootMSE = R6::R6Class("MeasureRootMSE",
inherit = mlr3::MeasureRegr,
public = list(
initialize = function() {
super$initialize(
# 自定义度量的 ID
id = "root_mse",
# 计算此度量所需的额外包
packages = character(),
# 属性,详见下文
properties = character(),
# 学习器所需的预测类型
predict_type = "response",
# 可行的值范围
range = c(0, Inf),
# 调优时是否最小化
minimize = TRUE
)
}
),
private = list(
# 对预测对象进行自定义评分的函数
.score = function(prediction, ...) {
root_mse = function(truth, response) {
mse = mean((truth - response)^2)
sqrt(mse)
}
root_mse(prediction$truth, prediction$response)
}
)
)
```
##### 1.3 重要参数解析
- **properties**:如果将度量标记为 `"requires_task"`,则 `Task` 对象将自动传递给 `.score()` 函数(别忘了在函数签名中添加 `task` 参数)。同样,如果需要操作 `Learner`,可以使用 `"requires_learner"`;如果想在评分函数中访问训练索引集,可以使用 `"requires_train_set"`。
- **aggregator**:该函数(默认为 `mean()`)控制如何将多个性能得分(例如来自不同重采样迭代的得分)聚合为一个单一的数值,如果设置为微观平均。宏观平均时此参数将被忽略。
- **predict_sets**:要操作的预测集(`("train", "test")` 的子集),默认为 “test” 集。
##### 1.4 添加自定义度量
最后,我们可以将自定义度量添加到 `mlr_measures` 字典中,以便像使用其他度量一样使用它:
```R
mlr3::mlr_measures$add("root_mse", MeasureRootMSE)
```
通常,将度量和 `mlr_measures$add()` 调用放在一个新的 R 文件中,并在项目中源引该文件是个不错的选择。
#### 2. 自定义管道操作(PipeOps)
`mlr3pipelines` 包允许我们扩展以包含自定义的管道操作。下面将介绍如何创建自定义的管道操作。
##### 2.1 准备工作
为了运行后续的示例,我们需要一个任务,这里使用著名的 “Iris” 任务:
```R
library("mlr3")
task = tsk("iris")
task$data()
```
##### 2.2 一般情况示例:PipeOpCopyTwo
`PipeOpCopyTwo` 是一个简单但实用的管道操作,它接受一个输入并创建两个输出通道,每个通道都接收输入数据的副本。
###### 2.2.1 继承 PipeOp
创建自定义管道操作的第一步是继承 `PipeOp` 类,并实现 `.train()` 和 `.predict()` 函数:
```R
PipeOpCopyTwo = R6::R6Class("PipeOpCopyTwo",
inherit = mlr3pipelines::PipeOp,
public = list(
initialize = function(id = "copy.two") {
...
}
),
private = list(
.train = function(inputs) {
...
},
.predict = function(inputs) {
...
}
)
)
```
###### 2.2.2 通道定义
我们需要告诉管道操作其通道的布局,包括通道数量、名称和可接受的类型。这可以通过在初始化时提供输入和输出的 `data.table` 对象来完成:
```R
initialize = function(id = "copy.two") {
input = data.table::data.table(name = "input", train = "*", predict = "*")
output = data.table::data.table(
name = c("output1", "output2"),
train = "*", predict = "*"
)
super$initialize(id,
input = input,
output = output
)
}
```
###### 2.2.3 训练和预测函数
`.train()` 和 `.predict()` 函数都接收一个列表作为输入,并返回一个列表。我们只需要创建两个输入数据的副本:
```R
.train = function(inputs) {
self$state = list()
c(in
```
0
0
复制全文
相关推荐








