Узнайте, как использовать конвейеры scikit-learn в рабочих процессах Python и Tidy Models в R

Введение:

Эксперимент по науке о данных или процесс построения модели представляет собой серию шагов, которые выполняются в определенной последовательности и чаще всего с несколькими итерациями. Акцент быстро смещается на отслеживание точности модели и связанных показателей, но есть много других аспектов, которые могут сделать весь процесс более структурированным, упрощая не только чтение кода, но и воспроизведение результатов. В этом блоге мы рассмотрим конвейеры scikit-learn и рабочие процессы Tidymodel с одним и тем же набором данных. Сначала мы рассмотрим построение моделей машинного обучения в неструктурированном подходе, а затем перейдем к построению тех же моделей с использованием Python, а затем с помощью R.

Оглавление

  1. Неструктурированный подход к построению модели
  2. Исследование и обработка данных
  3. Построение модели с конвейерами в Python
  4. Построение модели с конвейерами в R
  5. Дальнейшие исследования/эксперименты

Загрузить пакеты и данные Python: мы загрузим пакеты. Если у вас нет библиотеки, установите ее с помощью простой команды pip install ‹libraryname›. В этом блоге мы будем использовать Набор данных ВГС.

import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import StandardScaler,MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
# Load Dataset
df = pd.read_csv("hcvdata.csv")

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

df.describe()

df.dtypes
Category       object
Age             int64
Sex            object
ALB           float64
ALP           float64
ALT           float64
AST           float64
BIL           float64
CHE           float64
CHOL          float64
CREA          float64
GGT           float64
PROT          float64

Давайте также найдем процент пропущенных значений в наборе данных.

total = df.isnull().sum().sort_values(ascending=False)
percent = (df.isnull().sum()/df.isnull().count()).sort_values(ascending=False)*100
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data
output:
          Total   Percent
ALP       18      2.926829
CHOL      10      1.626016
ALB       1       0.162602
ALT       1       0.162602
PROT      1       0.162602
Category  0       0.000000
Age       0       0.000000
Sex       0       0.000000
AST       0       0.000000
BIL       0       0.000000
CHE       0       0.000000
CREA      0       0.000000
GGT       0       0.00000

Наблюдения:

  • Есть две категориальные переменные, а именно Категория и Пол, из которых Категория является нашей целевой переменной. Эти две переменные потребуют некоторой обработки, которую мы сделаем в следующем разделе.
  • В некоторых переменных отсутствуют значения, которые необходимо устранить, прежде чем мы начнем построение модели.

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

cat_dict = {'0=Blood Donor':0, '0s=suspect Blood Donor':0, '1=Hepatitis':1,
'2=Fibrosis':2, '3=Cirrhosis':3}
df['Category'].map(cat_dict)
df['Category'] = df['Category'].map(cat_dict)
df['Sex'] = df['Sex'].map({'m':1,'f':0})
df = df[['Age', 'Sex', 'ALB', 'ALP', 'ALT', 'AST','BIL', 'CHE', 'CHOL', 'CREA', 'GGT', 'PROT','Category']]
df.fillna(0,inplace=True)

Разделение данных:

# Features & Labels
X_features = df.drop('Category',axis=1)
y_labels = df['Category']
# Train Test Split
X_train,X_test,y_train,y_test = train_test_split(X_features, y_labels,
test_size=0.3,random_state=42)

Построение модели в Python. Мы построим модель логистической регрессии.

lr = LogisticRegression()
lr.fit(X_train,y_train)
# Prediction Accuracy
print("Accuracy:", lr.score(X_test,y_test))
Output:
Accuracy: 0.9243243243243243

Давайте построим ту же модель, но на этот раз с конвейерами.

