Class Weights Do Not Improve Model Performance


Welcome!

Every Monday, I’ll drop a no-fluff, straight-to-the-point tip on a data science skill, tool, or
method to help you stay sharp in the field. I hope you find it useful!


My New Book is Out!

Imbalanced Data, Myths, Mistakes and Modern Solutions

Most advice about imbalanced data is incomplete, or simply wrong. This book challenges common assumptions and provides practical, evidence-based guidance for tackling class imbalance and building better machine learning models.

Class Weights Do Not Improve Model Performance

Here’s a pattern I see all the time.

A practitioner trains a model on an imbalanced dataset and obtains a ROC-AUC of 0.670. They then set class_weight="balanced", train the model again, and obtain a ROC-AUC of 0.675.

The second number is higher, so the conclusion seems obvious: class weighting improved the model.

Except we have ignored an important question:

" How precise are those ROC-AUC estimates?"

 

A performance metric calculated on one test set is only an estimate. If the test set contained a slightly different collection of observations, we would obtain a slightly different value.

Therefore, the fact that one ROC-AUC is numerically higher than another does not necessarily mean that one model is genuinely better.

A difference is not necessarily an improvement

Imagine that we obtain the following results:

  • No class weights: ROC-AUC = 0.670
  • class_weight="balanced": ROC-AUC = 0.675

The observed improvement is 0.005.

If we report only those two numbers, class weighting wins.

But what if the uncertainty around that difference stretches is around 0.04?

Then, those values look more like the following:

  • No class weights: ROC-AUC = 0.666 - 0.674
  • class_weight="balanced": ROC-AUC = 0.671 - 0.679

Now we can see that the performance intervals overlap substantially. Therefore, the difference in performance is, most likely, not statistically significant. In lay terms: both models perform the same.

Show me the data

Let’s look at this with an example.

The following example creates an imbalanced dataset and trains two logistic regression models. Everything is identical except that one model uses class_weight="balanced".

First, we evaluate both models once on the test set:

import numpy as np

import matplotlib.pyplot as plt

​
​

from sklearn.datasets import make_classification

from sklearn.linear_model import LogisticRegression

from sklearn.metrics import roc_auc_score

from sklearn.model_selection import train_test_split

​
​

# Create an imbalanced dataset

X, y = make_classification(

n_samples=2200,

n_features=12,

n_informative=5,

n_redundant=3,

weights=[0.93, 0.07],

class_sep=0.75,

flip_y=0.03,

random_state=3,

)

​
​

X_train, X_test, y_train, y_test = train_test_split(

X,

y,

test_size=0.25,

stratify=y,

random_state=11,

)

​
​

models = {

"No weights": LogisticRegression(max_iter=2000),

"Balanced": LogisticRegression(

max_iter=2000,

class_weight="balanced",

),

}

​
​

probabilities = {}

​
​

for name, model in models.items():

model.fit(X_train, y_train)

probabilities[name] = model.predict_proba(X_test)[:, 1]

​
​

single_scores = {

name: roc_auc_score(y_test, probs)

for name, probs in probabilities.items()

}

​
​

print(single_scores)

The results are approximately:

No weights: 0.670

Balanced: 0.675

If we stopped here, we might conclude that class weighting worked.

Let’s not stop here.

Estimating the uncertainty

We can bootstrap the test set to see how much the ROC-AUC changes when the evaluation sample changes.

Importantly, we sample the same observations for both models during each bootstrap iteration. This gives us a paired comparison: for every sample, we calculate not only the two ROC-AUC values, but also their difference.

rng = np.random.default_rng(0)

n_bootstraps = 2000

​
​

bootstrap_scores = {

"No weights": [],

"Balanced": [],

}

bootstrap_differences = []

​
​

for _ in range(n_bootstraps):

idx = rng.integers(0, len(y_test), len(y_test))

​
​

# ROC-AUC requires both classes

if len(np.unique(y_test[idx])) < 2:

continue

​
​

auc_no_weights = roc_auc_score(

y_test[idx],

probabilities["No weights"][idx],

)

auc_balanced = roc_auc_score(

y_test[idx],

probabilities["Balanced"][idx],

)

​
​

bootstrap_scores["No weights"].append(auc_no_weights)

bootstrap_scores["Balanced"].append(auc_balanced)

bootstrap_differences.append(

auc_balanced - auc_no_weights

)

We can now plot the ROC-AUC estimates and their 95% bootstrap intervals. The second panel shows the paired difference directly.

names = list(models)

means = [

np.mean(bootstrap_scores[name])

for name in names

]

