Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

Примерно в декабре прошлого года Scikit-Learn выпустила крупное стабильное обновление (версия 1.2.0–1). Теперь Scikit-Learn хорошо совместим с Pandas, некоторые новые функции могут помочь нам с моделями регрессии, а также с задачами классификации.

В статье я расскажу о некоторых новых обновлениях с примерами кода и рекомендациями как их использовать. 

@sqllib – приглашаем нашу новую библиотеку бесплатных книг для Датасаентиста.

Совместимость с Pandas

Применение некоторой стандартизации данных перед их использованием для обучения модели ML, такой как регрессия или нейронная сеть, является распространённым методом, позволяющим убедиться, что различные функции с различными диапазонами приобретают одинаковую важность (если или когда это необходимо) для прогнозов. Scikit-Learn предоставляет различные API предварительной обработки, такие как StandardScaler, MaxAbsScaler и т.д. В более новой версии можно сохранить формат фрейма данных даже после предварительной обработки, как показано ниже:

from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
########################
X, y = load_wine(as_frame=True, return_X_y=True) 
# available from version >=0.23; as_frame
########################
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, 
                                                    random_state=0)
X_train.head(3)
Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

Новая версия включает в себя возможность сохранить этот формат фрейма данных даже после стандартизации:

############
# v1.2.0
############

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler().set_output(transform="pandas") 
## change here

scaler.fit(X_train)
X_test_scaled = scaler.transform(X_test)
X_test_scaled.head(3)
Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

Раньше это изменило бы формат на массив Numpy:

###########
# v 0.24
########### 

scaler.fit(X_train)
X_test_scaled = scaler.transform(X_test)
print (type(X_test_scaled))

>>> <class 'numpy.ndarray'>

Поскольку формат фрейма данных остаётся неизменным, нам не нужно следить за столбцами, как нам нужно было делать это раньше. Анализ и построение графиков становятся проще:

fig = plt.figure(figsize=(8, 5))
fig.add_subplot(121)
plt.scatter(X_test['proline'], X_test['hue'], 
            c=X_test['alcohol'], alpha=0.8, cmap='bwr')
clb = plt.colorbar()
plt.xlabel('Proline', fontsize=11)
plt.ylabel('Hue', fontsize=11)
fig.add_subplot(122)
plt.scatter(X_test_scaled['proline'], X_test_scaled['hue'], 
            c=X_test_scaled['alcohol'], alpha=0.8, cmap='bwr')
# pretty easy now in the newer version to see the effect

plt.xlabel('Proline (Standardized)', fontsize=11)
plt.ylabel('Hue (Standardized)', fontsize=11)
clb = plt.colorbar()
clb.ax.set_title('Alcohol', fontsize=8)
plt.tight_layout()
plt.show()
Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

Даже когда мы создаём конвейер, каждый трансформатор в конвейере может быть сконфигурирован для возврата фреймов данных, как показано ниже:

from sklearn.pipeline import make_pipeline
from sklearn.svm import SVC

clf = make_pipeline(StandardScaler(), SVC())
clf.set_output(transform="pandas") # change here 
svm_fit = clf.fit(X_train, y_train)

print (clf[:-1]) # StandardScaler 
print ('check that set_output format indeed remains even after we build a pipleline: ', '\n')
X_test_transformed = clf[:-1].transform(X_test)

X_test_transformed.head(3)
Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

Извлечение набора данных происходит быстрее и эффективнее

OpenML– это открытая платформа для обмена наборами данных, а Dataset API в Sklearn предлагает функцию fetch_openml для их извлечения. С обновлённым Sklearn этот процесс стал более эффективен с точки зрения использования памяти и времени.

from sklearn.datasets import fetch_openml

start_t = time.time()
X, y = fetch_openml("titanic", version=1, as_frame=True, 
                    return_X_y=True, parser="pandas")
# # parser pandas is the addition in the version 1.2.0

X = X.select_dtypes(["number", "category"]).drop(columns=["body"])
print ('check types: ', type(X), '\n',  X.head(3))
print ('check shapes: ', X.shape)
end_t = time.time()
print ('time taken: ', end_t-start_t)

Использование parser='pandas' значительно сокращает время выполнения и потребление памяти. Можно легко проверить потребление памяти, используя библиотеку psutil, как показано ниже:

print(psutil.cpu_percent())

Графики частичной зависимости: Категориальные характеристики

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

Как написано в документации Sklearn:

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

