{"record":{"id":"d4d4013635ef6b2d","repo":"d2l-ai/d2l-zh","slug":"len-centers-len-contexts-len-negatives","errorCode":null,"errorMessage":"len(centers) == len(contexts) == len(negatives)","messagePattern":"len\\(centers\\) == len\\(contexts\\) == len\\(negatives\\)","errorType":"validation","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"d2l/torch.py","lineNumber":2149,"sourceCode":"        contexts_negatives), d2l.tensor(masks), d2l.tensor(labels))\n\ndef load_data_ptb(batch_size, max_window_size, num_noise_words):\n    \"\"\"下载PTB数据集，然后将其加载到内存中\n\n    Defined in :numref:`subsec_word2vec-minibatch-loading`\"\"\"\n    num_workers = d2l.get_dataloader_workers()\n    sentences = read_ptb()\n    vocab = d2l.Vocab(sentences, min_freq=10)\n    subsampled, counter = subsample(sentences, vocab)\n    corpus = [vocab[line] for line in subsampled]\n    all_centers, all_contexts = get_centers_and_contexts(\n        corpus, max_window_size)\n    all_negatives = get_negatives(\n        all_contexts, vocab, counter, num_noise_words)\n\n    class PTBDataset(torch.utils.data.Dataset):\n        def __init__(self, centers, contexts, negatives):\n            assert len(centers) == len(contexts) == len(negatives)\n            self.centers = centers\n            self.contexts = contexts\n            self.negatives = negatives\n\n        def __getitem__(self, index):\n            return (self.centers[index], self.contexts[index],\n                    self.negatives[index])\n\n        def __len__(self):\n            return len(self.centers)\n\n    dataset = PTBDataset(all_centers, all_contexts, all_negatives)\n\n    data_iter = torch.utils.data.DataLoader(\n        dataset, batch_size, shuffle=True,\n        collate_fn=batchify, num_workers=num_workers)\n    return data_iter, vocab\n","sourceCodeStart":2131,"sourceCodeEnd":2167,"githubUrl":"https://github.com/d2l-ai/d2l-zh/blob/e6b18ccea71451a55fcd861d7b96fddf2587b09a/d2l/torch.py#L2131-L2167","documentation":"An AssertionError raised in the PTBDataset constructor for the word2vec (skip-gram with negative sampling) data pipeline. It verifies that the centers, contexts, and negatives lists produced by get_centers_and_contexts and get_negatives are element-aligned: every center word index i must have a matching contexts[i] and negatives[i]. Because negatives are generated per-context by sampling from the corpus counter with a population equal to the number of context words, any mismatch means the data-generation steps were not run against the same corpus/vocab or were manually edited out of sync.","triggerScenarios":"Calling load_data_ptb (the function containing PTBDataset) after the upstream arrays got out of sync: passing all_contexts from one corpus run but all_negatives from another; custom re-implementations of get_negatives that return a flat list of K negatives instead of one list per center; calling PTBDataset(all_centers, all_contexts, all_negatives) directly with hand-built lists of different lengths.","commonSituations":"Users copying the sec_word2vec-training notebook code into their own scripts and mutating all_contexts (e.g. filtering rare tokens) without regenerating negatives; changing max_window_size or num_noise_words between runs while reusing cached arrays; a subclass of PTBDataset that slices one list but not the others.","solutions":["Regenerate all three arrays in one pass: centers, contexts = get_centers_and_contexts(corpus, max_window_size) then negatives = get_negatives(contexts, vocab, counter, num_noise_words) so they stay aligned.","If building the dataset manually, verify lengths before construction: assert len(all_centers) == len(all_contexts) == len(all_negatives) in your own code with a descriptive message.","If you modified get_centers_and_contexts (e.g. custom window logic), audit get_negatives: it must append num_noise_words samples once per context token, keeping the per-center nesting.","Check that you did not flatten negatives: each element of all_negatives must itself be a list whose total count equals len(all_contexts[i]) * num_noise_words."],"exampleFix":"// before (manual construction, lists out of sync)\ndataset = PTBDataset(all_centers, all_contexts, all_negatives)  # AssertionError\n\n// after (regenerate together, then construct)\nall_centers, all_contexts = get_centers_and_contexts(corpus, max_window_size)\nall_negatives = get_negatives(all_contexts, vocab, counter, num_noise_words)\nassert len(all_centers) == len(all_contexts) == len(all_negatives)\ndataset = PTBDataset(all_centers, all_contexts, all_negatives)","handlingStrategy":"validation","validationCode":"centers, contexts = get_centers_and_contexts(corpus, max_window_size)\nnegatives = get_negatives(contexts, vocab, counter, num_noise_words)\nif not (len(centers) == len(contexts) == len(negatives)):\n    raise ValueError(\n        f'misaligned arrays: centers={len(centers)} '\n        f'contexts={len(contexts)} negatives={len(negatives)}')\ndataset = PTBDataset(centers, contexts, negatives)","typeGuard":"def is_aligned_ptb(centers, contexts, negatives) -> bool:\n    return (isinstance(centers, list) and isinstance(contexts, list)\n            and isinstance(negatives, list)\n            and len(centers) == len(contexts) == len(negatives))","tryCatchPattern":null,"preventionTips":["Always generate centers/contexts/negatives in one code path from the same corpus and vocab; never mix arrays across runs.","When customizing get_negatives, keep one nested list per center (len(negatives[i]) == num_noise_words * len(contexts[i])).","Add your own length check with a descriptive ValueError before constructing PTBDataset so the failure names the mismatched lengths."],"tags":["d2l","word2vec","nlp","dataset","assertion","pytorch"],"backgroundTag":null,"analyzedSha":"e6b18ccea71451a55fcd861d7b96fddf2587b09a","analyzedAt":"2026-08-14T20:05:26.414Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}