eriklindernoren/ML-From-Scratch · error · NotImplementedError

NotImplementedError()

Error message

NotImplementedError()

What it means

Loss.gradient is an abstract method on the Loss base class in mlfromscratch/deep_learning/loss_functions.py and raises NotImplementedError. During training, NeuralNetwork.backpropagate calls loss.gradient(y, y_pred) to get dL/dy_pred; if the loss object is the base Loss (or a custom subclass that didn't implement gradient), this stub raises. Note loss() on the base class returns NotImplementedError instead of raising — a related latent bug — but gradient() is the hard failure.

Source

Thrown at mlfromscratch/deep_learning/loss_functions.py:11

from __future__ import division
import numpy as np
from mlfromscratch.utils import accuracy_score
from mlfromscratch.deep_learning.activation_functions import Sigmoid

class Loss(object):
    def loss(self, y_true, y_pred):
        return NotImplementedError()

    def gradient(self, y, y_pred):
        raise NotImplementedError()

    def acc(self, y, y_pred):
        return 0

class SquareLoss(Loss):
    def __init__(self): pass

    def loss(self, y, y_pred):
        return 0.5 * np.power((y - y_pred), 2)

    def gradient(self, y, y_pred):
        return -(y - y_pred)

class CrossEntropy(Loss):
    def __init__(self): pass

    def loss(self, y, p):
        # Avoid division by zero

View on GitHub (pinned to a2806c6732)

Solutions

  1. Implement gradient(self, y, y_pred) in your Loss subclass returning the derivative of the loss w.r.t. y_pred (e.g. 0.5 * (y_pred - y) for SquareLoss in this library)
  2. Use a built-in concrete loss (SquareLoss, CrossEntropy, etc.) instead of the base Loss class
  3. Match the method exactly: def gradient(self, y, y_pred): — two arguments besides self
  4. If prototyping, subclass an existing loss like SquareLoss and override only what changes

Example fix

// before
class MyLoss(Loss):
    def loss(self, y, y_pred):
        return np.mean((y - y_pred) ** 2)
    # gradient missing -> NotImplementedError on first fit() backward pass

// after
class MyLoss(Loss):
    def loss(self, y, y_pred):
        return np.mean((y - y_pred) ** 2)

    def gradient(self, y, y_pred):
        return 2 * (y_pred - y) / y.shape[0]  # dL/dy_pred

    def acc(self, y, y_pred):
        return 0
Defensive patterns

Strategy: validation

Validate before calling

from mlfromscratch.deep_learning.loss_functions import Loss

fn = getattr(type(model.loss_function), 'gradient', None)
assert fn is not None and fn is not Loss.gradient, (
    'Loss object does not implement gradient(y, y_pred); '
    'use a built-in loss or implement gradient in your subclass')

Type guard

from mlfromscratch.deep_learning.loss_functions import Loss

def is_complete_loss(obj) -> bool:
    if not isinstance(obj, Loss):
        return False
    for m in ('loss', 'gradient', 'acc'):
        fn = getattr(type(obj), m, None)
        if fn is None or fn is getattr(Loss, m, None):
            return False
    return True

Try / catch

try:
    model.fit(X_train, y_train, n_epochs=10)
except NotImplementedError as e:
    raise TypeError(
        f'Loss {type(model.loss_function).__name__} is incomplete; '
        'implement gradient(y, y_pred) or use SquareLoss/CrossEntropy') from e

Prevention

When it happens

Trigger: Instantiating mlfromscratch.deep_learning.loss_functions.Loss and passing it as NeuralNetwork(loss=...), then calling fit; or defining a custom Loss subclass that implements loss/acc but not gradient(y, y_pred). The error occurs on the first backward pass of model.fit.

Common situations: Writing a custom loss (e.g. weighted cross-entropy) and forgetting the gradient method; assuming the library computes numerical gradients from loss() (it doesn't — gradients are analytic and must be supplied); version upgrades where the gradient method signature changed.

Related errors


AI-assisted analysis of eriklindernoren/ML-From-Scratch@a2806c6732 (2026-08-27). Data as JSON: /api/errors/ccfee711ebbc8487. Report an issue: GitHub.