Настройка модели LASSO и прогнозирование с использованием тидимоделей

Я хочу выполнить выбор штрафа для алгоритма 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 - это объект elnet и glmnet. Он содержит полный путь регуляризации в сетке из 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 полный путь регуляризации всегда подходит, независимо от значения, присвоенного штрафу. Кроме того, в аргумент штрафа можно передать несколько значений (или без значений). При использовании метода predic () в этих случаях возвращаемое значение зависит от значения штрафа. При использовании pred () можно использовать только одно значение штрафа. При прогнозировании нескольких штрафов можно использовать функцию multi_predict (). Он возвращает тиббл со столбцом списка с именем .pred, который содержит тиббл со всеми результатами штрафа.

Однако я не понимаю, как мне поступить, чтобы получить прогнозы настроенной модели LASSO с использованием tidymodels фреймворка. Функция multi_predict выдает ту же ошибку, что и predict.


person Augustin    schedule 15.03.2021    source источник
comment
Обновите свой вопрос, включив в него точную ошибку, возвращенную predict(final_mod, new_data = dt).   -  person desertnaut    schedule 15.03.2021


Ответы (1)


Вы действительно близки к тому, чтобы все работало правильно.

Давайте прочитаем данные, разделим их на обучение / тестирование и создадим свертки передискретизации.

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

Создано 16 марта 2021 года пакетом REPEX (v1.0.0)

Если вы настраиваете рабочий процесс, то обычно вам нужно доработать, подогнать и спрогнозировать рабочий процесс. Исключения могут быть, если вы используете в своем рабочем процессе действительно простой препроцессор, например формулу, которую можно передать fit(); Я показываю пример, с которым вы могли бы сделать это здесь.

person Julia Silge    schedule 16.03.2021