Data Science Private Sharing
Open in Telegram
Советы и лайфхаки для соревновательного (в основном) дата сайнса, о которых не расскажут на курсах. Если есть идеи, которыми хотите поделиться, пишите -> @slivka_83 Tags: #EDA #Evaluation #Models #TimeSeries #Train #Preprocessing #Valid
Show more1 340
Subscribers
-124 hours
+57 days
+2630 days
Posts Archive
#курс
После долгих лет (альфа-)тестирования, курс "Алгоритмы Машинного обучения с нуля" наконец-то выходит в свет (бета-тестирование :)
Курс бесплатный :) На Степике: https://stepik.org/course/68260
Курс посвящен реализации всех классических алгоритмов машинного обучения с нуля. На чистом питоне + нампай и пандас.
Упор в курсе будет делаться именно на алгоритмы. Хотя и немного математики тоже присутствует.
Пока реализовано 6 уроков:
- Два вида линейных моделей
- Деревья решений (классификация и регрессия)
- Случайный лес (классификация и регрессия)
Остальные будут открываться по мере готовности.
З.Ы. Для успешного прохождения курса вам понадобятся знания основ МЛ, а также уверенное владение питоном.
За инфошумом вокруг всяких ChatGPT и прочих Diffusion-моделей вы могли не заметить появления новой SOTA среди табличных данных - Dummy-алгоритмов. Одно из главных их преимуществ - для них практически не нужно готовить фичи (почти как в нейронках).
В скалерн завезли две их реализации: для классификации и для регрессии.
- https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyClassifier.html
- https://scikit-learn.org/stable/modules/generated/sklearn.dummy.DummyRegressor.html
Использовать довольно просто:
from sklearn.dummy import DummyRegressor model = DummyRegressor() model.fit(X, y)Думаю скоро они займут топовые строчки в соревнованиях. Уже удалось применить их в деле?
Всем привет :)
Почти 2 месяца ничего не постил.
А это потому, что был очень занят - писал лучший курс по машинному обучению (и другие курсы ему завидуют :)
А если серьезно, то аналогов такому курсу и в правду не встречал.
Курс посвящен реализации всех классических алгоритмов машинного обучения с нуля. На чистом питоне + нампай и пандас.
Упор в курсе будет делаться именно на алгоритмы. Хотя и немного математики там есть.
Курс бесплатный :)
Пока реализовал 6 модулей: два вида линейных моделей, деревьев решений и случайного леса.
Остальные будут добавляться по мере готовности.
Сейчас ищу 3-4 человека, которые помогут протестировать задания перед публикацией в открытый доступ.
От кандидатов жду понимания основ МЛ, умения писать на питоне и знания нампай и пандас. Плюс свободное время.
Кто хочет - пишите в личку.
#Tip32 #Train
Самый простой способ борьбы с выбросами (о котором не расскажут на курсах :)
1. Обучаете модель.
2. Делаете предсказание на трейне.
3. Объекты, у которых самые большие ошибки выкидываете из трейна.
З.Ы. Для задачи регрессии возможно стоит попробовать метрику MAE, поскольку MSE сильнее штрафует за большие ошибки (а значит лучше борется с выбросами).
#Tip31 #Train
Обычно оптуну воспринимают как инструмент оптимизации гипер-параметров модели.
Но ее возможности несколько шире. Например, можно повесить на нее и препроцессинг :)
Вот небольшой пример. Допустим у вас есть датасет. В датасете есть колонки с пропусками. Колонки, в которых очень много пропусков - бесполезны и по-хорошему от них нужно избавиться. Но какой процент считать бесполезным? Выкидывать колонки, в которых более 90% пропусков? Более 80%? 70%? Можно пробовать выяснять это вручную, а можно поручить это опутне. Примерно так:
nulls_perc = (X.isna().sum() / X.shape[0] * 100).astype(int).sort_values(ascending=False)
def objective(trial):
params = {
'objective': 'regression',
'metric': 'mse',
'learning_rate': trial.suggest_float('learning_rate', 1e-5, 0.7, log=True),
'num_leaves': trial.suggest_int('num_leaves', 20, 150),
'verbose': -1
}
nulls_max = trial.suggest_int('nulls_max', 10, 100)
f_cols = list(nulls_perc[nulls_perc < nulls_max].index)
X_train = train[f_cols]
X_test = test[f_cols]
model = lgbm.train(
params = params,
train_set = lgbm.Dataset(X_train, y_train),
valid_sets = lgbm.Dataset(X_test, y_test),
verbose_eval = None
)
score = model.best_score['valid_0']['l2']
print(score, len(f_cols))
return score
study = optuna.create_study(direction='minimize')
study.optimize(objective, n_trials=3000, timeout=300)
Здесь мы в начале подсчитываем процент пропусков в каждой колонке. Затем внутри цикла генерируем кол-во пропусков которое хотим оставить. Отбираем колонки по этому показателю и формируем датасеты для обучения и валидации.
Точно также можно выкидывать статичные колонки, можно перебирать способы заполнение пропусков и кучу других препроцессинговых задач.Написал небольшой эпос о библиотеке transformers.
И хотя текста много это все равно только лишь введение. Функционала в библиотеке слишком много.
https://habr.com/ru/post/704592/
Для тех кто не в курсе: библиотека Transformers предоставляет доступ к куче современных предобученных DL-моделей.
И на текущий момент является чуть ли не аналогом скалерна в мире Deep Learning.
#Tip30 #Train
В отличии от других бустингов в LGBM деревья растут асимметрично (по листьям). В то время как в CatBoost и XGBoost рост происходит по уровням. И это декларируется разработчиками LGBM как одно из основных преимуществ.
И тут закралась ловушка: если вы ограничите глубину дерева в LGBM (а для бустингов рекомендуются не глубокие деревья), то вы по сути превратите его в симметричное урезанное дерево!
Именно поэтому по умолчанию в LGBM глубина деревьев (max_depth) = -1 (что значит - бесконечная), а в CatBoost и XGBoost глубина = 6.
Но бесконечно расти тоже не вариант - так мы просто запомним все данные. Поэтому, чтобы ограничить глубину деревьев используйте такие параметры как:
num_leaves — максимальное количество листьев в одном дереве
min_data_in_leaf — минимальное количество данных в одном листе
min_data_in_bin — минимальное количество данных внутри одного бина
min_data_per_group — минимальное количество данных на одно значение категориальной фичи
min_gain_to_split — минимальный прирост, которое дает разделение
min_sum_hessian_in_leaf — минимальная сумма гессиана в одном листе
Считается, что асимметричные деревья более склонны к переобучению, особенно на маленьких датасетах. В этом случае рекомендуется задействовать max_depth. Также стоит использовать max_depth на очень больших датасетах - иначе модель будет слишком долго учиться.
З.Ы. Недавно поведение аналогичное LGBM и XGBoost реализовали в катбусте.
Залил на Хабр статью про библиотеку Voilà (читается "вуаля"), которая позволяет конвертировать ноутбуки в веб-приложения.
https://habr.com/ru/post/698662/
#Tip29 #Train
Случайный лес не переобучается! Но с оговоркой... :)
Лес переобучается, но он не переобучается от увеличения кол-ва деревьев (при прочих разумных условиях).
Есть у Случайного леса такое свойство, что по мере увеличения кол-ва деревьев кривые обучения выходят на плато и дальше на их предсказание влияет только шум. Для бустинга же бездумное увеличение кол-ва деревьев это прямой путь к оверфиту.
Но если вы зададите для леса глубину скажем 1000, то он конечно же переобучится: просто запомнит данные, вместо того чтобы выучить закономерности.
Интуитивно это можно понять так... Представим себе генеральную совокупность на 10 000 000 экземпляров. Возьмем из нее два случайных экземпляра и посчитаем их среднее. Очевидно, что это среднее будет сильно отличаться от среднего генеральной совокупности. Также и для трех, четыре, пяти... экземпляров. Но если мы возьмем 1 000 000 случайных экземпляров, то среднее будет очень близко к генеральной совокупности. И добавление миллион первого, второго, третьего экземпляра сильно на среднее не повлияет.
Лес точно также усредняет предсказания своих деревьев. И в определенный момент кол-во деревьев становится достаточным, и добавление новых уже ни на что не виляет (если не считать шум).
Написал статью про фреймворк Hydra
https://habr.com/ru/post/696820/
Гидра предназначена для управления файлами конфигурации в ML-проектах.
#Tip28 #Train
Nested Cross-Validation - самая дорогая из "классических" схем валидации.
Состоит из двух вложенных кросс-валидаций: внешней и внутренней. Внутренняя используется для подбора гипер-параметров/выбора модели, а внешняя - для оценки модели.
При обучении модели вам нужно решить две задачи: подобрать гипер-параметры и оценить модель. Если использовать для них только одну кросс-валидацию, то это может привести к чрезмерно оптимистичной оценке модели. Поскольку одни и те же данные используются и для подбора гипер-параметров и для оценки модели. Обычно в этом случае для финальной оценки выделяют тестовую часть, которая никаким образом в обучении не участвует. Но тогда мы теряем часть данных для обучения. С помощью Nested Cross-Validation можно задействовать все данные для обучения и получить корректную оценку.
Алгоритм такой:
1. Делим весь датасет на фолды
2. Для каждого фолда:
- Делим часть для обучения на свои фолды
- Подбираем гипер-параметры
- Обучаем одну модель на всей тренировочной части на лучших гипер-параметрах
- Оцениваем модель на тестовой части
3. Усредняем скоры по всем внешним фолдам
На выходе, помимо скоров, у нас будут N обученных моделей. Для предсказания используются все три, результаты усредняются.
Т.к. такая схема очень дорогая по времени, то применять ее следует в двух случаях:
1. У вас очень маленький датасет.
2. У вас есть своя майнинг-ферма :)
З.Ы. Для временного CV все аналогично просто оба цикла будут со сдвигом.
#Tip27 #Train
При K-fold валидации прогнозы делаются на тестовых частях, которые не участвуют в обучении и эти прогнозы называются Out-of-Fold (OOF) Predictions.
Обычно они используются по прямому назначению - оценки модели.
Но можно их задействовать и более интересным способом - для стэкинга.
Стэкинг это способ построения ансамбля моделей, в котором есть базовые модели и есть мета модель.
Базовые модели обучаются на исходных данных, а мета-модель на предсказаниях базовых моделей.
Обычно для построения стекинга исходный датасет делится на несколько крупных частей из-за чего все модели увидят меньше данных. Но благодаря Out-of-Fold Predictions можно реализовать обучение на всех (почти) данных.
В соревновательном DS алгоритм в целом такой:
- Разбиваем датасет с метками на трейн и эвал + у нас уже есть тестовый датасет от организаторов.
- Трейн разбиваем на фолды.
- Для каждого фолда:
- Обучаем модель на тренировочной части.
- Выполняем предсказание для тестовой части фолда и сохраняем предсказание в отдельный вектор, каждый элемент которого соответствует строке тренировочного датасета.
- Выполняем предсказание для валидационного датасета.
- Выполняем предсказание для тестового датасета.
- Собранные предсказания на фолдах передаем в мета-модель как фичу.
- Усредняем предсказания полученные для валидационного и тестового датасета.
- С помощью мета-модели делаем предсказание для усредненных значений валидационного и тестового датасета.
- Значения полученные для валидационного датасета используем для оценки, а значения полученные для тестового датасета заворачиваем в сабмит.
Реализуется данный алгоритм обычно вручную.
Варианты для изменений:
- На фолдах можно обучить несколько видов базовых моделей, тогда на вход мета-модели пойдет несколько фичей.
- На шаге обучения мета-модели можно присоединить фичи-предсказания к исходным фичам.
Для большей эффективности ансамбля для базовых и мета-модели стараются задействовать разные алгоритмы (линейные/KNN/деревья).
Ловите еще одну статью :)
На этот раз повещенную Evidently - библиотеке для мониторинга качества данных и моделей в проде.
https://habr.com/ru/post/692272/
Лайки приветствуются :)
Написал на Хабре еще один туториал (в рамках курса ML System Design).
На этот раз посвященный ClearML - системе трекинга Ml-экспериментов.
https://habr.com/ru/post/691314/
Заходим, читаем и не забываем ставить лайки :)
Написал на Хабре туториал про Dagster - оркестратор предназначенный для организации конвейеров обработки данных: ETL, проведение тестов, формирование отчетов, обучение ML-моделей.
https://habr.com/ru/post/690342/
Заходим, не стесняемся, лайкаем :)
З.Ы. Туториал написан в рамках прохождения курса ML System Design (можете еще присоединиться): https://ods.ai/tracks/ml-system-design-22
#Tip26.1 #EDA
Для понимания как строится PDP попробуем построить его в ручную.
Алгоритм такой:
- Выбираем какую-нибудь фичу
- Вычисляем все ее уникальные значения (для непрерывной фичи можно взять сколько-то квантильных значений)
- Проходимся по каждому из них и:
- В исходном датасете для нашей фичи заменяем все значения на одно - текущее
- Делаем предсказание
- Усредняем полученные предсказания
И так для каждого уникального значения. В итоге получаем набор пар: (уникальное значение - средний предикт)
По смыслу мы как бы спрашиваем модель: а что если бы фича принимала бы только одно значение из возможного диапазона (при этом другие фичи не трогаем) - какими тогда стали бы предсказания?
- Выводим их на графике.
З.Ы. Также для демонстрации влияния на отдельные примеры выведем случайные 50 записей.
# Считаем PDP
col = 'grade'
uniq_values = sorted(X[col].unique())
preds = {}
for v in uniq_values:
X_copy = X.copy()
X_copy[col] = v
preds[v] = model.predict(X_copy)
preds = pd.DataFrame(preds)
pdp = preds.mean()
# Выбираем лучайные 50 записей
samp = preds.sample(50).copy()
samp = samp.reset_index().melt('index', var_name='cols', value_name='vals')
# Строим графики
plt.figure(figsize=(13,6))
sns.lineplot(data=samp, x='cols', y='vals', hue='index', legend=False, palette='GnBu', alpha=0.2)
sns.lineplot(x=pdp.index, y=pdp, color='red', linewidth=2)
plt.xlabel(col)
plt.ylabel('PDP');#Tip26 #EDA
Partial dependence plots (PDP) — это график, который позволяет понять, как различные значения конкретной фичи влияют на предсказания модели.
Под капотом PDP меняет исходные значения целевой фичи и смотрит как изменяются предсказания модели.
По сути PDP показывает ожидаемое среднее значение таргета при различных значения интересующей нас фичи.
Строятся PDP в двух вариантах - для одной фичи и для пары фичей. Обычно для графика выбирают наиболее значимые для модели фичи.
Т.к. PDP работает с уже обученной моделью, то это еще один способ приоткрыть завесу черного ящика (наряду с SHAP и пр.).
PDP можно построить либо с помощью скалерна (но на вход принимаются только родные для скалерна модели):
fig, ax = plt.subplots(figsize=(18.5,4.5)) features = ['bahrooms','sqf_living','yr_buil'] PartialDependenceDisplay.from_estimator(clf, X, features, ax=ax);Либо с помощью сторонних пакетов, например PDPbox (https://github.com/SauceCat/PDPbox):
features_to_plot = ['sqf_living','bahrooms'] inter1 = pdp.pdp_interact(model=model, dataset=X, model_features=fcols, features=features_to_plot) pdp.pdp_interact_plot(pdp_interact_out=inter1, feature_names=features_to_plot, plot_type='contour') plt.show()З.Ы. У PDPbox есть еще много других интересных графиков.
#Tip25 #TimeSeries #Valid
TimeSeriesSplit - известный способ разбиения временных данных для кросс-валидации из скалерна.
Метод разбиения, который в нем реализован называется Expanding window.
Но наука о временных рядах знает гораздо больше способов разбить временные данные.
Основных два:
- Expanding Window
- Sliding Window
А дальше включается воображение:
- Нахлесты между периодами
- Пробел между трейном/валидом/тестом
- Способ выделения тестовой выборки
Как уже было сказано, Expanding Window можно найти в скалерне.
Для Sliding Window и других ухищрений нужно либо писать свой код (основные ML-библиотеки принимают готовые сплиты в виде индексов строк), либо поискать на гитхабе:
https://github.com/WenjieZ/TSCV
https://github.com/AaronOS0/Time-series-Cross-validation
https://github.com/marmurar/jano
и прочие...
#Tip24 #Train
Для честной проверки все методы валидации включают в себя тестовую выборку. Тест при этом никак не используется для обучения. Но в нем может содержаться полезная информация.
В самом простом случаи можете просто объединить трейн и тест и обучить модель заново с лучшими гипер-праметрами. Но они были подобраны для другого датасета, и могут быть не оптимальными для нового.
Поэтому для градиентного бустинга придумали такой фокус:
1. Разбиваете датасет на трейн и тест.
2. Обучаете модель и подбираете наилучшие гипер-параметры. Проверяете модель на тесте.
3. Объединяете трейн и тест.
4. Заново обучаете модель на полном датасете с найденными гиперпарамтрами, но только увеличиваете в них количество итераций на % теста.
Т.е. если у вас получилось 100 итераций, а тест вы взяли в 20%, то для нового обучения выставляете кол-во итераций в 120.
5. Делаете предсказание с новой моделью. При этом протестировать ее сможете только на лидерборде.
Данный фокус не имеет под собой научного обоснования, но иногда докидывает...
#Tip23 #Preprocessing
pipe - это метод реализации паплайна в датафреймах пандас посредством последовательной цепочки преобразований.
З.Ы. Не путать с Pipeline из скалерна - это разные вещи.
Предположим, у нас есть ряд функций, каждая из которых отвечает за отдельный вид преобразований.
Тогда в пандасе пайплайн можно реализовать так:
def normilize(df):
...
return df
def fill_null(df, method):
...
return df
def parse_time(df):
...
return df
new_df = (df
.pipe(normilize)
.pipe(fill_null, method='median')
.pipe(parse_time)
)
