d2l-ai/d2l-zh · error · AssertionError

train_loss < 0.5

Error message

train_loss < 0.5

What it means

This is a Python AssertionError raised by d2l.mxnet.train_ch3 after the training loop finishes. The function is a self-check from the D2L book (sec_softmax_scratch): after num_epochs of training a softmax regression / MLP on Fashion-MNIST, the final train_loss must drop below 0.5. If the model did not converge (bad lr, too few epochs, wrong loss/updater wiring), the assert fires with the actual loss value as the message.

Source

Thrown at d2l/mxnet.py:314

        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
    """训练模型(定义见第3章)

    Defined in :numref:`sec_softmax_scratch`"""
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train loss', 'train acc', 'test acc'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
        test_acc = evaluate_accuracy(net, test_iter)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_loss, train_acc = train_metrics
    assert train_loss < 0.5, train_loss
    assert train_acc <= 1 and train_acc > 0.7, train_acc
    assert test_acc <= 1 and test_acc > 0.7, test_acc

def predict_ch3(net, test_iter, n=6):
    """预测标签(定义见第3章)

    Defined in :numref:`sec_softmax_scratch`"""
    for X, y in test_iter:
        break
    trues = d2l.get_fashion_mnist_labels(y)
    preds = d2l.get_fashion_mnist_labels(d2l.argmax(net(X), axis=1))
    titles = [true +'\n' + pred for true, pred in zip(trues, preds)]
    d2l.show_images(
        d2l.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n])

def evaluate_loss(net, data_iter, loss):
    """评估给定数据集上模型的损失

View on GitHub (pinned to e6b18ccea7)

Solutions

  1. Check the learning rate: use lr=0.1 (or d2l.sgd with the correct params) as in the book, and rerun
  2. Train for the full num_epochs=10 the section specifies instead of a shortened run
  3. Verify net, loss, updater come from the same framework (all mxnet) and the net's output dim equals the number of classes (10)
  4. If convergence is genuinely slow, lower lr or switch to Trainer-based d2l.train_ch3 counterparts, then only re-enable the assert once metrics stabilize

Example fix

// before
updater = lambda params: d2l.sgd(params, lr=10.0, batch_size=256)
train_ch3(net, train_iter, test_iter, loss, 10, updater)  # AssertionError: train_loss
// after
batch_size = 256
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1})
updater = lambda batch_size: d2l.sgd(net.collect_params().values(), lr=0.1, batch_size=batch_size)
train_ch3(net, train_iter, test_iter, loss, 10, updater)  # converges, loss < 0.5
Defensive patterns

Strategy: validation

Validate before calling

# before calling train_ch3, verify hyperparameters and one-batch loss decrease
lr, num_epochs = 0.1, 10
assert num_epochs >= 10, 'book setting needs >=10 epochs'
assert 0.01 <= lr <= 0.5, f'suspicious lr={lr}'
assert len(updater_params) > 0, 'updater has no parameters to optimize'
# one-batch smoke: loss should backprop without error
for X, y in train_iter:
    l = loss(net(X), y)
    break

Type guard

def is_converged_config(num_epochs: int, lr: float, n_classes: int = 10) -> bool:
    return num_epochs >= 10 and 0.01 <= lr <= 0.5 and n_classes == 10

Try / catch

try:
    train_ch3(net, train_iter, test_iter, loss, 10, updater)
except AssertionError as e:
    print(f'training did not converge (train_loss={e.args[0]}); check lr/updater/epochs')
    raise

Prevention

When it happens

Trigger: Calling train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) with an SGD updater whose learning rate is too large/small (e.g. 10.0 or 0.001 when 0.1 is expected), num_epochs < 10, a net whose output dimension does not match 10 Fashion-MNIST classes, or an updater from a different framework flavor (torch sgd passed to the mxnet train_ch3).

Common situations: Running the D2L chapter 3 notebooks in a fresh environment with mxnet not GPU-accelerated so the user cuts num_epochs to 1-3; typos in the lr schedule (d2l.sgd with wrong params list); using batch_size so large the loss plateaus; mixing d2l.torch and d2l.mxnet helpers in one session.

Related errors


AI-assisted analysis of d2l-ai/d2l-zh@e6b18ccea7 (2026-08-14). Data as JSON: /api/errors/8538064b4e0f8f60. Report an issue: GitHub.