Используя приведённый выше набор данных “titanic”, мы можем легко построить график частичной зависимости категориальных признаков:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import fetch_openml

start_t = time.time()
X, y = fetch_openml("titanic", version=1, as_frame=True, return_X_y=True, parser="pandas")

# parser pandas is the addition in the version 1.2.0

X = X.select_dtypes(["number", "category"]).drop(columns=["body"])
print ('check types: ', type(X), '\n',  X.head(3))
print ('check shapes: ', X.shape)

### drop nans
print ('check for nans in cols: ', X.isna().sum()) 
X_nonan = X.dropna(how='any', inplace=False)
print ('check new shapes: ', X_nonan.shape)

nonan_indices = X_nonan.index.to_list()
y_nonan = y[y.index.isin(nonan_indices)]
print ('check shape y: ', y_nonan.shape)
# print ('check for indices: ', X_nonan.index.to_list())

from sklearn.preprocessing import OrdinalEncoder
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import GradientBoostingRegressor #HistGradientBoostingRegressor
from sklearn.compose import ColumnTransformer

#### build a pipeline
categorical_features = ["pclass", "sex", "embarked"]
model = make_pipeline(ColumnTransformer(transformers=[("cat", OrdinalEncoder(), categorical_features)], 
                                        remainder="passthrough",), 
                      GradientBoostingRegressor(random_state=0),).fit(X_nonan, y_nonan) 
# gradientboosting regressor doesn't work with nan entries

from sklearn.inspection import PartialDependenceDisplay

fig, ax = plt.subplots(figsize=(14, 4), constrained_layout=True)
disp = PartialDependenceDisplay.from_estimator(model, 
                                               X_nonan, features=["age", "sex", ("pclass", "sex")], 
                                               categorical_features=categorical_features, ax=ax,)
fig.savefig(path_to_file + './part_disp.png', dpi=200)

################
# with v 0.24
################

# GBR_disp = plot_partial_dependence(model, X_NotNan, 
#                                    ['age', 'sex', ('age', 'sex')], ax=ax)
# >>> ValueError: could not convert string to float: 'female'

С помощью приведённого выше блока кода мы можем получить графики частичной зависимости, как показано ниже:

Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

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

>>> ValueError: could not convert string to float: ‘female’

Построение графика остатков (регрессионные модели)

Для анализа производительности классификационной модели в Sklearn metrics API в старых версиях (0.24) существовали процедуры построения графиков, такие как PrecisionRecallDisplay , RocCurveDisplay. В новом обновлении можно сделать аналогичное для регрессионных моделей. Давайте посмотрим на пример ниже:

from sklearn.linear_model import Lasso
from sklearn.datasets import load_diabetes
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import PredictionErrorDisplay


X_db, y_db = load_diabetes(return_X_y=True, as_frame=True)


print (X_db.head(3))


lasso001 = Lasso(alpha=0.01, max_iter=int(10e5))



y_pred = cross_val_predict(lasso001, X_db, y_db, cv=5)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
PredictionErrorDisplay.from_predictions(y_db, y_pred=y_pred, 
                                        kind="actual_vs_predicted", 
                                        subsample=100, 
                                        ax = ax1,
                                        random_state=0,)

ax1.set_title("Actual vs. Predicted values", fontsize=11)

PredictionErrorDisplay.from_predictions(y_db, 
                                        y_pred=y_pred, 
                                        kind="residual_vs_predicted", 
                                        subsample=100,
                                        ax = ax2,
                                        random_state=0,)

ax2.set_title("Residuals vs. Predicted Values", fontsize=11)
fig.suptitle("Prediction Results after Cross-Validation (Regression & Residual)", fontsize=14)
plt.tight_layout()
plt.show()
Новая версия Scikit-Learn (1.2.0–1). Обзор функций для анализа данных.

Хотя всегда можно построить подогнанную линию и остатки с помощью matplotlib или seaborn, после того, как мы определились с наилучшей моделью, здорово иметь возможность быстро проверять результаты непосредственно в среде Sklearn.

В новом Sklearn доступно ещё несколько улучшений / дополнений, но я обнаружил, что эти 4 основных улучшения чаще всего оказываются особенно полезными для стандартного анализа данных.

Ссылки на литературу

[1] Sklearn Release Highlights: V 1.2.0

[2] Основные моменты выпуска Sklearn: Video

[3] Все графики и коды: My GitHub

+1
0
+1
3
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *