Введение
В этой статье я буду использовать фреймворк Tidymodels в R для построения модели классификации на титаническом наборе данных.
Инфраструктура Tidymodels позволяет использовать разработку функций, проверку модели, выбор модели и многое другое в элегантном, простом и эффективном стиле Tidyverse.
Исследование данных
В этой статье я буду использовать титанический набор данных с этого конкурса Kaggle. Обзор данных показан ниже.
Наша цель будет состоять в том, чтобы предсказать выживание пассажиров на основе других функций в наборе данных. Давайте начнем с загрузки пакетов, которые мы будем использовать, считывания данных и просмотра общего обзора.
library(tidyverse) library(tidymodels) library(janitor) library(skimr) titanic_data <- read_csv("train.csv") |> clean_names() test_data <- read_csv("test.csv") |> mutate( survived = NA ) |> clean_names() skim(titanic_data)
Мы можем сделать следующие наблюдения:
- имя содержит 891 уникальную запись, что эквивалентно количеству строк в наборе данных, поэтому мы исключим его из нашего анализа.
- sex — это символ, поэтому мы закодируем эту переменную в горячем режиме.
- ticket, как и name, имеет большое количество уникальных значений, поэтому мы также исключим это.
- cabin имеет большое количество пропущенных значений, поэтому мы создадим для него индикаторный столбец NA. Кроме того, кабина имеет формат B55, E17, C92 и т. д., поэтому мы сохраним только первую букву.
- начало будет закодировано горячим способом.
- passenger_id, как и name, уникален, поэтому мы его исключим.
- pclass пока кажется хорошим.
- В возрасте отсутствуют значения, которые мы будем вычислять с помощью медианы.
- sib_sp, parch,и fare пока все в порядке.
Разработка функций
Теперь мы готовы преобразовать набор данных в соответствии с приведенными выше спецификациями. Вот этапы разработки функций в коде.
model_recipe <- titanic_data |> recipe(survived ~ .) |> step_select( -c( name, ticket, passenger_id ) ) |> step_mutate( cabin = as.factor( str_sub( cabin, start = 1, end = 1 ) ) ) |> step_indicate_na( all_predictors() ) |> step_dummy( all_nominal_predictors() ) |> step_impute_median( all_numeric_predictors() ) |> step_nzv( all_numeric_predictors() ) |> step_corr( all_numeric_predictors() ) |> step_BoxCox( all_numeric_predictors() ) |> step_normalize( all_numeric_predictors() )
Вот объяснение каждого шага в коде:
- step_select, чтобы исключить переменные ticket,passage_id,иимя.
- step_mutate, чтобы получить только первую букву из переменной cabin.
- step_indicate_na, чтобы создать столбцы индикаторов NA для переменных с отсутствующими наблюдениями.
- step_dummy, чтобы создать фиктивные переменные для каждого из категориальных предикторов.
- step_impute_median для расчета медианы.
- step_nzv для удаления предикторов с почти нулевой дисперсией.
- step_corr для удаления предикторов с высокой степенью корреляции (таких как sex_male и sex_female, созданных однократным кодированием sex).
- step_BoxCox, чтобы выполнить преобразование кокса с данными (преобразование данных для более нормального распределения).
- step_normalize для центрирования и масштабирования данных.
Порядок этих шагов важен!
Теперь давайте применим преобразование к данным и просмотрим результат.
titanic_data_transformed <- prep(model_recipe) |> bake(new_data = titanic_data) skim(titanic_data_transformed)
Давайте также посмотрим на график корреляции предикторов и выжившего столбца.
Мы видим, что переменные действительно обладают предсказательной силой, поэтому мы должны увидеть довольно сильный результат.
Выбор модели
Мы попытаемся подогнать четыре разные модели к набору данных и посмотреть, какая из них работает лучше всего. Мы попробуем регрессию случайного леса, логистическую регрессию, регрессию ближайшего соседа и регрессию дерева решений.
Следующая функция — это оболочка, которую я создал для шагов Tidymodels, описанных в этой статье. Эта функция включает в себя разделение теста/обучения, перекрестную проверку для автоматической настройки гиперпараметров и подгонку модели.
# Function to create a model create_model <- function(data, formula, model_type, mode = "regression", n_folds = 10, seed = 101) { # Model recipe model_recipe <- data |> recipe(formula) # Model mode and engine model <- model_type |> set_mode(mode) # Wrap in a workflow model_workflow <- workflow() |> add_recipe(model_recipe) |> add_model(model) # Split the data set.seed(seed) data_split <- initial_split( as.data.frame(data), strata = all.vars(formula)[1] ) # Create data folds set.seed(seed) data_folds <- vfold_cv( training(data_split), v = n_folds, strata = all.vars(formula)[1] ) # Select best model set.seed(seed) best_model <- tune_grid( model_workflow, resamples = data_folds ) |> select_best() # Finalize the model model_workflow_final <- finalize_workflow( model_workflow, best_model ) # Fit the model set.seed(seed) model_fit <- model_workflow_final |> last_fit( data_split ) }
Вот код для установки четырех моделей. Обратите внимание, что я изменяю выживший столбец на фактор (требование для прогнозирования с классификацией) и устанавливаю параметры моделей, которые хочу настроить, с помощью функции tune()
.
Примечание. Для запуска следующего кода вам потребуются установленные пакеты ranger и kknn.
rf_model <- titanic_data_transformed |> mutate( survived = factor(survived) ) |> create_model( survived ~ ., rand_forest( mtry = tune(), min_n = tune() ), "classification", 3 ) lr_model <- titanic_data_transformed |> mutate( survived = factor(survived) ) |> create_model( survived ~ ., logistic_reg(), "classification", 3 ) nn_model <- titanic_data_transformed |> mutate( survived = factor(survived) ) |> create_model( survived ~ ., nearest_neighbor( neighbors = tune() ), "classification", 3 ) dt_model <- titanic_data_transformed |> mutate( survived = factor(survived) ) |> create_model( survived ~ ., decision_tree( min_n = tune() ), "classification", 3 )
Давайте теперь проверим результаты.
do.call( bind_rows, args = list( collect_metrics(rf_model), collect_metrics(lr_model), collect_metrics(nn_model), collect_metrics(dt_model) ) ) |> mutate( model = c( rep("random_forest", 2), rep("logistic_regression", 2), rep("nearest_neighbor", 2), rep("decision_tree", 2) ) ) |> select( -c(.config) )
Мы видим, что наиболее эффективной моделью была модель ближайшего соседа с точностью 0,804 и оценкой ROC_AUC 0,855.
Мы можем выбрать эту модель и собрать прогнозы нашей модели для набора тестовых данных, предоставленного Kaggle.
test_data_transformed <- prep(model_recipe) |> bake(new_data = test_data) result <- predict( object = extract_fit_parsnip( nn_model ), new_data = test_data_transformed, type = "class" )
Результат
Я загрузил фрейм данных результата в CSV и загрузил его в Kaggle. Моя оценка точности была 0,75837.
Вот полная копия кода из этой статьи: MCodrescu/titanic_data_model (github.com)
Спасибо за прочтение!
Дополнительная информация о R доступна на странице medium.com/r-evolution. Подпишитесь на нашу рассылку новостей и следите за нами в Твиттере.