{"record":{"id":"8538064b4e0f8f60","repo":"d2l-ai/d2l-zh","slug":"train-loss-0-5","errorCode":null,"errorMessage":"train_loss < 0.5","messagePattern":"train_loss < 0\\.5","errorType":"validation","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"d2l/mxnet.py","lineNumber":314,"sourceCode":"        self.axes[0].cla()\n        for x, y, fmt in zip(self.X, self.Y, self.fmts):\n            self.axes[0].plot(x, y, fmt)\n        self.config_axes()\n        display.display(self.fig)\n        display.clear_output(wait=True)\n\ndef train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):\n    \"\"\"训练模型（定义见第3章）\n\n    Defined in :numref:`sec_softmax_scratch`\"\"\"\n    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],\n                        legend=['train loss', 'train acc', 'test acc'])\n    for epoch in range(num_epochs):\n        train_metrics = train_epoch_ch3(net, train_iter, loss, updater)\n        test_acc = evaluate_accuracy(net, test_iter)\n        animator.add(epoch + 1, train_metrics + (test_acc,))\n    train_loss, train_acc = train_metrics\n    assert train_loss < 0.5, train_loss\n    assert train_acc <= 1 and train_acc > 0.7, train_acc\n    assert test_acc <= 1 and test_acc > 0.7, test_acc\n\ndef predict_ch3(net, test_iter, n=6):\n    \"\"\"预测标签（定义见第3章）\n\n    Defined in :numref:`sec_softmax_scratch`\"\"\"\n    for X, y in test_iter:\n        break\n    trues = d2l.get_fashion_mnist_labels(y)\n    preds = d2l.get_fashion_mnist_labels(d2l.argmax(net(X), axis=1))\n    titles = [true +'\\n' + pred for true, pred in zip(trues, preds)]\n    d2l.show_images(\n        d2l.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n])\n\ndef evaluate_loss(net, data_iter, loss):\n    \"\"\"评估给定数据集上模型的损失\n","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/d2l-ai/d2l-zh/blob/e6b18ccea71451a55fcd861d7b96fddf2587b09a/d2l/mxnet.py#L296-L332","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check the learning rate: use lr=0.1 (or d2l.sgd with the correct params) as in the book, and rerun","Train for the full num_epochs=10 the section specifies instead of a shortened run","Verify net, loss, updater come from the same framework (all mxnet) and the net's output dim equals the number of classes (10)","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"],"exampleFix":"// before\nupdater = lambda params: d2l.sgd(params, lr=10.0, batch_size=256)\ntrain_ch3(net, train_iter, test_iter, loss, 10, updater)  # AssertionError: train_loss\n// after\nbatch_size = 256\ntrainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1})\nupdater = lambda batch_size: d2l.sgd(net.collect_params().values(), lr=0.1, batch_size=batch_size)\ntrain_ch3(net, train_iter, test_iter, loss, 10, updater)  # converges, loss < 0.5","handlingStrategy":"validation","validationCode":"# before calling train_ch3, verify hyperparameters and one-batch loss decrease\nlr, num_epochs = 0.1, 10\nassert num_epochs >= 10, 'book setting needs >=10 epochs'\nassert 0.01 <= lr <= 0.5, f'suspicious lr={lr}'\nassert len(updater_params) > 0, 'updater has no parameters to optimize'\n# one-batch smoke: loss should backprop without error\nfor X, y in train_iter:\n    l = loss(net(X), y)\n    break","typeGuard":"def is_converged_config(num_epochs: int, lr: float, n_classes: int = 10) -> bool:\n    return num_epochs >= 10 and 0.01 <= lr <= 0.5 and n_classes == 10","tryCatchPattern":"try:\n    train_ch3(net, train_iter, test_iter, loss, 10, updater)\nexcept AssertionError as e:\n    print(f'training did not converge (train_loss={e.args[0]}); check lr/updater/epochs')\n    raise","preventionTips":["Pin the book's hyperparameters (lr=0.1, epochs=10, batch=256) before trusting the asserts","Run a one-batch forward/backward smoke test before the full loop","Ensure net, loss, updater all come from the mxnet d2l module","Never shorten num_epochs in CI runs that include train_ch3's built-in asserts"],"tags":["d2l","mxnet","assertion","training","convergence","fashion-mnist"],"backgroundTag":null,"analyzedSha":"e6b18ccea71451a55fcd861d7b96fddf2587b09a","analyzedAt":"2026-08-14T20:05:26.414Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}