Logistic Regression
Gradient Descent
Java Programming
Machine Learning
Algorithm Implementation

Implementation of Logistic regression with Gradient Descent in Java

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

Implementing logistic regression with gradient descent in Java is a solid way to understand classification fundamentals. Logistic regression predicts class probability using the sigmoid of a linear score. Gradient descent iteratively updates weights to minimize cross-entropy loss. The algorithm is simple, but stable implementation requires attention to normalization, learning rate, and numerical behavior.

Java is well suited for this when you want explicit control, production integration, or educational clarity without framework abstraction. A compact implementation can handle many tabular binary classification tasks.

Core Sections

1. Model and loss formulation

For feature vector x and weights w, prediction is:

p = sigmoid(w·x + b)

Binary cross-entropy loss for one sample:

L = -(y*log(p) + (1-y)*log(1-p))

Gradient updates:

  • dw += (p - y) * x
  • db += (p - y)

Average over batch before applying learning rate.

2. Java implementation skeleton

java
1public class LogisticRegression {
2    private final double[] w;
3    private double b;
4    private final double lr;
5
6    public LogisticRegression(int nFeatures, double learningRate) {
7        this.w = new double[nFeatures];
8        this.b = 0.0;
9        this.lr = learningRate;
10    }
11
12    private double sigmoid(double z) {
13        if (z >= 0) {
14            double ez = Math.exp(-z);
15            return 1.0 / (1.0 + ez);
16        } else {
17            double ez = Math.exp(z);
18            return ez / (1.0 + ez);
19        }
20    }
21
22    public double predictProb(double[] x) {
23        double z = b;
24        for (int i = 0; i < w.length; i++) z += w[i] * x[i];
25        return sigmoid(z);
26    }
27}

The split sigmoid prevents overflow for large magnitude z.

3. Gradient descent training loop

java
1public void fit(double[][] X, int[] y, int epochs) {
2    int n = X.length;
3    int d = w.length;
4
5    for (int epoch = 0; epoch < epochs; epoch++) {
6        double[] gradW = new double[d];
7        double gradB = 0.0;
8        double loss = 0.0;
9
10        for (int i = 0; i < n; i++) {
11            double p = predictProb(X[i]);
12            double err = p - y[i];
13            for (int j = 0; j < d; j++) gradW[j] += err * X[i][j];
14            gradB += err;
15            loss += -(y[i] * Math.log(p + 1e-12) + (1 - y[i]) * Math.log(1 - p + 1e-12));
16        }
17
18        for (int j = 0; j < d; j++) w[j] -= lr * gradW[j] / n;
19        b -= lr * gradB / n;
20
21        if (epoch % 100 == 0) {
22            System.out.println("epoch=" + epoch + " loss=" + loss / n);
23        }
24    }
25}

4. Feature scaling and regularization

Convergence improves significantly with standardized features. Add L2 regularization if overfitting appears:

gradW[j] += lambda * w[j]

This keeps weights bounded and improves generalization.

5. Evaluate with classification metrics

Use thresholded predictions and compute accuracy, precision, recall, and AUC where possible. Accuracy alone can hide poor minority-class performance.

Common Pitfalls

  • Skipping feature scaling and blaming gradient descent when convergence is slow.
  • Using learning rates too high, causing oscillating or diverging loss.
  • Ignoring numerical stability in sigmoid and log computations.
  • Evaluating only accuracy on imbalanced datasets.
  • Forgetting regularization and overfitting to training data.

Summary

Logistic regression with gradient descent in Java is straightforward when you implement stable math, proper training loops, and sensible preprocessing. Build from a numerically safe sigmoid, accumulate gradients correctly, and monitor loss during training. Add feature scaling and regularization as needed, then evaluate with balanced metrics. This gives a dependable baseline classifier and a strong foundation for more advanced optimization methods.

To make this guidance robust in day-to-day engineering work, treat it as an executable checklist instead of one-time reading material. Capture the expected environment, dependency versions, runtime flags, and validation commands in your repository so every contributor can reproduce the same behavior from a clean setup. This is especially important when onboarding new developers, rotating on-call ownership, or debugging incidents under time pressure. Documentation that includes concrete commands, expected outputs, and failure interpretation prevents repeat confusion and shortens recovery time.

It is also worth adding at least one automated guardrail in CI that validates the highest-risk assumption described in the article. Depending on the topic, that guardrail may be a smoke test, policy check, schema validation, benchmark threshold, import check, or integration assertion against a minimal fixture. The goal is to fail fast when environment drift or configuration changes reintroduce old errors. Teams that convert troubleshooting knowledge into small, repeatable checks reduce operational noise and keep this class of issue from returning every sprint.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the 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.