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

Aug*_*tin 5 r machine-learning lasso-regression tidymodels

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

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

data("BostonHousing")

dt <- BostonHousing
Run Code Online (Sandbox Code Playgroud)

我首先将数据集分成训练/测试子集。

dt_split <- initial_split(dt)
dt_train <- training(dt_split)
dt_test <- testing(dt_split)
Run Code Online (Sandbox Code Playgroud)

使用recipe包定义预处理。

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

模型和工作流程的初始化。我用的是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)
Run Code Online (Sandbox Code Playgroud)

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

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

让我们运行交叉验证:

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

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

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 
Run Code Online (Sandbox Code Playgroud)

到目前为止,一切都很好。现在我想将工作流程应用于训练数据以获得最终模型:

final_mod <- fit(final_wf, data = dt_train) %>%
  pull_workflow_fit()
Run Code Online (Sandbox Code Playgroud)

现在问题来了。

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

predict(final_mod, new_data = dt)返回错误:

Error in cbind2(1, newx) %*% nbeta : 
  invalid class 'NA' to dup_mMatrix_as_dgeMatrix
Run Code Online (Sandbox Code Playgroud)

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

对于 glmnet 模型,无论给予惩罚的值如何,完整的正则化路径始终适合。此外,还可以选择将多个值(或不传递任何值)传递给penalty 参数。在这些情况下使用predict()方法时,返回值取决于惩罚值。当使用predict()时,只能使用单个惩罚值。当预测多重惩罚时,可以使用 multi_predict() 函数。它返回一个带有名为 .pred 的列表列的 tibble,其中包含带有所有惩罚结果的 tibble。

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

Jul*_*lge 11

你已经非常接近让一切正常运转了。

\n

让我们读入数据,将其拆分为训练/测试并创建重采样折叠。

\n
library(tidymodels)\nlibrary(tidyverse)\nlibrary(mlbench)\n\ndata("BostonHousing")\n\ndt <- BostonHousing\n\ndt_split <- initial_split(dt)\ndt_train <- training(dt_split)\ndt_test <- testing(dt_split)\nfolds <- vfold_cv(dt_train, v = 5, strata = medv, nbreaks = 5)\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们创建一个预处理方案。prep()(请注意,如果您使用的是 ,则不需要它workflow();如果您的数据很大,则可能会变慢,因此最好不要这样做,直到workflow()稍后为您处理为止。)

\n
rec <- recipe(medv ~ ., data = dt_train) %>%\n    step_center(all_predictors(), -all_nominal()) %>% \n    step_dummy(all_nominal())\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
lasso_mod <- linear_reg(mode = "regression",\n                        penalty = tune(),\n                        mixture = 1) %>% \n    set_engine("glmnet")\n\nwf <- workflow() %>%\n    add_model(lasso_mod) %>%\n    add_recipe(rec)\n\nmy_grid <- tibble(penalty = 10^seq(-2, -1, length.out = 10))\n\nmy_res <- wf %>% \n    tune_grid(resamples = folds,\n              grid = my_grid,\n              control = control_grid(verbose = FALSE, save_pred = TRUE),\n              metrics = metric_set(rmse))\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
best_mod <- my_res %>% select_best("rmse")\nbest_mod\n#> # A tibble: 1 x 2\n#>   penalty .config              \n#>     <dbl> <chr>                \n#> 1  0.0215 Preprocessor1_Model04\n
Run Code Online (Sandbox Code Playgroud)\n

我们的做法与您的做法略有不同。我将以最好的惩罚来最终确定我的工作流程,然后将最终确定的工作流程适合训练数据。这里的输出是一个合适的工作流程。我不想从中提取底层模型,因为该模型需要预处理才能正常工作;它经过训练,期望进行预处理。

\n

相反,我可以直接predict()执行经过训练的工作流程:

\n
final_fitted <- finalize_workflow(wf, best_mod) %>%\n    fit(data = dt_train)\n\npredict(final_fitted, dt_train)\n#> # A tibble: 379 x 1\n#>    .pred\n#>    <dbl>\n#>  1  18.5\n#>  2  24.2\n#>  3  23.3\n#>  4  21.6\n#>  5  37.6\n#>  6  21.5\n#>  7  16.7\n#>  8  15.6\n#>  9  21.3\n#> 10  21.3\n#> # \xe2\x80\xa6 with 369 more rows\npredict(final_fitted, dt_test)\n#> # A tibble: 127 x 1\n#>    .pred\n#>    <dbl>\n#>  1  30.2\n#>  2  25.1\n#>  3  19.6\n#>  4  17.0\n#>  5  13.9\n#>  6  15.4\n#>  7  13.7\n#>  8  20.8\n#>  9  31.1\n#> 10  21.3\n#> # \xe2\x80\xa6 with 117 more rows\n
Run Code Online (Sandbox Code Playgroud)\n

由reprex 包于 2021 年 3 月 16 日创建(v1.0.0)

\n

如果您调整工作流程,那么您通常需要最终确定、调整和预测工作流程。如果您在工作流程中使用非常简单的预处理器(例如可以传递给的公式),则可能会出现例外情况fit();我展示了一个您可以在此处执行此操作的示例

\n