Узнайте, как использовать конвейеры scikit-learn в рабочих процессах Python и Tidy Models в R
Введение:
Эксперимент по науке о данных или процесс построения модели представляет собой серию шагов, которые выполняются в определенной последовательности и чаще всего с несколькими итерациями. Акцент быстро смещается на отслеживание точности модели и связанных показателей, но есть много других аспектов, которые могут сделать весь процесс более структурированным, упрощая не только чтение кода, но и воспроизведение результатов. В этом блоге мы рассмотрим конвейеры scikit-learn и рабочие процессы Tidymodel с одним и тем же набором данных. Сначала мы рассмотрим построение моделей машинного обучения в неструктурированном подходе, а затем перейдем к построению тех же моделей с использованием Python, а затем с помощью R.
Оглавление
- Неструктурированный подход к построению модели
- Исследование и обработка данных
- Построение модели с конвейерами в Python
- Построение модели с конвейерами в R
- Дальнейшие исследования/эксперименты
Загрузить пакеты и данные 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://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html