​
​

intervals = [

np.percentile(bootstrap_scores[name], [2.5, 97.5])

for name in names

]

​
​

errors = np.array([

[mean - low, high - mean]

for mean, (low, high) in zip(means, intervals)

]).T

plt.figure(figsize=(5, 4))

plt.errorbar(

names,

means,

yerr=errors,

fmt="o",

capsize=6,

color="#c44e7b",

)

​
​

plt.ylabel("ROC-AUC")

plt.title("Performance with 95% bootstrap intervals")

plt.grid(axis="y", alpha=0.25)

plt.tight_layout()

plt.show()

The weighted model still has a slightly higher point estimate. But the confidence intervals overlap:

So the correct conclusion is not that class weighting improved performance.

The correct conclusion is that this experiment does not provide convincing evidence that either model performs better.

This is not an isolated result

For my book, Imbalanced Data: Myths, Mistakes and Modern Solutions, I investigated this question across 37 benchmark datasets.

I trained random forests and modern implementations of gradient boosting machines on the original class distribution. I then trained otherwise identical models using class weights derived from the imbalance ratio.

Across the datasets, the ROC-AUC of the weighted models remained essentially unchanged. The observed differences fell within the estimated uncertainty: class weighting did not make the models inherently better at separating positive from negative observations.

But class weighting did change something.

It distorted the estimated probabilities.

Class weights alter the effective class distribution seen during training. Consequently, the resulting probabilities are calibrated to the weighted distribution rather than the original one. In my experiments, this was reflected in higher Brier scores for the weighted models.

The same classification performance could be achieved by training on the original distribution and adjusting the decision threshold. Class weighting mostly shifted the decision boundary towards 0.5; it did not add new discriminatory information.

Conclusion

Never conclude that a modelling intervention worked because one metric increased on one evaluation sample.

Estimate the uncertainty around the comparison using cross-validation or bootstrapping. Ideally, examine the paired differences, not just two isolated error bars.

If the intervals overlap, the evidence does not establish an improvement.

And remember that class_weight="balanced" is not harmless. It may leave ranking performance unchanged while making the predicted probabilities harder to interpret.

If you liked this article, you’ll also enjoy my new book: Imbalanced Data: Myths, Mistakes and Modern Solutions.

I hope this information was useful!

Wishing you a successful week ahead - see you next Monday! πŸ‘‹πŸ»

Sole


Ready to enhance your skills?

Our specializations, courses and books are here to assist you:


Hi…I’m Sole
​
I’m a Python developer, AI educator, and developer advocate. I’m the creator and maintainer of Feature-engine and the main instructor at Train in Data.
​
I share practical Python lessons, open-source insights, and ideas to help our community build better software and grow as developers and data scientists.

You are receiving this email because you subscribed to our newsletter, signed up on our website, purchased or downloaded any products from us.


Follow us on social media

Copyright (C) 2026 Train in Data. All rights reserved.

​
​Unsubscribe Β· Preferences​

Train in Data

by Soledad Galli, PhD | Data scientist | Python Developer | Best-selling instructor | Book author | πŸ‘‰ www.trainindata.com

Read more from Train in Data

Welcome! Every Monday, I’ll drop a no-fluff, straight-to-the-point tip on a data science skill, tool, ormethod to help you stay sharp in the field. I hope you find it useful! My New Book is Out! Imbalanced Data, Myths, Mistakes and Modern Solutions Most advice about imbalanced data is incomplete, or simply wrong. This book challenges common assumptions and provides practical, evidence-based guidance for tackling class imbalance and building better machine learning models. Find out more...

Models are not sensitive to class imbalance

Welcome! Every Monday, I’ll drop a no-fluff, straight-to-the-point tip on a data science skill, tool, ormethod to help you stay sharp in the field. I hope you find it useful! My latest eBook Imbalanced Data, Myths, Mistakes and Modern Solutions is here! Most advice about imbalanced data is incomplete, or simply wrong. This book challenges common assumptions and provides practical, evidence-based guidance for tackling class imbalance and building better machine learning models. Get your copy...

Welcome! Every Monday, I’ll drop a no-fluff, straight-to-the-point tip on a data science skill, tool, ormethod to help you stay sharp in the field. I hope you find it useful! The ROC Curve Myth for Imbalanced Datasets One myth I hear far too often is that the ROC curve is not a good metric for evaluating models trained on imbalanced datasets because it tends to produce overly optimistic ROC-AUC values. This is not true. Last Monday we saw that ROC curves are insensitive to class prevalence:...