Aspiring Data Science
373 subscribers
425 photos
11 videos
10 files
1.87K links
Заметки экономиста о программировании, прогнозировании и принятии решений, научном методе познания.
Контакт: @fingoldo

I call myself a data scientist because I know just enough math, economics & programming to be dangerous.
Download Telegram
#mlgems #sklearn #pipelines

Столкнулся с ситуацией, когда есть конвейер sklearn/imblearn с препроцессингом (удаление, нормализация некоторых столбцов), в конце бустинг, поддерживающий задание отдельного eval_set ( catboost, xgboost). К eval_set конвейер, конечно же, не применяется, т.к. pipeline про eval_set знает только то, что это ещё один из параметров для модели в конце списка. Как быть? Тестирую пока решение с промежуточным конвейером, только пытаюсь сделать его более эффективным.
#featureselection #rfe

Понравилась идея recursive feature elimination, решил добавить этот метод в сравнение. Идея с фиксированным количеством фичей, как в RFE, мне кажется странной. Конечно же, оптимальное число можно определить только на кросс-валидации. И тут sklearn опять обделался. RFECV не работает с датафреймами и тем более категориальными фичам. В этом фреймворке вообще хоть что-то есть для реальной жизни и рабочих нагрузок?

Бюджета времени нет. Девелоперам предлагается, видимо, на глазок манипулировать параметром min_features_to_select, или просто забить на SLA. Ну а как реализован поиск оптимального количества признаков? Лезем в код:

# Build an RFE object, which will evaluate and score each possible
# feature count, down to self.min_features_to_select

Полный перебор! А это ТОЧНО оптимальный метод, когда мы оптимизируем функцию от 1 переменной с 1 глобальным экстремумом? Нет, серьёзно, блять, в 2023-м? Да какой нам искусственный интеллект. У нас же "натурального" нету. Пишу свой RFECV, короче.
😁1
#earlystopping #transformedtarget #sklearn #improvement

Никогда такого не было, и вот опять: трансформация таргета в sklearn несовместима с кастомными валидационными множествами для ранней остановки, которые обычно используются в современных бустингах. Скорее всего, как принято, найдут 100 причин этого не делать, но всё же запостил feature request.
#sklearn #imblanced #resampling #calibration

Этот Лемэтр зарубил много моих оптимизационных инициатив в sklearn, кстати.

Из новшеств: cost-sensitive learning, metadata routing. Английский докладчика понимается с трудом, но пытливый ум при желании разберётся )

https://www.youtube.com/watch?v=Gjrz4YCp6l8
#sklearn #dataframes

Оказывается, есть инициатива унификации библиотек работы с датафреймами. И её поддерживают в sklearn.

"Enhancement All estimators now recognizes the column names from any dataframe that adopts the DataFrame Interchange Protocol. Dataframes that return a correct representation through np.asarray(df) is expected to work with our estimators and functions."

"Python users today have a number of great choices for dataframe libraries. From Pandas and cuDF to Vaex, Koalas, Modin, Ibis, and more. Combining multiple types of dataframes in a larger application or analysis workflow, or developing a library which uses dataframes as a data structure, presents a challenge though. Those libraries all have different APIs, and there is no standard way of converting one type of dataframe into another."

Похожая идея с использованием массивов Array API:

"Python users have a wealth of choice for libraries and frameworks for numerical computing, data science, machine learning, and deep learning. New frameworks pushing forward the state of the art in these fields are appearing every year. One unintended consequence of all this activity and creativity has been fragmentation in multidimensional array (a.k.a. tensor) libraries - which are the fundamental data structure for these fields. Choices include NumPy, Tensorflow, PyTorch, Dask, JAX, CuPy, MXNet, Xarray, and others.

The APIs of each of these libraries are largely similar, but with enough differences that it’s quite difficult to write code that works with multiple (or all) of these libraries. This array API standard aims to address that issue, by specifying an API for the most common ways arrays are constructed and used.

Why not simply pick an existing API and bless that as the standard?"
11
#sklearn #cupy

Вот пример выполнения LDA на GPU с применением этого экспериментального Array API.

from sklearn.datasets import make_classification
from sklearn import config_context
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
import cupy

X_np, y_np = make_classification(random_state=0)
X_cu = cupy.asarray(X_np)
y_cu = cupy.asarray(y_np)
X_cu.device

