1919
Советы и лайфхаки для соревновательного (в основном) дата сайнса, о которых не расскажут на курсах. Канал про LLM -> @llm_is_all_you_need Мои курсы -> https://stepik.org/a/231306 и https://stepik.org/a/68260
#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);
#Tip9.1 #Preprocessing #Evaluation
Еще одно интересное применение техники Adversarial Validation - поиск и удаление выбросов.
Алгоритм начинается так же, как описан выше:
1. Объединяем фичи трейна и теста
2. Создаем новый таргет: 1 - для теста, 0 для трейна
3. Обучаем модель бинарной классификации
А вот далее:
5. Применяем модель только к трейну и считаем скоры
6. Скоры, которые стремятся к 0 - наиболее сильно отличаются от теста
Строки с наиболее низкими скором можно попробовать удалить из обучающей выборки.
З.Ы. По очевидным причинам из теста ничего удалять нельзя. Но вы можете его проскорить и посмотреть на строки, которые наиболее сильно отличаются от трейна (со скором близким к 1)
#Tip8 #Preprocessing #Evaluation
Если Вы проходили какой-либо базовый курс по ML, то должны были сталкиваться с темой заполнения пропусков в данных.
Но в современных реалиях это может быть не актуально. И на то есть как минимум две причины:
1. Современные градиентные бустинги (LightGBM, CatBoost, XGBoost) умеют работать с пропусками из коробки. Их просто не нужно заполнять - скармливаете модели данные как есть.
2. Очень популярная библиотека оценки моделей - SHAP - показывает пропуски отдельным цветом (серым), что позволяет более осмысленно их интерпретировать.
З.Ы. Различные линейные модели (как впрочем и большинство моделей из скалерна) все же требует предварительного заполнения пропусков.
#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);
#Tip10 #Preprocessing #EDA
Два самых простых способа избавиться от выбросов...
1. С помощью стандартного отклонения:
col = 'col_name'Здесь X - множитель для стандартного отклонения; обычно в пределах 2-4.
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)]
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)]
#Tip9 #Preprocessing #Evaluation
Отправили в платформу предсказания, а скор сильно ниже чем на локальной валидации?
Одной из причин может быть не соответствие распределений в трейне и в тесте в каких-либо фичах.
Определить это вам поможет метод Adversarial Validation. Суть его проста - научить модель отличать трейн от теста.
Алгоритм действий таков:
1. Объединяем фичи трейна и теста в новом датафрейме.
2. Формируем новый бинарный таргет: строки из трейна помечаем как 0, из теста - как 1.
3. Обучаем модель бинарной классификации (которая может считать Feature Importance)
4. Считаем скор (проще всего взять ROC AUC) и формируем Feature Importanceю
5. Далее обращаем внимание на две вещи:
- Высокий скор, например, ROC AUC > 85% - это означает, что модель хорошо отличает трейн от теста
- Аномально высокую важность топовой фичи (или пары фичей) по сравнению с другими фичами
Далее пробуете удалить топовые фичи из датасета при обучении вашей обычной модели и смотрите как это повлияет скор (локальный и на ЛБ).
#Tip7 #Preprocessing
Не многие знают, но всем известная функция train_test_split может стратифицировать выборку по нескольким полям:
X_train, X_test, y_train, y_test = train_test_split(Это пригодится если у вас мульти-лейбл задача или Вы помимо таргета хотите стратифицировать датасет еще по какbм-либо полям.
df[f_cols], df[target], test_size=0.3, random_state=42, stratify=df[['target','col2']])
from skmultilearn.model_selection import iterative_train_test_splitЗ.Ы. не забудьте перевести датасет в numpy перед применением - пандас она почему то не кушает. Читать полностью…
X_train, y_train, X_test, y_test = iterative_train_test_split(df[fcols].values, train[['col1','col2','col3']].values, test_size = 0.3)