R caret: Как применить отдельный PCA к различным кадрам данных перед тренировкой? - PullRequest
3 голосов
/ 23 декабря 2019

Я использую каретку в R. Моя конечная цель состоит в том, чтобы представить различные кадры данных для разделения pca preProcess, а затем соединить компоненты PCA в одну тренировку с регрессией гребня. Тем не менее, см. Пример кода ниже, где я не получаю те же результаты при применении pca в preProcess в сравнении с функцией снаружи / перед поездом.

  1. Почему я не получаю те же результаты?
  2. А как мне лучше всего достичь своей главной цели?
#Sample data
s <- c(-0.412440717220306, -0.459911376237869, -0.234769582748413, -0.332282930612564, -0.486973077058792, -0.301480442285538, -0.181094691157341, -0.240918189287186, 0.0962697193026543, -0.119731709361076, -0.389783203601837, -0.217093095183372, -0.302948802709579, -0.406619131565094, 0.247409552335739, -0.406119048595428, 0.0574243739247322, -0.301231145858765, -0.229316398501396, -0.0620433799922466)
t <- c(0.20061232149601, 0.0536709427833557, 0.530373573303223, 0.523406386375427, 0.267315864562988, 0.413556098937988, 0.274257719516754,  0.275401413440704, 0.634453296661377, 0.145272701978683, 0.196711808443069, 0.332845687866211, 0.345706522464752, 0.444085538387299, 0.253269702196121, 0.231440827250481, -0.196317762136459, 0.49691703915596, 0.43754768371582, 0.0106721892952919)
u <- c(-0.565160751342773, 0.377725303173065,-0.273447960615158, -0.338064402341843, -0.59904420375824, -0.780133605003357,-0.508388638496399, -0.226167500019073, -0.257708549499512, -0.349863946437836,-0.443032741546631, -0.36387038230896, -0.455201774835587, -0.137616977095604,0.130770832300186, -0.420618057250977, -0.125859051942825, -0.382272869348526, -0.355217516422272, -0.0601325333118439)
v <- c(-0.45850995182991, -0.0105021595954895, -0.475157409906387, -0.325350821018219, -0.548444092273712, -0.562069535255432, -0.473256289958954, -0.492668628692627, -0.205974608659744, -0.266964733600616, -0.289298176765442, -0.615423858165741, -0.261823982000351, -0.472221553325653,  -0.684594392776489, -0.42777806520462, -0.240604877471924, -0.589631199836731, -0.782602787017822, -0.468854814767838)
w <- c(-0.886135756969452, -0.96577262878418,-0.755464434623718, -0.640497982501984, -0.849709093570709, -0.837802410125732, -0.659287571907043, -0.646972358226776, 0.0532735884189606, -0.646163880825043,-0.963890254497528, -0.91286826133728, -1.10484659671783, -0.596551716327667,  -0.371927708387375, -0.684276521205902, -0.55376398563385, -0.969008028507233, -0.956810772418976, -0.0229262933135033)

y <- c(9, 26, 30, 15, 25, 30, 30, 35, 35, 30, 21, 30,  9, 33, 31, 34, 29, 35, 25, 31)

#Sample data for procedure 1 and 2
df_test1 <- data.frame(s, t, u, v, w)
df_test2 <- df_test1

#PROCEDURE 1: preProcess (pca) applied WITHIN "train" function
library(caret)    
ytrain_df_test <- c(1:nrow(df_test1)) # number of observation that should be split in to the number of folds.
ntrain <- length(ytrain_df_test)    
# define folds
cv_folds <- createFolds(ytrain_df_test, k = 10, list = TRUE, returnTrain = TRUE) #, ...)
# define training control
train_control <- trainControl(method="cv", index = cv_folds, savePredictions = 'final') #, ...)
#adding y
df_test1$y <- y
# train the model
set.seed(1)
model1 <- caret::train(y~., data=df_test1, trControl=train_control, method= 'ridge', preProcess = 'pca')
output1 <- list(model1, model1$pred, summary(model1$pred), cor.test(model1$pred$pred, model1$pred$obs))
names(output1) <- c("Model", "Model_pred", "Summary", "Correlation")
output1