with config_context(array_api_dispatch=True):
lda = LinearDiscriminantAnalysis()
X_trans = lda.fit_transform(X_cu, y_cu)
X_trans.device


А еще теперь можно напрямую работать с тензорами Pytorch:

import torch
X_torch = torch.asarray(X_np, device="cuda", dtype=torch.float32)
y_torch = torch.asarray(y_np, device="cuda", dtype=torch.float32)

with config_context(array_api_dispatch=True):
lda = LinearDiscriminantAnalysis()
X_trans = lda.fit_transform(X_torch, y_torch)
type(X_trans)
X_trans.device.type


Пока далеко не все модули это поддерживают, но сама идея чудесная.

https://scikit-learn.org/stable/modules/array_api.html#array-api
1
#sklearn

Продолжаем обзор новых интересных фичей, добавленных в sklearn за последние пар лет.

С версии 1.3 там есть TargetEncoder, т.е. можно по идее не тянуть в проект category_encoders. Сравнение с CE бы не помешало.

https://scikit-learn.org/stable/auto_examples/preprocessing/plot_target_encoder.html#sphx-glr-auto-examples-preprocessing-plot-target-encoder-py
#sklearn

StackingRegressor/StackingClassifier. Можно самому не реализовывать стекинг,а взять готовый. Вопрос только в том, что не видно способа передавать fit_params,и, как следствие, валидационное множество для контроля переобучения. Зная подход авторов sklearn, я не удивлён такой лажей (хотя, возможно, это поддерживается через set_request?).
🤯1
#sklearn

TunedThresholdClassifierCV - тюнить порог решения классификатора теперь (с версии 1.5) можно элегантно. Открывает путь к удобному cost-sensitive learning.

"Classifier that post-tunes the decision threshold using cross-validation.

This estimator post-tunes the decision threshold (cut-off point) that is used for converting posterior probability estimates (i.e. output of predict_proba) or decision scores (i.e. output of decision_function) into a class label. The tuning is done by optimizing a binary metric, potentially constrained by a another metric."

from sklearn.model_selection import TunedThresholdClassifierCV

tuned_model = TunedThresholdClassifierCV(
estimator=model,
scoring=scoring["cost_gain"],
store_cv_results=True, # necessary to inspect all results
)
tuned_model.fit(X_train, y_train)
print(f"{tuned_model.best_threshold_=:0.2f}")

tuned_model.best_threshold_=0.02

Трейдерам на заметку: отличный пример, как использовать metadata routing, чтобы передать модели дополнительные параметры (здесь это величина транзакции в задаче обнаружения фрода).
👍1
#sklearn

Большой новый раздел, который я давно видел, но не вникал - роутинг метаданных. Видимо, с его помощью как раз можно передать валидационное множество объекту StackingClassifier, хотя там и нет такого явного параметра.

В примере ниже веса для скорера и для модельки передаются механизмом роутинга с более высокого уровня кросс-валидации. Удобно, что можно подписать разные компоненты на одни и те же метаданные, и всё это без parameters hell и всяких вложенных двойных подчёркиваний. Кажется очень перспективной новинкой, и придётся учитывать при разработке своих кастомных модулей, совместимых с sklearn.

weighted_acc = make_scorer(accuracy_score).set_score_request(
sample_weight="scoring_weight"
)
lr = LogisticRegressionCV(
cv=GroupKFold(), scoring=weighted_acc,
).set_fit_request(sample_weight="fitting_weight")
cv_results = cross_validate(
lr,
X,
y,
cv=GroupKFold(),
params={
"scoring_weight": my_weights,
"fitting_weight": my_other_weights,
"groups": my_groups,
},
scoring=weighted_acc,
)
#sklearn

GradientBoostingClassifier/HistGradientBoostingClassifier - реализация градиентного бустинга над деревьями, конкурент xgboost, lightgbm, catboost.

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

Также поддерживает раннюю остановку и validation_fraction - автосоздание валидационного множества, чего не могут сделать уже xgboost и lightgbm.

Завезены ограничения монотонности. Не завезена поддержка GPU.
1
#sklearn

set_output в схожей с set_request манере позволяет задать тип выходных данных компонента - массив numpy, фрейм pandas или polars. можно вызывать глобально, на уровне компонента и метакомпонента.

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler().set_output(transform="pandas")

scaler.fit(X_train)
X_test_scaled = scaler.transform(X_test)
X_test_scaled.head()
#sklearn #mlgems

