sklearn
LogisticRegression
classification threshold
machine learning
Python

sklearn LogisticRegression and changing the default threshold for classification

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

LogisticRegression in scikit-learn produces probabilities or decision scores, but the default predict() method converts those to class labels using a built-in threshold. For binary classification, that threshold is effectively 0.5 on the positive-class probability. If your problem values recall, precision, cost sensitivity, or class imbalance differently, you should usually leave the model alone and change the decision threshold yourself.

Understand What predict() Is Doing

For binary logistic regression, predict_proba(X)[:, 1] gives the estimated probability of the positive class. The default predict(X) then labels rows as positive when that probability crosses the standard cutoff.

Basic example:

python
1from sklearn.datasets import make_classification
2from sklearn.linear_model import LogisticRegression
3from sklearn.model_selection import train_test_split
4
5X, y = make_classification(
6    n_samples=500,
7    n_features=8,
8    weights=[0.8, 0.2],
9    random_state=42
10)
11
12X_train, X_test, y_train, y_test = train_test_split(
13    X, y, test_size=0.3, random_state=42
14)
15
16model = LogisticRegression(max_iter=1000)
17model.fit(X_train, y_train)
18
19proba = model.predict_proba(X_test)[:, 1]
20print(proba[:5])

Those probabilities are the real raw material. The classification threshold is just the policy layer on top.

Apply a Custom Threshold Manually

Once you have positive-class probabilities, changing the threshold is easy:

python
1import numpy as np
2
3threshold = 0.3
4y_pred_custom = (proba >= threshold).astype(int)
5
6print(y_pred_custom[:10])

This does not retrain the model. It only changes how predicted probabilities are turned into labels.

That distinction matters because many people think they need a special logistic-regression parameter to change the threshold. They usually do not.

Compare Metrics at Different Thresholds

Threshold choice is a business decision backed by metrics, not a magic number. For example:

python
1from sklearn.metrics import classification_report
2
3for threshold in [0.2, 0.5, 0.8]:
4    y_pred = (proba >= threshold).astype(int)
5    print(f"Threshold: {threshold}")
6    print(classification_report(y_test, y_pred))

Lower thresholds usually increase recall and decrease precision. Higher thresholds usually do the opposite.

This is especially important for imbalanced problems such as fraud detection, medical screening, or alerting systems, where the default cutoff may not align with the true cost of false positives and false negatives.

Choose Thresholds with Precision-Recall or ROC Analysis

A better way to pick a threshold is to inspect the tradeoff curve rather than guessing.

python
1from sklearn.metrics import precision_recall_curve
2
3precision, recall, thresholds = precision_recall_curve(y_test, proba)
4
5for p, r, t in zip(precision[:5], recall[:5], thresholds[:5]):
6    print(f"threshold={t:.3f}, precision={p:.3f}, recall={r:.3f}")

You can then choose the threshold that matches the actual objective:

  • maximize recall above a minimum precision
  • maximize F1 score
  • satisfy a business cost function

The same idea applies if you prefer ROC analysis, but precision-recall curves are often more informative for imbalanced datasets.

Keep Threshold Tuning Separate from Training

Threshold tuning should be done on validation data, not the same data used to fit the model. A practical workflow is:

  1. train on training data
  2. choose threshold on validation data
  3. report final performance once on test data

That avoids optimistic threshold selection based on the test set.

If you want to operationalize this, wrap the model and threshold together in your own prediction function:

python
def predict_with_threshold(model, X, threshold=0.5):
    proba = model.predict_proba(X)[:, 1]
    return (proba >= threshold).astype(int)

This makes deployment behavior explicit and reproducible.

Common Pitfalls

  • Looking for a LogisticRegression constructor parameter to change the classification threshold.
  • Evaluating threshold choices on the training set and overstating model quality.
  • Confusing probability calibration with threshold selection.
  • Reporting predict() metrics only, even when the business problem needs a different precision-recall tradeoff.
  • Changing the threshold without documenting it in the inference path.

Summary

  • Scikit-learn logistic regression outputs probabilities; predict() just applies a default decision rule.
  • Change the threshold by using predict_proba and comparing against your chosen cutoff.
  • Threshold tuning changes classification behavior without retraining the model.
  • Pick thresholds using validation metrics such as precision-recall tradeoffs.
  • Keep the chosen threshold explicit in deployment so predictions remain understandable and reproducible.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.