ds_private_sharing | Unsorted

Telegram-канал ds_private_sharing - Data Science Private Sharing

1919

Советы и лайфхаки для соревновательного (в основном) дата сайнса, о которых не расскажут на курсах. Канал про LLM -> @llm_is_all_you_need Мои курсы -> https://stepik.org/a/231306 и https://stepik.org/a/68260

Subscribe to a channel

Data Science Private Sharing

#Tip11 #Preprocessing #Models
Стратификация - это способ разделения датасета на тренировочную/валидационную/тестовые выборки с сохранением баланса классов. Нужно это чтобы модель не переобучилась под какой-либо отдельный класс.

Но что если у нас задача регрессии и мы тоже хотим как-то равномерно разделить наш датасет. Для этого нужно наш таргет разбить на квантили, и уже по ним стратифицировать выборку. Здесь квантили будут выступать своего рода классами.

Примерно так:

# q - кол-во квантилей
df['price_strat'], bin_edges = pd.qcut(df['price'], q=10, labels=False, retbins=True, precision=0)

Далее стратифицируем выборку по квантилям:
X_train, X_test, y_train, y_test = train_test_split(
df, df['price'], stratify = df['price_strat'])

Нарисовать картинку можно так:
plt.figure(figsize=(15,3))
sns.kdeplot(df['price']);
for v_line in bin_edges:
plt.axvline(v_line, c='red', linewidth=1);

Читать полностью…

Data Science Private Sharing

#Tip9.1 #Preprocessing #Evaluation
Еще одно интересное применение техники Adversarial Validation - поиск и удаление выбросов.

Алгоритм начинается так же, как описан выше:
1. Объединяем фичи трейна и теста
2. Создаем новый таргет: 1 - для теста, 0 для трейна
3. Обучаем модель бинарной классификации

А вот далее:
5. Применяем модель только к трейну и считаем скоры
6. Скоры, которые стремятся к 0 - наиболее сильно отличаются от теста

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

З.Ы. По очевидным причинам из теста ничего удалять нельзя. Но вы можете его проскорить и посмотреть на строки, которые наиболее сильно отличаются от трейна (со скором близким к 1)

Читать полностью…

Data Science Private Sharing

#Tip8 #Preprocessing #Evaluation
Если Вы проходили какой-либо базовый курс по ML, то должны были сталкиваться с темой заполнения пропусков в данных.
Но в современных реалиях это может быть не актуально. И на то есть как минимум две причины:
1. Современные градиентные бустинги (LightGBM, CatBoost, XGBoost) умеют работать с пропусками из коробки. Их просто не нужно заполнять - скармливаете модели данные как есть.
2. Очень популярная библиотека оценки моделей - SHAP - показывает пропуски отдельным цветом (серым), что позволяет более осмысленно их интерпретировать.

З.Ы. Различные линейные модели (как впрочем и большинство моделей из скалерна) все же требует предварительного заполнения пропусков.

Читать полностью…

Data Science Private Sharing

#Tip6 #Evaluation
Обычно важностей фичей вытаскивается просто в виде столбчатой диаграммы.
Я предпочитаю смотреть на них в виде ящика с усами, значения для которого считаются на кросс-валидации.
Примерно так (самый простой способ):

cv_results = lgb.cv(
{'objective': 'rmse'},
lgb.Dataset(X, y),
nfold=5,
stratified=False,
return_cvbooster=True)
models = cv_results['cvbooster'].boosters

а затем выводится так:
fi = pd.DataFrame(index = X.columns)
for i, m in enumerate(models):
fi[f'm_{i}'] = m.feature_importance()
fi = fi.stack().reset_index().iloc[:,[0,2]]
fi.columns = ['feature','importance']

cols_ord = list(fi.groupby('feature')['importance'].mean().sort_values(ascending=False).index)

plt.figure(figsize=(10, 10))
sns.boxplot(data=fi, y='feature', x='importance', orient='h', order=cols_ord);

Спросите, что это дает? Это дает понимание стабильности топовых фичей.
И если мы видим большой разброс по важности фичи, то можем ее также стратифицировать по фолдам.

Читать полностью…

Data Science Private Sharing

#Tip10 #Preprocessing #EDA
Два самых простых способа избавиться от выбросов...

1. С помощью стандартного отклонения:

col = 'col_name'
x = 2 # множитель

upper_lim = df[col].mean() + df[col].std() * x
lower_lim = df[col].mean() - df[col].std() * x

df = df[(df[col] > lower_lim) & (df[col] < upper_lim)]

Здесь X - множитель для стандартного отклонения; обычно в пределах 2-4.

2. С помощью процентилей:
col = 'col_name'

upper_lim = df[col].quantile(.99) # .95
lower_lim = df[col].quantile(.01) # .05

df = df[(df[col] > lower_lim) & (df[col] < upper_lim)]

Процентиль задается любой, но поближе к одной из границ.

Читать полностью…

Data Science Private Sharing

#Tip9 #Preprocessing #Evaluation
Отправили в платформу предсказания, а скор сильно ниже чем на локальной валидации?
Одной из причин может быть не соответствие распределений в трейне и в тесте в каких-либо фичах.

Определить это вам поможет метод Adversarial Validation. Суть его проста - научить модель отличать трейн от теста.

Алгоритм действий таков:
1. Объединяем фичи трейна и теста в новом датафрейме.
2. Формируем новый бинарный таргет: строки из трейна помечаем как 0, из теста - как 1.
3. Обучаем модель бинарной классификации (которая может считать Feature Importance)
4. Считаем скор (проще всего взять ROC AUC) и формируем Feature Importanceю
5. Далее обращаем внимание на две вещи:
- Высокий скор, например, ROC AUC > 85% - это означает, что модель хорошо отличает трейн от теста
- Аномально высокую важность топовой фичи (или пары фичей) по сравнению с другими фичами

Далее пробуете удалить топовые фичи из датасета при обучении вашей обычной модели и смотрите как это повлияет скор (локальный и на ЛБ).

Читать полностью…

Data Science Private Sharing

#Tip7 #Preprocessing
Не многие знают, но всем известная функция train_test_split может стратифицировать выборку по нескольким полям:

X_train, X_test, y_train, y_test = train_test_split(
df[f_cols], df[target], test_size=0.3, random_state=42, stratify=df[['target','col2']])

Это пригодится если у вас мульти-лейбл задача или Вы помимо таргета хотите стратифицировать датасет еще по какbм-либо полям.
Но делает она это путем составлением всех возможных комбинаций значений перечисленных полей.
И если какой-либо комбинации окажется слишком мало для разделения на трейн и тест (т.е. она всего одна), то функция свалится с ошибкой.

К счастью есть функция iterative_train_test_split из пакета scikit-multilearn.
Она итеративно попробует создать сбалансированный датасет, но если не получится и какой-либо комбинации не хватит, то она поместит ее только в трейн, но не упадет с ошибкой:
from skmultilearn.model_selection import iterative_train_test_split
X_train, y_train, X_test, y_test = iterative_train_test_split(df[fcols].values, train[['col1','col2','col3']].values, test_size = 0.3)

З.Ы. не забудьте перевести датасет в numpy перед применением - пандас она почему то не кушает.

Читать полностью…
Subscribe to a channel