У Винсента всегда классные выступления. Кратко рассматривает кэширование конвейера (Pipeline(...,memory=...)), роутинг весов, дообучение для потоковых данных (partial_fit), модуль semi_supervised, Но вы этот раз всё очень кратко и галопом по Европам.

https://www.youtube.com/watch?v=es_8_iT-oQk
#sklearn #codegems

Прошло больше года, и только сейчас в сайкит-лёрн замёрджили ГОТОВУЮ ветку, которая втрое ускоряет расчёт classification_report.

Adrin Jalali закодил решение в течение суток с момента, как я нашёл дублирующие вызовы в sklearn. Остальное время заняли бюрократические согласования.

Вывод: open-source это страшная бюрократия, не надейтесь, что вам что-то быстро исправят, делайте сами. Вместе с тем, не ленитесь создавать хотя бы багрепорты, чтобы следующие поколения DS-ов хоть немного могли почиллить 😅

Примерно с той же эффективностью можно оставлять багрепорты или запросы функциональности для catboost - да, сделают в течение года-полутора (что по сути вечность), но я таким путём за последние N лет пропихнул улучшений 4-5 туда.
🔥3
#sklearn #wizards

Авторы sklearn не перестают меня удивлять своими решениями.

На этот раз, они поленились проверять дубликаты комбинаций в RandomizedSearchCV, из-за чего на интовых и категориальных гиперпараметрах с низкой кардинальностью он теряет 20-30% времени впустую.

https://github.com/scikit-learn/scikit-learn/issues/29794
😁1
#dummy #baselines #sklearn #timeseries

Чёт я задумался, что Dummy* модели в sklearn совсем не предназначены для временных рядов. Сел сочинять Feature Request, посмотрим, если не зарубят, даже сам над ним поработаю.

И, кстати сказать, неудобно, что на практике всегда перебираешь все доступные стратегии, чтобы создать хоть какой-то разумный бэйз.

У меня пока такие идеи:

1) дать возможность указывать окно, как в pandas при вычислении rolling/expanding. Вводим для этого параметр scope:str={"rolling","expanding","all"}. Если scope!="all", стратегия применяется не ко всему датасету, а к окошку. параметры **kwargs передаются напрямую в метод scope ряда pandas.

Для полной поддержки взвешенных окон придётся добавить **extra_kwargs:

kwargs=dict(window=5, win_type="gaussian")
extra_kwargs=dict(std=0.1)
# Supplementary Scipy arguments passed in the aggregation function
y.rolling(**kwargs).mean(**extra_kwargs)


2) добавить strategy="lag" (только для scope="rolling")

3) добавить strategy="best", которая будет внутри перебирать все остальные стратегии, чтобы выдать пользователю самую сильную базу. Также придётся добавить параметр scoring, как в cross_validate, чтоб можно было сравнивать стратегии.

4) добавить strategy="ewm" на базе того же пандас. параметры **kwargs передаются напрямую в метод ewm pandas.

5) добавить strategy="apply" (для scope!="all") для применения кастомных аггрегирующих функций. Будет транслироваться в

def mad(x):
return np.fabs(x - x.mean()).mean()

kwargs=dict(window=4)
extra_kwargs=dict(func=mad, raw=True)

y.rolling(**kwargs).apply(**extra_kwargs)


6) в случае задания окна, хотелось бы добавить классу немного ума. пусть бы сам искал, какое окно даёт наилучший скоринг? только вот как это сделать, если индексом служит время, слишком много же вариантов получается.

Ещё придётся думать о сортировке y (если это ряд pandas с datetimelike-индексом) и заполнении пропусков (у окон есть параметр min_period, так что пропуски вполне могут появиться). Думаю заполнять пропуски либо backfill-ом, либо стратегией, применённой ко всему датасету. Нужен новый параметр fill_na:str={"backfill","strategy"}, видимо.

Есть ещё предложения по стратегиям?

В общем, запостил FR: https://github.com/scikit-learn/scikit-learn/issues/29813
2🏆1
#sklearn

А вот пример роутинга метаданных в sklearn, появившийся относительно недавно. Раньше подобных эффектов (передачи произвольных именованных массивов/объектов любым компонентам конвейера) можно было достичь лишь частично, с использованием глобальных переменных, что к тому же было ненадёжно, неэлегантно и криво.

https://www.youtube.com/watch?v=lQ_-Aja-slA
🔥1