Новая версия 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)
Новая версия включает в себя возможность сохранить этот формат фрейма данных даже после стандартизации:
############
# 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)
Раньше это изменило бы формат на массив 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()
Даже когда мы создаём конвейер, каждый трансформатор в конвейере может быть сконфигурирован для возврата фреймов данных, как показано ниже:
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)
Извлечение набора данных происходит быстрее и эффективнее
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'
С помощью приведённого выше блока кода мы можем получить графики частичной зависимости, как показано ниже:
С версией 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()
Хотя всегда можно построить подогнанную линию и остатки с помощью matplotlib или seaborn, после того, как мы определились с наилучшей моделью, здорово иметь возможность быстро проверять результаты непосредственно в среде Sklearn.
В новом Sklearn доступно ещё несколько улучшений / дополнений, но я обнаружил, что эти 4 основных улучшения чаще всего оказываются особенно полезными для стандартного анализа данных.
Ссылки на литературу
[1] Sklearn Release Highlights: V 1.2.0
[2] Основные моменты выпуска Sklearn: Video
[3] Все графики и коды: My GitHub