Условие
Объясните, что делает class_weight='balanced' в sklearn, как это математически.
Решение
Подход
class_weight='balanced' устанавливает w_c = n_total / (K · n_c), где K — число классов. Самый редкий класс получает наибольший вес. Каждый пример классa c вносит в loss w_c · L(y_i, ŷ_i) вместо обычного L.
Эквивалентно oversampling без действительного дублирования данных.
Реализация
from sklearn.linear_model import LogisticRegression
from sklearn.utils.class_weight import compute_class_weight
import numpy as np
# auto-balanced
clf = LogisticRegression(class_weight='balanced').fit(X, y)
# вручную
classes = np.unique(y)
weights = compute_class_weight('balanced', classes=classes, y=y)
weight_dict = dict(zip(classes, weights))
# Например: {0: 0.51, 1: 33.3} для 99.7%/0.3%
clf = LogisticRegression(class_weight=weight_dict)Эквиваленты в библиотеках
# XGBoost (бинарка)
import xgboost as xgb
scale_pos_weight = (y==0).sum() / (y==1).sum() # ~333
xgb.XGBClassifier(scale_pos_weight=scale_pos_weight)
# LightGBM
import lightgbm as lgb
lgb.LGBMClassifier(class_weight='balanced')
# или is_unbalance=True
# CatBoost
from catboost import CatBoostClassifier
CatBoostClassifier(auto_class_weights='Balanced')
# PyTorch
import torch.nn as nn
weights = torch.tensor([1.0, 333.0])
criterion = nn.CrossEntropyLoss(weight=weights)Sample weight vs class weight
class_weight: вес одинаков для всех экземпляров класса.sample_weight: индивидуальный вес каждой строки. Гибче (можно учитывать recency, importance).
sample_w = np.where(y==1, 100, 1) * recency_weight # сочетаем
clf.fit(X, y, sample_weight=sample_w)Сравнение с oversampling
| class_weight | Oversampling | SMOTE | |
|---|---|---|---|
| Память | O(n) | O(K·n) | O(K·n) |
| Train time | Тот же | Дольше | Дольше |
| Калибровка | Раbias predict_proba | Bias proba | Bias proba |
| Variance reduction | Нет | Да (точные дубли увеличивают шум) | Чуть лучше дублирования |
class_weight обычно — first choice для tree-based / linear; почти бесплатно и эффективно.
Подводные камни
- Predict_proba становится mis-calibrated: модель predicts «balanced» probabilities, не population priors. Калибровать после.
- С threshold по умолчанию 0.5: после class_weight модель predicts много positives. Думайте через threshold, не «balanced» означает «лучше».
- На очень severe imbalance (1:10000+) class_weight может не помочь; нужно сочетание с focal loss или undersampling.
- На multilabel или multiclass с rare class — взвешивание тоже помогает, но осторожно с micro vs macro метриками.
class_weight='balanced_subsample'для RF — пересчёт весов на каждом bootstrap, не на полном dataset.
Эталонный ответ
class_weight='balanced' устанавливает w_c = n_total/(K·n_c), умножая loss на вес класса. Эквивалент oversampling без дублирования. Эквиваленты: scale_pos_weight (XGB), class_weight (LGBM), weight в CrossEntropyLoss (torch). Predict_proba становится biased — нужна калибровка.