首页 > 解决方案 > 调整 LASSO 模型并使用 tidymodels 进行预测

问题描述

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

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)

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)返回错误:

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

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

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

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

标签: rmachine-learninglasso-regressiontidymodels

解决方案


你真的很接近让一切正常工作。

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

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)

现在让我们创建一个预处理配方。(请注意,prep()如果您使用的是workflow(); 如果您的数据很大,这可能会变慢,所以最好不要这样做,直到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

在这里,我们做的事情与您所做的有点不同。我将用最好的惩罚来完成我的工作流程,然后最终的工作流程与训练数据相匹配。这里的输出是一个合适的工作流。我不想把底层模型拉出来,因为模型需要预处理才能正常工作;它被训练期望预处理发生。

相反,我可以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 包(v1.0.0)于 2021-03-16 创建

如果您调整工作流程,那么您通常希望最终确定、适应和预测工作流程。如果您在工作流程中使用非常简单的预处理器(例如可以传递给的公式),则可能会出现例外情况fit();我举了一个例子,你可以在这里做到这一点


推荐阅读