#PROCEDURE 2: preProcess (pca) applied OUTSIDE/BEFORE "train" function
ytrain_df_test <- c(1:nrow(df_test2)) # number of observation that should be split in to the number of folds.
ntrain <- length(ytrain_df_test)    

df2 <- preProcess(df_test2, method="pca", thresh = 0.95)
df_test2 <- predict(df2, df_test2)
df_test2$y <- y
df_test2
# define folds
cv_folds <- createFolds(ytrain_df_test, k = 10, list = TRUE, returnTrain = TRUE) 
# define training control
train_control <- trainControl(method="cv", index = cv_folds, savePredictions = 'final') 
# train the model
set.seed(1)
model2 <- caret::train(y~., data=df_test2, trControl=train_control, method= 'ridge') #, preProcess = 'pca')
model2
output2 <- list(model2, model2$pred, summary(model2$pred), cor.test(model2$pred$pred, model2$pred$obs))
names(output2) <- c("Model", "Model_pred", "Summary", "Correlation")
output2```

1 Ответ

3 голосов
/ 25 декабря 2019

1. при выполнении предварительной обработки (pca) в функции поезда:

  • pca запускается на каждом наборе поездов во время CV, и набор поездов преобразуется
  • оценивается несколько моделей регрессии гребня (на основе поиска определенных гиперпараметров) для каждого из этих преобразованных наборов поездов.
  • на основе pca, полученного для каждого набора поездов, соответствующий набор тестов преобразуется
  • все подходящие модели оцениваются на соответствующих преобразованных тестовых наборах

Когда это закончено, окончательная модель строится с гиперпараметрами, которые имели наилучшие средние характеристики на тестовых наборах:

  • pca применяется ко всем данным набора поездов и получается преобразованные данные поезда.
  • с использованием предварительно выбранных гиперпараметров модель регрессии гребня строится на преобразованных данных поезда

Когда вы выполняете предварительную обработку (pca) перед функцией поезда, вы вызываете утечку данных , так как выпойте информацию из ваших тестовых сгибов CV для оценки координат pca. Это вызывает оптимистический уклон во время CV и его следует избегать.

2. Мне не известны встроенные функции каретки, которые бы обеспечивали это жонглирование несколькими наборами данных. Я верю, что это может быть достигнуто с mlr3pipelines . Особенно этот учебник удобен.

Вот пример того, как разделить набор данных радужной оболочки на два набора данных, применить масштабирование и pca к каждому из них, объединить преобразованные столбцы и подобрать модель rpart. Настройка количества сохраняемых компонентов PCA, а также одного гиперпараметра rpart с помощью случайного поиска:

пакетов:

library(mlr3pipelines)
library(visNetwork)
library(mlr3learners)
library(mlr3tuning)
library(mlr3)  
library(paradox)

определяет конвейер селектора с именем "slct1":

pos1 <- po("select", id = "slct1")

скажите ему, какие столбцы выбрать:

pos1$param_set$values$selector <- selector_name(c("Sepal.Length", "Sepal.Width"))

скажите, что делать после того, как он получит функции

pos1 %>>%
  mlr_pipeops$get("scale", id = "scale1") %>>%
  mlr_pipeops$get("pca", id = "pca1") -> pr1

определите селектор pipeop с именем "slct2":

pos2 <- po("select", id = "slct2")

скажите ему, какие столбцы выбрать:

pos2$param_set$values$selector <- selector_name(c("Petal.Length", "Petal.Width"))

скажите, что делать после того, как он получит функции

pos2 %>>%
   mlr_pipeops$get("scale", id = "scale2") %>>%
   mlr_pipeops$get("pca", id = "pca2") -> pr2

объедините два выхода:

piper <- gunion(list(pr1, pr2)) %>>%
  mlr_pipeops$get("featureunion")

и отправьте их ученику:

graph <- piper %>>%
  mlr_pipeops$get("learner",
                  learner = mlr_learners$get("classif.rpart"))

давайте проверим, как это выглядит:

graph$plot(html = TRUE)

enter image description here

Теперь определите, как это должно быть настроено:

glrn <- GraphLearner$new(graph)

10 кратное CV:

cv10 <- rsmp("cv", folds = 10)

Настройте количество измерений PCA, сохраненных для каждого набора данных, а также параметр сложности rpart:

ps <- ParamSet$new(list(
  ParamDbl$new("classif.rpart.cp", lower = 0, upper = 1),
  ParamInt$new("pca1.rank.",  lower = 1, upper = 2),
  ParamInt$new("pca2.rank.",  lower = 1, upper = 2)
))

определить задачу и настройку:

task <- mlr_tasks$get("iris")

instance <- TuningInstance$new(
  task = task,
  learner = glrn,
  resampling = cv10,
  measures = msr("classif.ce"),
  param_set = ps,
  terminator = term("evals", n_evals = 20)
)

Инициировать случайный поиск:

tuner <- TunerRandomSearch$new()
tuner$tune(instance)
instance$result

Возможно, это также можно сделать with tidymodels hover Я еще не попробовал их.

РЕДАКТИРОВАТЬ: чтобы ответить на вопросы в комментариях.

Чтобы полностью понять mlr3, я советую вам прочитать книгу , а также учебные пособия для каждого из дополнительных пакетов.

В приведенном выше примере количество измерений PCA, сохраняемых для каждого из наборов данных, было настроено совместно с гиперпараметром cp. Это было определено в этой строке:

ps <- ParamSet$new(list(
  ParamDbl$new("classif.rpart.cp", lower = 0, upper = 1),
  ParamInt$new("pca1.rank.",  lower = 1, upper = 2),
  ParamInt$new("pca2.rank.",  lower = 1, upper = 2)
)) 

Таким образом, для pca1 алгоритм мог выбрать 1 или 2 ПК для сохранения (я установил его таким образом, поскольку в каждом наборе данных есть только две функции)

Если вы не хотите настраивать количество измерений для оптимизации производительности, вы можете определить pipeop следующим образом:

pos1 %>>%
  mlr_pipeops$get("scale", id = "scale1") %>>%
  mlr_pipeops$get("pca", id = "pca1", param_vals = list(rank. = 1)) -> pr1

, в этом случае вы должны опустить его в наборе параметров:

ps <- ParamSet$new(list(
  ParamDbl$new("classif.rpart.cp", lower = 0, upper = 1)
))

Насколько я знаю, объяснение отклонения в настоящее время не может быть изменено только для количества сохраненных измерений для преобразования pca.

Чтобы изменить тип предсказания, можно определить ученика:

learner <- mlr_pipeops$get("learner",
                            learner = mlr_learners$get("classif.rpart"))

и задайте тип прогнозирования:

learner$learner$predict_type <- "prob"

, а затем создайте график:

graph <- piper %>>%
  learner

Чтобы получить производительность для каждой комбинации гиперпараметров:

instance$archive(unnest = "params")

Чтобы получить прогнозы для каждой комбинации гиперпараметров:

lapply(as.list(instance$archive(unnest = "params")[,"resample_result"])$resample_result,
       function(x) x$predictions())

Чтобы получить прогнозы для лучшей комбинации гиперпараметров:

instance$best()$predictions()

Если вам это нужно в виде фрейма данных:

do.call(rbind,
        lapply(instance$best()$predictions(),
               function(x) data.frame(x$data$tab,
                                      x$data$prob)))

, вероятно, есть некоторые дополнительные функции, которые делают это проще, я просто не наигрался.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...