Построение модели с конвейерами.Существуют определенные этапы построения модели, начиная с загрузки данных и заканчивая измерением окончательных показателей модели, и каждый из этих этапов является объектом. Назначение конвейеров — собрать все объекты в одну структурированную последовательность. Это значительно повышает читабельность кода и возможность его повторного использования.

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier
df = pd.read_csv("hcvdata.csv")
# Train Test Split
X_train,X_test,y_train,y_test = train_test_split(
X_features, y_labels,test_size=0.3,random_state=42)
numeric_features = df.select_dtypes(include=['int64', 'float64']).drop(['Unnamed: 0'], axis =1).columns
categorical_features = df.select_dtypes(include=['object']).drop(['Category'], axis=1).columns

Предварительная обработка. На этом шаге мы определим вид преобразования, которое нам нужно выполнить для наших numeric_features и categorical_features. Например: отсутствующее значение вменение, стандартизация данных или тип кодирования и т. д.

numeric_transformer = Pipeline(steps=[
     ('imputer', SimpleImputer(strategy='median')),    
     ('scaler', StandardScaler())]) 
categorical_transformer = Pipeline(steps=[
     ('imputer', SimpleImputer(strategy='constant', fill_value= 2)),
     ('onehot', OneHotEncoder(handle_unknown='ignore'))])
preprocessor = ColumnTransformer(transformers=[
     ('num', numeric_transformer, numeric_features),         
     ('cat', categorical_transformer, categorical_features)])

Теперь, когда мы определили наши функции и соответствующие преобразования, которые необходимо выполнить, мы перенесем их в конвейер в следующем разделе.

Создание конвейера в Python. Мы определим конвейер и подключим к нему уже определенные объекты.

