{"record":{"id":"4f79464c62503e1f","repo":"d2l-ai/d2l-zh","slug":"x-shape-0-y-shape-0-4f7946","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/torch.py","lineNumber":1480,"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 (nn.parallel.scatter(X, devices),\n            nn.parallel.scatter(y, devices))\n\ndef resnet18(num_classes, in_channels=1):\n    \"\"\"稍加修改的ResNet-18模型\n\n    Defined in :numref:`sec_multi_gpu_concise`\"\"\"\n    def resnet_block(in_channels, out_channels, num_residuals,\n                     first_block=False):\n        blk = []\n        for i in range(num_residuals):\n            if i == 0 and not first_block:\n                blk.append(d2l.Residual(in_channels, out_channels,\n                                        use_1x1conv=True, strides=2))\n            else:\n                blk.append(d2l.Residual(out_channels, out_channels))\n        return nn.Sequential(*blk)\n","sourceCodeStart":1462,"sourceCodeEnd":1498,"githubUrl":"https://github.com/d2l-ai/d2l-zh/blob/e6b18ccea71451a55fcd861d7b96fddf2587b09a/d2l/torch.py#L1462-L1498","documentation":"AssertionError from d2l.torch.split_batch (sec_multi_gpu): X and y must share the same leading batch dimension before nn.parallel.scatter distributes them across devices. The assert is a pre-flight check so mismatched data fails loudly instead of producing silent device-allocation errors inside scatter.","triggerScenarios":"split_batch(X[:64], y, devices) after trimming only X; y coming from a different batch than X (double next(iter(...)) calls); labels reshaped to (n,1) while X stays (n, c, h, w) is fine, but y flattened to a scalar or None breaks it; mixing numpy arrays (no .shape ordering guarantee after slicing mistakes) with tensors.","commonSituations":"Custom multi-GPU training loops modeled on the book; asynchronous prefetch where X is fetched one step ahead of y; last-batch handling that drops unmatched rows from only one tensor; debugging sessions that slice batches manually.","solutions":["Slice both tensors identically: split_batch(X[:n], y[:n], devices)","Always obtain X, y together from one DataLoader batch rather than separate iterators","Validate shapes at the source: if X.shape[0] != y.shape[0], raise a contextual error including shapes and step index","Check intermediate reshapes keep the batch axis aligned (y.view(-1) vs y.view(1, -1))"],"exampleFix":"# before\nX = X[:32]  # manual trim of features only\nsplit_batch(X, y, devices)  # y.shape[0]=256 -> AssertionError\n# after\nX, y = X[:32], y[:32]\nsplit_batch(X, y, devices)  # both 32 rows, scatter ok","handlingStrategy":"validation","validationCode":"assert X.shape[0] == y.shape[0], f'misaligned batch: X={tuple(X.shape)} y={tuple(y.shape)}'\nXs, ys = split_batch(X, y, devices)","typeGuard":"def batch_aligned(X, y) -> bool:\n    return X.shape[0] == y.shape[0]","tryCatchPattern":null,"preventionTips":["Pull X and y from the same DataLoader batch","Slice features and labels with the same index range","Guard with a contextual assert before split_batch in custom multi-GPU loops","Double-check reshape targets keep the batch axis consistent"],"tags":["d2l","pytorch","assertion","multi-gpu","data-shape","batch"],"backgroundTag":null,"analyzedSha":"e6b18ccea71451a55fcd861d7b96fddf2587b09a","analyzedAt":"2026-08-14T20:05:26.414Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}