d2l-ai/d2l-zh · error · AssertionError

len(centers) == len(contexts) == len(negatives)

Error message

len(centers) == len(contexts) == len(negatives)

What it means

This is an assertion inside PTBDataset.__init__ (d2l/paddle.py:2158) enforcing that the centers, contexts and negatives lists passed to the word2vec-negative-sampling dataset all have the same length. The dataset is indexed positionally (__getitem__ returns the i-th element of each list), so unequal lengths would silently truncate or IndexError during training. The library throws at construction time to fail fast instead of mid-epoch.

Source

Thrown at d2l/paddle.py:2158

        contexts_negatives), d2l.tensor(masks), d2l.tensor(labels))

def load_data_ptb(batch_size, max_window_size, num_noise_words):
    """下载PTB数据集,然后将其加载到内存中

    Defined in :numref:`subsec_word2vec-minibatch-loading`"""
    num_workers = d2l.get_dataloader_workers()
    sentences = read_ptb()
    vocab = d2l.Vocab(sentences, min_freq=10)
    subsampled, counter = subsample(sentences, vocab)
    corpus = [vocab[line] for line in subsampled]
    all_centers, all_contexts = get_centers_and_contexts(
        corpus, max_window_size)
    all_negatives = get_negatives(
        all_contexts, vocab, counter, num_noise_words)

    class PTBDataset(paddle.io.Dataset):
        def __init__(self, centers, contexts, negatives):
            assert len(centers) == len(contexts) == len(negatives)
            self.centers = centers
            self.contexts = contexts
            self.negatives = negatives

        def __getitem__(self, index):
            return (self.centers[index], self.contexts[index],
                    self.negatives[index])

        def __len__(self):
            return len(self.centers)

    dataset = PTBDataset(all_centers, all_contexts, all_negatives)

    data_iter = paddle.io.DataLoader(
        dataset, batch_size=batch_size, shuffle=True, return_list=True,
        collate_fn=batchify, num_workers=num_workers)
    return data_iter, vocab

View on GitHub (pinned to e6b18ccea7)

Solutions

  1. Check the three lengths right after they are produced (print(len(all_centers), len(all_contexts), len(all_negatives))) to find which list diverges and fix its producer (get_centers_and_contexts must append to centers and contexts in lockstep; get_negatives must yield exactly len(all_contexts) lists).
  2. If you truncated one list for a quick experiment, apply the same slice to all three: PTBDataset(all_centers[:N], all_contexts[:N], all_negatives[:N]).
  3. If you wrote a custom negative sampler, make it a generator that runs exactly len(all_contexts) iterations and never breaks out early or swallows exceptions.
  4. If a stale notebook state is suspected, restart the kernel / re-run the cells from read_ptb() down so all lists are rebuilt consistently.
  5. If you only need the dataset, use the canonical d2l.load_data_ptb(...) helper from the installed d2l package instead of the hand-inlined class, so the pipeline that produces the three lists stays matched.

Example fix

# before
centers, contexts = get_centers_and_contexts(corpus, max_window_size)  # custom version, lengths drift
negatives = get_negatives(contexts, vocab, counter, num_noise_words)
dataset = PTBDataset(centers, contexts, negatives)  # AssertionError

# after
assert len(centers) == len(contexts), 'centers/contexts must be built in lockstep'
negatives = get_negatives(contexts, vocab, counter, num_noise_words)  # yields exactly len(contexts) items
assert len(negatives) == len(contexts)
dataset = PTBDataset(centers, contexts, negatives)
Defensive patterns

Strategy: validation

Validate before calling

all_centers, all_contexts = get_centers_and_contexts(corpus, max_window_size)
all_negatives = get_negatives(all_contexts, vocab, counter, num_noise_words)
assert len(all_centers) == len(all_contexts) == len(all_negatives), (
    f'length mismatch: centers={len(all_centers)} contexts={len(all_contexts)} '
    f'negatives={len(all_negatives)}')
dataset = PTBDataset(all_centers, all_contexts, all_negatives)

Type guard

def is_aligned_ptb_data(centers, contexts, negatives) -> bool:
    return (isinstance(centers, list) and isinstance(contexts, list)
            and isinstance(negatives, list)
            and len(centers) == len(contexts) == len(negatives))

# usage
assert is_aligned_ptb_data(all_centers, all_contexts, all_negatives), 'rebuild all three lists together'

Try / catch

try:
    dataset = PTBDataset(all_centers, all_contexts, all_negatives)
except AssertionError:
    raise RuntimeError(
        f'centers/contexts/negatives out of sync: '
        f'{len(all_centers)}/{len(all_contexts)}/{len(all_negatives)}; '
        f're-run get_centers_and_contexts and get_negatives from the same corpus') from None

Prevention

When it happens

Trigger: Calling PTBDataset(all_centers, all_contexts, all_negatives) (or the enclosing d2l.load_data_ptb helper) after replacing or modifying get_centers_and_contexts / get_negatives so the returned lists diverge: e.g., a custom get_centers_and_contexts that appends centers without matching contexts, a get_negatives that returns fewer noise-word lists than len(all_contexts) (its while-loop samples K negatives per context, so any early break/exception swallow truncates it), or slicing one list (all_centers[:N]) but not the others.

Common situations: Users editing the book's code to experiment with custom corpora, window sizes, or subsampling; interrupting (Ctrl-C) get_negatives partway and reusing a partially built list; passing subsampled data of a different shape; notebook re-runs where a stale variable of a different length is picked up; upgrading d2l versions where the data-loading helpers changed signature but user code kept the old shape.

Related errors


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