19 самых элегантных трюков Sklearn, которые я нашёл после 3 лет использования
Узнайте о 19 функциях Sklearn, которые вы возможно не видели, которые являются прямой и элегантной заменой обычных операций, которые вы выполняете вручную.
После трёх лет использования и работы справочником по API Sklearn я понял, что самые популярные и часто используемые модели и функции — это лишь малая часть того, что может сделать библиотека. Несмотря на то, что некоторые функции чрезвычайно узконаправлены и используются в редких случаях, я обнаружил множество интересных функций, которые являются элегантными вариантами решения для различных типичаных операций, которые датасаентисты обычно выполняют вручную.
Итак, я решил составить список самых элегантных и важных из них и кратко объяснить их, чтобы вы могли значительно расширить свой набор инструментов Sklearn. Наслаждайтесь!
1️⃣. covariance.EllipticEnvelope
Распределения часто имеют выбросы. Многие алгоритмы нужны для работы с выбросами. EllipticalEnvelop
e -
функция, поиска выбросов, которая напрямую встроена в Sklearn. Преимущество этого алгоритма в том, что она исключительно хорошо обнаруживает выбросы в нормально распределенных (гауссовских) функциях:
import numpy as np
from sklearn.covariance import EllipticEnvelope
# Create a sample normal distribution
X = np.random.normal(loc=5, scale=2, size=50).reshape(-1, 1)
# Fit the estimator
ee = EllipticEnvelope(random_state=0)
_ = ee.fit(X)
# Test
test = np.array([6, 8, 20, 4, 5, 6, 10, 13]).reshape(-1, 1)
# predict returns 1 for an inlier and -1 for an outlier
>>> ee.predict(test)
array([ 1, 1, -1, 1, 1, 1, -1, -1])
Чтобы протестировать функции, мы создаем нормальное распределение со средним значением 5 и стандартным отклонением 2. После того, как модель обучена, мы передаем в метод predict
несколько случайных чисел. Метод возвращает -1 для выбросов в test
20, 10, 13.
2️⃣. feature_selection.RFECV
Выбор функций, которые лучше всего помогают с прогнозами, является обязательным шагом для борьбы с переобучением и снижения сложности модели. Одним из самых надежных алгоритмов, предлагаемых Sklearn, является устранение рекурсивных признаков (RFE). Он автоматически находит наиболее важные фичи с помощью перекрестной проверки и отбрасывает остальные.
Преимущество этого оценщика заключается в том, что он представляет собой оболочку — его можно использовать вокруг любого алгоритма Sklearn, который возвращает значения важности признаков или коэффициенты. Вот пример синтетического набора данных:
from sklearn.datasets import make_regression
from sklearn.feature_selection import RFECV
from sklearn.linear_model import Ridge
# Build a synthetic dataset
X, y = make_regression(n_samples=10000, n_features=15, n_informative=10)
# Init/fit the selector
rfecv = RFECV(estimator=Ridge(), cv=5)
_ = rfecv.fit(X, y)
# Transform the feature array
>>> rfecv.transform(X).shape
(10000, 10)
Фейковый набор данных имеет 15 признаков, 10 из которых информативны, а остальные избыточны. Мы подгоняем 5-кратный RFECV с регрессией Ridge
в качестве оценки. После обучения вы можете использовать метод transform
, чтобы отбросить избыточные функции. Вызов .shape
показывает нам, что оценщику удалось отбросить все 5 ненужных функций.
3️⃣. ensemble.ExtraTrees
Несмотря на то, что случайные леса работают хорошо, риск переобучения очень высок. Поэтому Sklearn предлагает альтернативу RF, называемую ExtraTrees (как классификатор, так и регрессор).
Слово «extra» означает не больше деревьев, а больше случайности. Алгоритм использует другой тип дерева, который очень похож на деревья решений.
Единственное отличие состоит в том, что вместо вычисления порогов разделения при построении каждого дерева эти пороги рисуются случайным образом для каждого признака, и в качестве правила разделения выбирается наилучший порог. Это уменьшает дисперсию за счет небольшого увеличения смещения:
from sklearn.ensemble import ExtraTreesRegressor, RandomForestRegressor
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeRegressor
X, y = make_regression(n_samples=10000, n_features=20)
# Decision trees
clf = DecisionTreeRegressor(max_depth=None, min_samples_split=2, random_state=0)
scores = cross_val_score(clf, X, y, cv=5)
>>> scores.mean()
0.6376080094392635
# Random Forest
clf = RandomForestRegressor(
n_estimators=10, max_depth=None, min_samples_split=2, random_state=0
)
scores = cross_val_score(clf, X, y, cv=5)
>>> scores.mean()
0.8446103607404536
# ExtraTrees
clf = ExtraTreesRegressor(
n_estimators=10, max_depth=None, min_samples_split=2, random_state=0
)
scores = cross_val_score(clf, X, y, cv=5)
>>> scores.mean()
0.8737373931608834
Как видите, ExtraTreesRegressor работает лучше, чем Random Forests, на синтетическом наборе данных.
Подробнее о Extremely Randomized Trees читайте в официальном руководстве пользователя .
4️⃣. impute.IterativeImputer и KNNImputer
Класс SimpleImputer
предоставляет основные стратегии для восстановления отсутствующих значений. Пропущенные значения могут быть восстановлены с использованием предоставленного постоянного значения или с использованием статистики (среднего, медианного или наиболее частого) каждого столбца, в котором находятся пропущенные значения. Этот класс также допускает различные кодировки пропущенных значений.
Подпакет sklearn.impute
включает в себя два алгоритма на основе моделей — KNNImputer
и IterativeImputer
.
Как следует из названия, KNNImputer
использует алгоритм k-ближайших соседей, чтобы найти лучшую замену отсутствующим значениям:
from sklearn.impute import KNNImputer
# Code taken from Sklearn user guide
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
imputer = KNNImputer(n_neighbors=2)
>>> imputer.fit_transform(X)
array([[1. , 2. , 4. ],
[3. , 4. , 3. ],
[5.5, 6. , 5. ],
[8. , 8. , 7. ]])
Более надёжным алгоритмом является IterativeImputer
. Он находит пропущенные значения, моделируя каждую функцию с пропущенными значениями. Этот процесс выполняется пошагово в циклическом режиме. На каждом шаге один признак с отсутствующими значениями выбирается в качестве цели (y
), а остальные выбираются в качестве массива признаков (X
). Затем используется регрессор для прогнозирования отсутствующих значений y
, и этот процесс продолжается для каждого признака до времени max_iter
(параметр IterativeImputer).
В результате для одного пропущенного значения генерируются несколько прогнозов. Преимущество этого заключается в том, что каждое отсутствующее значение рассматривается как случайная величина и связывается с присущей им неопределенностью:
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.linear_model import BayesianRidge
imp_mean = IterativeImputer(estimator=BayesianRidge())
imp_mean.fit([[7, 2, 3], [4, np.nan, 6], [10, 5, 9]])
X = [[np.nan, 2, 3], [4, np.nan, 6], [10, np.nan, 9]]
>>> imp_mean.transform(X)
array([[ 6.95847623, 2. , 3. ],
[ 4. , 2.6000004 , 6. ],
[10. , 4.99999933, 9. ]])
Обнаружено, что BayesianRidge и ExtraTree работают лучше с IterativeImputer.
5️⃣. linear_model.HuberRegressor
Наличие выбросов может сильно исказить предсказания любой модели. Многие алгоритмы обнаружения выбросов отбрасывают выбросы и отмечают их как отсутствующие. Хотя это помогает обучающей функции моделей, оно полностью устраняет влияние выбросов на распределение.
Альтернативный алгоритм HuberRegressor
. Вместо того, чтобы полностью удалять их, он придает выбросам меньший вес во время работы алгоритма. Он имеет гиперпараметр epsilon
, контролирующий количество выборок, которые следует классифицировать как выбросы. Чем меньше параметр, тем более устойчива модель к выбросам. Его API такой же, как и у любого другого линейного регрессора.
Ниже вы можете увидеть его сравнение с регрессором Байесовского хребта на наборе данных с сильными выбросами:
Как видите, HuberRegressor с epsilon 1.35 1.5, 1.75 удалось зафиксировать линию наилучшего соответствия, на которую не влияют выбросы.
Подробнее об алгоритме можно узнать из руководства пользователя .
6️⃣. tree.plot_tree
Sklearn позволяет построить структуру дерева решений с помощью функции plot_tree
. Эта функция может быть полезна для новичков, которые только начали изучать древовидные и ансамблевые модели:
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, plot_tree
iris = load_iris()
X, y = iris.data, iris.target
clf = DecisionTreeClassifier()
clf = clf.fit(X, y)
plt.figure(figsize=(15, 10), dpi=200)
plot_tree(clf, feature_names=iris.feature_names,
class_names=iris.target_names);
Существуют и другие методы построения деревьев, например, в формате Graphviz. Узнайте о них из руководства пользователя .
7️⃣. linear_model.Perceptron
Самое крутое имя в этом списке принадлежит номеру 7 — Perceptron. Несмотря на причудливое название, это простой линейный двоичный классификатор. Отличительной особенностью алгоритма является то, что он подходит для крупномасштабного обучения и по умолчанию:
- Не требует скорости обучения.
- Не применяет регуляризацию.
- Обновляет свою модель только при ошибках.
Это эквивалентно SGDClassifier loss='perceptron', eta=1, learning_rate="constant", penalty=None
, но немного быстрее:
from sklearn.datasets import make_classification
from sklearn.linear_model import Perceptron
# Create a large dataset
X, y = make_classification(n_samples=100000, n_features=20, n_classes=2)
# Init/Fit/Score
clf = Perceptron()
_ = clf.fit(X, y)
>>> clf.score(X, y)
0.91928
8️⃣. feature_selection.SelectFromModel
Другой оценщик выбора функций на основе модели в Sklearn — это SelectFromModel
. Он не так надежен, как RFECV, но может быть хорошим вариантом для массивных наборов данных, поскольку требует меньших вычислительных затрат. Он также является оценщиком-оболочкой и работает с любой моделью, имеющей атрибуты .feature_importances_
или .coef_
:
from sklearn.feature_selection import SelectFromModel
# Make a dataset with 40 uninformative features
X, y = make_regression(n_samples=int(1e4), n_features=50, n_informative=10)
# Init the selector and transform feature array
selector = SelectFromModel(estimator=ExtraTreesRegressor()).fit(X, y)
>>> selector.transform(X).shape
(10000, 8)
Как видите, алгоритму удалось отбросить все 40 избыточных функций.
9️⃣. metrics.ConfusionMatrixDisplay
Матрицы путаницы — святой Грааль задач классификации. На ней основано большинство метрик, таких как precision, recall, F1, ROC AUC и т.д. Sklearn позволяет вычислить и построить матрицу путаницы по умолчанию:
from sklearn.metrics import plot_confusion_matrix
from sklearn.model_selection import train_test_split
# Make a binary classification problem
X, y = make_classification(n_samples=200, n_features=5, n_classes=2)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.5, random_state=1121218
)
clf = ExtraTreeClassifier().fit(X_train, y_train)
fig, ax = plt.subplots(figsize=(5, 4), dpi=100)
plot_confusion_matrix(clf, X_test, y_test, ax=ax);
Честно говоря, я бы не сказал, что мне нравится матрица путаницы по умолчанию. Их формат фиксирован — строки являются истинными метками, а столбцы — прогнозами. Кроме того, первая строка и столбец относятся к отрицательному классу, а вторая строка и столбец — к положительному. Некоторые люди могут предпочесть матрицу другого формата, возможно, транспонированную или перевернутую.
Например, мне нравится делать положительный класс первой строкой и первым столбцом, чтобы соответствовать формату, указанному в Википедии. Это помогает мне лучше выделить 4 термина матрицы — TP, FP, TN, FN. К счастью, вы можете построить пользовательскую матрицу с помощью другой функции ConfusionMatrixDisplay
:
from sklearn.metrics import ConfusionMatrixDisplay, confusion_matrix
clf = ExtraTreeClassifier().fit(X_train, y_train)
y_preds = clf.predict(X_test)
fig, ax = plt.subplots(figsize=(5, 4), dpi=100)
cm = confusion_matrix(y_test, y_preds)
cmp = ConfusionMatrixDisplay(cm, display_labels=["Positive", "Negative"])
cmp.plot(ax=ax);
Вы можете поместить матрицу путаницы cm
в любой формат, который вы хотите, прежде чем передавать ее в ConfusionMatrixDisplay
.
🔟. Generalized Linear Models
Нет смысла преобразовывать цель (y ), чтобы сделать ее нормально распределенной, если есть альтернативы, которые могут работать с другими типами распределений.
Например, Sklearn предлагает три обобщенные линейные модели для целевых переменных с распределением Пуассона, Твиди или Гаммы. Вместо того, чтобы ожидать нормального распределения, PoissonRegressor
и TweedieRegressor
может давать GammaRegressor надежные результаты для целей с соответствующими распределениями.
Кроме того, их API такие же, как и у любой другой модели Sklearn. Чтобы узнать, соответствует ли распределение цели трем вышеперечисленным, вы можете построить их PDF (функция плотности вероятности) на тех же осях с идеальным распределением.
Например, чтобы увидеть, соответствует ли цель распределению Пуассона, постройте ее PDF-распределение, используя Seaborn, kdeplot
и постройте идеальное распределение Пуассона, выбрав его из Numpy, используя np.random.poisson
той же оси.
1️⃣1️⃣. ensemble.IsolationForest
Поскольку древовидные и ансамблевые модели обычно дают более надежные результаты, они также доказали свою эффективность при обнаружении выбросов. в Sklearn для обнаружения выбросов IsolationForest
используется лес чрезвычайно случайных деревьев (tree.ExtraTreeRegressor ). Каждое дерево пытается изолировать каждую выборку, выбирая один признак и случайным образом выбирая значение разделения между максимальным и минимальным значениями выбранного признака.
Этот тип случайного разбиения создает заметно более короткие пути между корневым узлом и конечным узлом каждого дерева.
Таким образом, когда лес случайных деревьев в совокупности дает более короткие пути для определенных выборок, они, скорее всего, будут аномалиями — руководство пользователя Sklearn.
from sklearn.ensemble import IsolationForest
X = np.array([-1.1, 0.3, 0.5, 100]).reshape(-1, 1)
clf = IsolationForest(random_state=0).fit(X)
>>> clf.predict([[0.1], [0], [90]])
array([ 1, 1, -1])
1️⃣2️⃣. preprocessing.PowerTransformer
Многие линейные модели требуют некоторого преобразования числовых признаков, чтобы сделать их нормально распределенными. StandardScaler
и MinMaxScaler
работают довольно хорошо для большинства дистрибутивов.
Однако при наличии высокой асимметрии в данных затрагиваются основные показатели распределения, такие как среднее, медиана, минимальное и максимальное значения. Таким образом, простая нормализация и стандартизация не работают с асимметричными распределениями.
Вместо этого Sklearn PowerTransformer
использует логарифмическое преобразование, чтобы максимально приблизить любую асимметричную функцию к нормальному распределению. Рассмотрим эти две функции в наборе данных Diamonds:
import seaborn as sns
diamonds = sns.load_dataset("diamonds")
diamonds[["price", "carat"]].hist(figsize=(10, 5));
Оба сильно перекошены. Давайте исправим это с помощью логарифмического преобразования:
from sklearn.preprocessing import PowerTransformer
pt = PowerTransformer()
diamonds.loc[:, ["price", "carat"]] = pt.fit_transform(diamonds[["price", "carat"]])
diamonds[["price", "carat"]].hist(figsize=(10, 5));
1️⃣3️⃣. preprocessing.RobustScaler
Другой числовой преобразователь в Sklearn — это RobustScaler
. Вероятно, вы можете догадаться, что он делает по его названию — он может преобразовывать функции таким образом, чтобы они были устойчивы к выбросам. Если в признаке присутствуют выбросы, их трудно сделать нормально распределенными, потому что они могут сильно искажать среднее значение и стандартное отклонение.
Вместо использования среднего/стандартного значения, с помощью RobustScaler
данные масштабируются с использованием медианы и IQR (межквартильный размах), поскольку обе метрики не имеют смещения из-за выбросов. Вы также можете прочитать об этом в руководстве пользователя .
1️⃣4️⃣. compose.make_column_transformer
В Sklearn есть сокращение для создания экземпляров Pipeline с функцией make_pipeline
. Вместо того, чтобы называть каждый шаг и делать ваш код излишне длинным, функция просто принимает преобразователи и оценщики и выполняет свою работу:
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
pipeline = make_pipeline(SimpleImputer(), StandardScaler(), ExtraTreesRegressor())
>>> pipeline
Pipeline(steps=[('simpleimputer', SimpleImputer()),
('standardscaler', StandardScaler()),
('extratreesregressor', ExtraTreesRegressor())])
Для более сложных сценариев используется ColumnTransformer
, у которого та же проблема — каждый шаг предварительной обработки должен быть назван, что делает ваш код длинным и нечитаемым. К счастью, Sklearn предлагает аналогичную функцию make_pipeline
— make_column_transformer
:
import seaborn as sns
from sklearn.compose import make_column_transformer
from sklearn.preprocessing import OneHotEncoder
# Load diamonds dataset
diamonds = sns.load_dataset("diamonds")
X, y = diamonds.drop("price", axis=1), diamonds.price.values.reshape(-1, 1)
# Isolate numeric and categorical cols
num_cols = X.select_dtypes(include=np.number).columns
cat_cols = X.select_dtypes(exclude=np.number).columns
>>> make_column_transformer((StandardScaler(), num_cols),
(OneHotEncoder(), cat_cols))
ColumnTransformer(
transformers=[('standardscaler', StandardScaler(),
Index(['carat', 'depth', 'table', 'x', 'y', 'z'], dtype='object')),
('onehotencoder', OneHotEncoder(),
Index(['cut', 'color', 'clarity'], dtype='object'))]
)
Как видите, использование make_column_transformer
намного короче, и оно заботится об именовании каждого шага преобразования отдельно.
1️⃣5️⃣. compose.make_column_selector
Если вы обратили внимание, мы использовали функцию select_dtypes
вместе с атрибутом columns
pandas DataFrames, чтобы изолировать числовые и категориальные столбцы. Хотя это работает, есть гораздо более гибкое и элегантное решение с использованием Sklearn.
Функция
make_column_selector
создает селектор столбца, который может быть передан непосредственно в экземпляры ColumnTransformer
. Он имеет параметры dtype_include
и dtype_exclude
для выбора столбцов на основе типа данных.
Если вам нужен настраиваемый фильтр столбцов, вы даже можете передать регулярное выражение pattern, задав для других параметров значение None
. Вот как это работает:
from sklearn.compose import make_column_selector
make_column_transformer(
(StandardScaler(), make_column_selector(dtype_include=np.number)),
(OneHotEncoder(), make_column_selector(dtype_exclude=np.number)),
)
Вместо того, чтобы передавать список имен столбцов, просто передайте экземпляр make_column_selector
с соответствующими параметрами, и все готово!
1️⃣6️⃣. preprocessing.OrdinalEncoder
Распространенной ошибкой среди новичков является использование LabelEncoder
для кодирования порядковых категориальных признаков . Если вы заметили, LabelEncoder
позволяет преобразовывать столбцы только по одному, а не одновременно, как OneHotEncoder
. Вы можете подумать, что Sklearn ошибся!
На самом деле его следует использовать только для кодирования переменной ответа (y
), как указано в документации . Чтобы закодировать массив функций (X
), вы должны использовать OrdinalEncoder
, который работает должным образом. Он преобразует порядковые категориальные столбцы в функцию с классами (0, n_categories – 1). И это делается для всех указанных столбцов в одной строке кода, что позволяет включать его в конвейеры.
from sklearn.preprocessing import OrdinalEncoder
oe = OrdinalEncoder()
X = [
["class_1", "rank_1"],
["class_1", "rank_3"],
["class_3", "rank_3"],
["class_2", "rank_2"],
]
>>> oe.fit_transform(X)
array([[0., 0.],
[0., 2.],
[2., 2.],
[1., 1.]])
1️⃣7️⃣. metrics.get_scorer
В Sklearn встроено более 50 метрик, и их текстовые названия можно увидеть в файлах sklearn.metrics.SCORERS.keys()
. В одном проекте вам, возможно, придется использовать несколько метрик и импортировать их, если вы используете их по отдельности.
Импорт множества метрик sklearn.metrics
напрямую может загрязнить ваше пространство имен и стать излишне длинным. В качестве решения вы можете использовать функцию metrics.get_scorer
для доступа к любой метрике с ее текстовым именем, даже не импортируя ее:
from sklearn.metrics import get_scorer
>>> get_scorer("neg_mean_squared_error")
make_scorer(mean_squared_error, greater_is_better=False)
>>> get_scorer("recall_macro")
make_scorer(recall_score, pos_label=None, average=macro)
>>> get_scorer("neg_log_loss")
make_scorer(log_loss, greater_is_better=False, needs_proba=True)
1️⃣8️⃣. model_selection.HalvingGrid и HalvingRandomSearchCV
В версии 0.24 Sklearn мы познакомились с двумя экспериментальными оптимизаторами гиперпараметров: классами HalvingGridSearchCV
и HalvingRandomSearchCV
.
В отличие от своих исчерпывающих собратьев GridSearch и RandomizedSearch, новые классы используют технику, называемую последовательным делением пополам . Вместо обучения всех наборов-кандидатов (наборов комбинаций параметров) на всех данных параметрам задается только подмножество данных. Наихудшие кандидаты отфильтровываются путем их обучения на меньшем подмножестве данных. После каждой итерации обучающие выборки увеличиваются на некоторый коэффициент, а количество возможных кандидатов уменьшается на столько же, что приводит к гораздо более быстрому времени оценки.
Насколько быстрее? В экспериментах, которые я проводил, HalvingGridSearch был в 11 раз быстрее, чем обычный GridSearch, а HalvingRandomSearch даже в 10 раз быстрее, чем HalvingGridSearch.
1️⃣9️⃣. sklearn.utils
И последнее, но не менее важное: у Sklearn есть целый ряд служебных и вспомогательных функций в подпакете sklearn.utils
. Сам Sklearn использует функции этого модуля для создания всех используемых нами преобразователей и оценщиков.
Существует много полезных, таких как class_weight.compute_class_weight
, estimator_html_repr
, shuffle
, check_X_y
и т. д. Вы можете использовать их в своем собственном рабочем процессе, чтобы сделать ваш код более похожим на Sklearn, или они могут пригодиться при создании пользовательских преобразователей и оценщиков, которые вписываются в Sklearn API.
Заключение
Несмотря на то, что такие библиотеки, как CatBoost, XGBoost и LightGBM, постепенно отбирают кусок у Sklearn как у ведущей библиотеки машинного обучения, она по-прежнему остается бесценной частью стека навыков современного инженера машинного обучения.
Последовательный API, исключительный дизайн кода и возможность создавать надежные рабочие процессы ML по-прежнему делают Sklearn непревзойденным с точки зрения функциональности и гибкости. Несмотря на то, что мы можем многого добиться с помощью основ, эта статья показала, что Sklearn может предложить гораздо больше, чем кажется на первый взгляд!
Спасибо за чтение!