{"record":{"id":"6b49ed52bd4b5812","repo":"d2l-ai/d2l-zh","slug":"x-shape-0-y-shape-0","errorCode":null,"errorMessage":"X.shape[0] == y.shape[0]","messagePattern":"X\\.shape\\[0\\] == y\\.shape\\[0\\]","errorType":"validation","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"d2l/mxnet.py","lineNumber":1368,"sourceCode":"\nclass Benchmark:\n    \"\"\"用于测量运行时间\"\"\"\n    def __init__(self, description='Done'):\n        \"\"\"Defined in :numref:`sec_hybridize`\"\"\"\n        self.description = description\n\n    def __enter__(self):\n        self.timer = d2l.Timer()\n        return self\n\n    def __exit__(self, *args):\n        print(f'{self.description}: {self.timer.stop():.4f} sec')\n\ndef split_batch(X, y, devices):\n    \"\"\"将X和y拆分到多个设备上\n\n    Defined in :numref:`sec_multi_gpu`\"\"\"\n    assert X.shape[0] == y.shape[0]\n    return (gluon.utils.split_and_load(X, devices),\n            gluon.utils.split_and_load(y, devices))\n\ndef resnet18(num_classes):\n    \"\"\"稍加修改的ResNet-18模型\n\n    Defined in :numref:`sec_multi_gpu_concise`\"\"\"\n    def resnet_block(num_channels, num_residuals, first_block=False):\n        blk = nn.Sequential()\n        for i in range(num_residuals):\n            if i == 0 and not first_block:\n                blk.add(d2l.Residual(\n                    num_channels, use_1x1conv=True, strides=2))\n            else:\n                blk.add(d2l.Residual(num_channels))\n        return blk\n\n    net = nn.Sequential()","sourceCodeStart":1350,"sourceCodeEnd":1386,"githubUrl":"https://github.com/d2l-ai/d2l-zh/blob/e6b18ccea71451a55fcd861d7b96fddf2587b09a/d2l/mxnet.py#L1350-L1386","documentation":"AssertionError from d2l.mxnet.split_batch (sec_multi_gpu): features X and labels y must have the same batch dimension before being scattered with gluon.utils.split_and_load across GPUs. It is a cheap pre-flight check; failure means the data pairing is broken before multi-GPU training starts.","triggerScenarios":"split_batch(X[:32], y[:16], devices) via mismatched slicing; a DataLoader returning y=None or a padded y of different length; feeding X from one iterator and y from another (async prefetch bugs); passing tensors whose batch dims diverged after a transpose/reshape mistake.","commonSituations":"Hand-rolled multi-GPU training loops adapted from the book; batch trimming like X = X[:batch_size] applied to only one of X/y; mixed torch tensors and mxnet ndarrays where .shape semantics surprise the user; shuffling X and y separately so rows no longer correspond.","solutions":["Align the slices: use the same index/length for X and y (X[:n], y[:n])","Take X, y as a pair from the same DataLoader batch instead of assembling them from separate sources","Add a guard `if X.shape[0] != y.shape[0]: raise ValueError(...)` with context (shapes, batch idx) before calling split_batch for clearer diagnostics","Verify no stray reshape dropped the batch axis (e.g. reshape(y, -1) versus reshape(y, (batch, 1)))"],"exampleFix":"# before\nX, y = next(iter(train_iter))\nX = X[:64]  # trimmed features only\nsplit_batch(X, y, devices)  # y has 256 rows -> AssertionError\n# after\nX, y = X[:64], y[:64]\nsplit_batch(X, y, devices)  # both 64 rows, scatter ok","handlingStrategy":"validation","validationCode":"assert X.shape[0] == y.shape[0], f'batch mismatch: X={X.shape}, y={y.shape}'\nXs, ys = split_batch(X, y, devices)","typeGuard":"def batch_aligned(X, y) -> bool:\n    return getattr(X, 'shape', None) is not None and X.shape[0] == y.shape[0]","tryCatchPattern":null,"preventionTips":["Always take X and y from the same DataLoader batch","Apply identical slicing to features and labels","Validate shapes with a contextual message before split_batch in custom loops","Avoid separate iterators/prefetch paths for features and labels"],"tags":["d2l","mxnet","assertion","multi-gpu","data-shape","batch"],"backgroundTag":null,"analyzedSha":"e6b18ccea71451a55fcd861d7b96fddf2587b09a","analyzedAt":"2026-08-14T20:05:26.414Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}