rf = Pipeline(steps=[('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier())
])
pipe_lr = pipe_lr.fit(X_train, y_train)
print('Accuracy score: ', pipe_lr.score(X_test, y_test))
Output:
Accuracy score:  0.9135

Создание нескольких моделей и сравнение. Давайте создадим модели Random Forest и KNeigbors Classifiers, используя уже созданный конвейер — повторное использование.

classifiers = [KNeighborsClassifier(3),RandomForestClassifier(),
LogisticRegression()]
for classifier in classifiers:
   pipe = Pipeline(steps=[('preprocessor', preprocessor),  
                             ('classifier', classifier)])
   pipe.fit(X_train, y_train)
   print(classifier)
   print("model score: %.3f" % pipe.score(X_test, y_test))
Output:
KNeighborsClassifier(n_neighbors=3)
model score: 0.854
RandomForestClassifier()
model score: 0.892
LogisticRegression()
model score: 0.892

Мы попробовали два подхода, и процесс, сопровождаемый конвейером, делает рабочий процесс структурированным, последовательным с улучшенной читабельностью кода и легкой воспроизводимостью.

Построение модели в R:Подобно конвейерам в Python, у нас есть рабочие процессы в R. В R термин конвейер широко используется для объединения последовательности операций с %›%. Во избежание двусмысленности последовательность операций в контексте построения модели называется рабочим процессом. В следующем разделе мы попытаемся воспроизвести все шаги в R.

Загрузить пакеты и данные:

library(tidymodels)
library(tidyverse)
library(workflows)
library(tune)
library(nnet)
setwd('<Set your working directory here>')
df <- read.csv('hcvdata.csv')

Предварительная обработка. Мы разделим данные и разделим числовые и категориальные переменные.

hcv_split <- initial_split(df, prop = 3/4)
# extract training and testing sets
hcv_train <- training(hcv_split)
hcv_test <- testing(hcv_split)
numeric_features <- df1 %>% 
  select_if(is.numeric)
names(numeric_features)
categorical_features <- df1 %>% 
  select_if(is.character) %>% 
  select(Sex)
names(categorical_features)

Построение конвейера в R. Терминология в Tidy Models немного отличается. Давайте познакомимся с некоторыми ключевыми терминами.

  • Рецепт — функция для определения преобразования для предварительной обработки данных.
  • Juice — функция для извлечения данных из Recipe.
  • Выпечка — функция для применения всех преобразований, выполненных в наборе поездов, к тестовому набору.

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

# define the recipe
hcv_recipe <- 
  # which consists of the formula (outcome ~ predictors)
  recipe(Category ~ Age + Sex + ALB + ALP + ALT +AST +BIL+CHE+CHOL+CREA+GGT+PROT, 
         data = df)
  # and some pre-processing steps
  step_normalize(all_numeric()) %>%
  step_knnimpute(all_predictors())

Определение спецификации модели. Здесь мы определяем модели nnet и rpart.

glm_spec <-
  multinom_reg(mode = "classification") %>%
  set_engine("nnet")
rpart_spec <-
  decision_tree(mode = "classification") %>%
  set_engine("rpart")

Определение рабочего процесса. Мы создадим рабочий процесс и добавим рецепт вместе с моделью.

hcv_wf <- workflow() %>%
  add_recipe(hcv_recipe) %>%
  add_model(rpart_spec)

Окончательная подгонка и показатели модели. Мы будем использовать наш рабочий процесс для построения нашей модели и отслеживания показателей модели. last_fit() применяет рабочий процесс ко всему набору поездов и оценивает набор тестов.

final_res <- last_fit(hcv_wf, split = hcv_split)
collect_metrics(final_res)
Output:
A tibble: 2 x 4
  .metric  .estimator .estimate .config             
  <chr>    <chr>          <dbl> <chr>               
1 accuracy multiclass     0.909 Preprocessor1_Model1
2 roc_auc  hand_till      0.793 Preprocessor1_Model1

Примечание. Цель этого блога — понять конвейер как в Python, так и в R. Поэтому в этом блоге пропущено множество исследований данных и разработки функций. Например, набор данных несбалансирован.

df.groupby([df.Category]).size()/len(df)*100
Output:
0=Blood Donor             86.666667
0s=suspect Blood Donor     1.138211
1=Hepatitis                3.902439
2=Fibrosis                 3.414634
3=Cirrhosis                4.878049

Цель имеет 0=Класс донора крови имеет 86,6% вклада, а оставшиеся четыре класса вносят свой вклад в остальную часть. Набор данных необходимо будет сбалансировать перед этапом построения модели с недостаточной или избыточной выборкой.

Под выборкой:

from imblearn.under_sampling import RandomUnderSampler
undersamp = RandomUnderSampler(random_state=1)
X_under, y_under = undersamp.fit_resample(X_features, y_labels)
df.groupby([df.Category]).size()/len(df)*100
y_under.groupby([y_under]).size()/len(y_under)*100
Output:
Category
0=Blood Donor             20.0
0s=suspect Blood Donor    20.0
1=Hepatitis               20.0
2=Fibrosis                20.0
3=Cirrhosis               20.0

Излишняя выборка:

from imblearn.over_sampling  import RandomOverSampler
oversamp  = RandomOverSampler(random_state=1)
X_over, y_over = oversamp.fit_resample(X_features, y_labels)
df.groupby([df.Category]).size()/len(df)*100
y_over.groupby([y_over]).size()/len(y_over)*100
Output:
Category
0=Blood Donor             20.0
0s=suspect Blood Donor    20.0
1=Hepatitis               20.0
2=Fibrosis                20.0
3=Cirrhosis               20.0

После завершения вышеуказанных преобразований тот же конвейер, который мы определили ранее, можно повторно использовать для построения модели.

Заключительное примечание:

Сначала мы увидели неструктурированную версию, а затем перешли к более сложной конвейерной версии как в R, так и в Python. Код, в котором каждый логический шаг представляет собой отдельный объект, прост в управлении/отладке, и мы видели, как эти объекты были объединены в конвейеры/рабочие процессы в определенной последовательности, что делает каждый шаг плавным.

Надеюсь, этот блог был полезен, и вам понравилось его читать. Удачной учебы!!!!

Вы можете связаться со мной в Linkedin

Вы можете найти код на Github

Использованная литература:

https://www.tidymodels.org/

https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html

https://www.tmwr.org