调整 LASSO 模型并使用 tidymodels 进行预测

Tuning a LASSO model and predicting using tidymodels

我想为 LASSO 算法执行惩罚选择并使用 tidymodels 预测结果。我将使用波士顿住房数据集来说明问题。

library(tidymodels)
library(tidyverse)
library(mlbench)

data("BostonHousing")

dt <- BostonHousing

我首先将数据集拆分为 train/test 个子集。

dt_split <- initial_split(dt)
dt_train <- training(dt_split)
dt_test <- testing(dt_split)

使用 recipe 包定义预处理。

rec <- recipe(medv ~ ., data = dt_train) %>%
  step_center(all_predictors(), -all_nominal()) %>% 
  step_dummy(all_nominal()) %>% 
  prep()

模型和工作流的初始化。我使用 glmnet 引擎。 mixture = 1表示我选择LASSO惩罚,penalty = tune()表示我稍后使用交叉验证来选择最佳惩罚参数lambda

lasso_mod <- linear_reg(mode = "regression",
                        penalty = tune(),
                        mixture = 1) %>% 
  set_engine("glmnet")

wf <- workflow() %>%
  add_model(lasso_mod) %>%
  add_recipe(rec)

准备分层 5 折交叉验证和惩罚网格:

folds <- rsample::vfold_cv(dt_train, v = 5, strata = medv, nbreaks = 5)
my_grid <- tibble(penalty = 10^seq(-2, -1, length.out = 10))

让我们运行进行交叉验证:

my_res <- wf %>% 
  tune_grid(resamples = folds,
            grid = my_grid,
            control = control_grid(verbose = FALSE, save_pred = TRUE),
            metrics = metric_set(rmse))

我现在可以从网格中获得最佳惩罚并更新我的工作流程以获得最佳惩罚:

best_mod <- my_res %>% select_best("rmse")
print(best_mod)

final_wf <- finalize_workflow(wf, best_mod)
print(final_wf)

== Workflow ===================================================================================================================
Preprocessor: Recipe
Model: linear_reg()

-- Preprocessor ---------------------------------------------------------------------------------------------------------------
2 Recipe Steps

* step_center()
* step_dummy()

-- Model ----------------------------------------------------------------------------------------------------------------------
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = 0.0278255940220712
  mixture = 1

Computational engine: glmnet 

到目前为止一切顺利。现在我想将工作流应用于训练数据以获得我的最终模型:

final_mod <- fit(final_wf, data = dt_train) %>%
  pull_workflow_fit()

问题来了。

final_mod$fit 是一个 elnetglmnet 对象。它包含 75 个惩罚参数值的网格上的完整正则化路径。因此,之前的惩罚调整步骤几乎没有用。所以预测步骤失败:

predict(final_mod, new_data = dt)returns一个错误:

Error in cbind2(1, newx) %*% nbeta : 
  invalid class 'NA' to dup_mMatrix_as_dgeMatrix

当然,我可以使用 glmnet::cv.glmnet 获得最佳惩罚,然后使用方法 predict.cv.glmnet,但我需要一个能够使用相同界面处理多个机器学习模型的通用工作流。在 parsnip::linear_regdocumentation 中有关于 glmnet 引擎的注释:

For glmnet models, the full regularization path is always fit regardless of the value given to penalty. Also, there is the option to pass multiple values (or no values) to the penalty argument. When using the predict() method in these cases, the return value depends on the value of penalty. When using predict(), only a single value of the penalty can be used. When predicting on multiple penalties, the multi_predict() function can be used. It returns a tibble with a list column called .pred that contains a tibble with all of the penalty results.

但是,我不明白我应该如何使用 tidymodels 框架继续获得调整后的 LASSO 模型的预测。 multi_predict 函数抛出与 predict.

相同的错误

你真的很接近一切正常。

让我们读入数据,将其拆分为 training/testing 并创建重采样折叠。

library(tidymodels)
library(tidyverse)
library(mlbench)

data("BostonHousing")

dt <- BostonHousing

dt_split <- initial_split(dt)
dt_train <- training(dt_split)
dt_test <- testing(dt_split)
folds <- vfold_cv(dt_train, v = 5, strata = medv, nbreaks = 5)

现在让我们创建一个预处理配方。 (请注意,如果您使用的是 workflow(),则不需要 prep();如果您的数据很大,那可能会变慢,所以最好在 workflow() 之前不要这样做稍后为您处理。)

rec <- recipe(medv ~ ., data = dt_train) %>%
    step_center(all_predictors(), -all_nominal()) %>% 
    step_dummy(all_nominal())

现在让我们制作我们的模型,将其与我们的食谱放在一起 workflow(),并使用网格调整工作流程。

lasso_mod <- linear_reg(mode = "regression",
                        penalty = tune(),
                        mixture = 1) %>% 
    set_engine("glmnet")

wf <- workflow() %>%
    add_model(lasso_mod) %>%
    add_recipe(rec)

my_grid <- tibble(penalty = 10^seq(-2, -1, length.out = 10))

my_res <- wf %>% 
    tune_grid(resamples = folds,
              grid = my_grid,
              control = control_grid(verbose = FALSE, save_pred = TRUE),
              metrics = metric_set(rmse))

这是我们得到的最好的处罚:

best_mod <- my_res %>% select_best("rmse")
best_mod
#> # A tibble: 1 x 2
#>   penalty .config              
#>     <dbl> <chr>                
#> 1  0.0215 Preprocessor1_Model04

我们在这方面的做法与您的做法略有不同。我将 finalize 我的工作流程,然后 fit 将最终工作流程与训练数据相匹配。此处的输出是一个 拟合工作流 。我不想从中提取底层模型,因为模型 需要 预处理才能正常工作;它经过训练,期望进行预处理。

相反,我可以 predict() 直接使用训练有素的工作流程:

final_fitted <- finalize_workflow(wf, best_mod) %>%
    fit(data = dt_train)

predict(final_fitted, dt_train)
#> # A tibble: 379 x 1
#>    .pred
#>    <dbl>
#>  1  18.5
#>  2  24.2
#>  3  23.3
#>  4  21.6
#>  5  37.6
#>  6  21.5
#>  7  16.7
#>  8  15.6
#>  9  21.3
#> 10  21.3
#> # … with 369 more rows
predict(final_fitted, dt_test)
#> # A tibble: 127 x 1
#>    .pred
#>    <dbl>
#>  1  30.2
#>  2  25.1
#>  3  19.6
#>  4  17.0
#>  5  13.9
#>  6  15.4
#>  7  13.7
#>  8  20.8
#>  9  31.1
#> 10  21.3
#> # … with 117 more rows

reprex package (v1.0.0)

于 2021 年 3 月 16 日创建

如果您调整工作流程,那么您通常希望最终确定、调整和预测工作流程。如果您在工作流程中使用非常简单的预处理器(例如可以传递给 fit() 的公式),则可能会有例外;我show an example that you could do that with here.