Imbalanced classification data can let a model improve overall accuracy while ignoring rare labels. scikit-learn classifiers that support class_weight can give each class a different loss contribution during fitting, which makes the minority class visible to the optimizer without changing the original rows.
LogisticRegression is a compact baseline for this weighting pattern because its class_weight parameter accepts either a class-to-weight dictionary or balanced. The balanced mode calculates weights from the training labels, so the smaller class receives a larger weight before fit() solves the model.
Fit the weighted model from the training split only, then judge it with class-aware metrics such as recall, F1-score, and balanced accuracy. A weighted classifier can lower plain accuracy while improving minority recall, so compare the report with the unweighted baseline before keeping the setting.
from collections import Counter import numpy as np from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.metrics import balanced_accuracy_score, classification_report from sklearn.model_selection import train_test_split from sklearn.utils.class_weight import compute_class_weight X, y = make_classification( n_samples=1200, n_features=6, n_informative=4, n_redundant=0, weights=[0.92, 0.08], class_sep=0.65, flip_y=0.02, random_state=42, ) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.25, stratify=y, random_state=42, ) classes = np.unique(y_train) class_weights = compute_class_weight( class_weight="balanced", classes=classes, y=y_train, ) weight_map = { int(label): round(float(weight), 3) for label, weight in zip(classes, class_weights) } class_counts = { int(label): int(count) for label, count in sorted(Counter(y_train).items()) } print(f"Training class counts: {class_counts}") print(f"Balanced class weights: {weight_map}") models = { "unweighted": LogisticRegression(max_iter=1000, random_state=42), "balanced": LogisticRegression( class_weight="balanced", max_iter=1000, random_state=42, ), } for name, model in models.items(): model.fit(X_train, y_train) predictions = model.predict(X_test) score = balanced_accuracy_score(y_test, predictions) print() print(f"{name} classifier") print(f"class_weight: {model.get_params()['class_weight']}") print(f"balanced accuracy: {score:.3f}") print( classification_report( y_test, predictions, target_names=["majority 0", "minority 1"], digits=3, zero_division=0, ) )
compute_class_weight() prints the same inverse-frequency weights that class_weight="balanced" applies during fitting.
$ python train_weighted_classifier.py
Training class counts: {0: 817, 1: 83}
Balanced class weights: {0: 0.551, 1: 5.422}
unweighted classifier
class_weight: None
balanced accuracy: 0.550
precision recall f1-score support
majority 0 0.915 0.993 0.952 272
minority 1 0.600 0.107 0.182 28
accuracy 0.910 300
macro avg 0.758 0.550 0.567 300
weighted avg 0.886 0.910 0.880 300
balanced classifier
class_weight: balanced
balanced accuracy: 0.589
precision recall f1-score support
majority 0 0.927 0.750 0.829 272
minority 1 0.150 0.429 0.222 28
accuracy 0.720 300
macro avg 0.539 0.589 0.526 300
weighted avg 0.855 0.720 0.773 300
The training split has 817 rows for class 0 and 83 rows for class 1, so balanced weighting assigns class 1 a much larger loss weight.
The weighted run raises minority 1 recall from 0.107 to 0.429, but plain accuracy falls from 0.910 to 0.720. Keep the weighted model only when that tradeoff matches the project cost of missed minority cases.