Mean pooling
# grade this stage (from your course directory)
npx theinference test --stage mean-pooling --watchA sentence is a variable number of token vectors, but an embedding is one fixed vector. Mean pooling bridges the gap: average the token rows over the real (non-padding) positions. Sentence-BERT found mean pooling beats both the [CLS] token and max pooling for sentence similarity, and it's still the default in sentence-transformers.
Why the mask matters
rows = [[1, 1],
[3, 3],
[5, 5]] mask = [1, 1, 0] ← position 2 is padding
pooled = ([1,1] + [3,3]) / 2 = [2, 2] ← divide by 2, not 3
What to build
Implement mean pooling
Implement runMeanPool(rows, mask): sum the rows where mask[i] === 1, then divide by the number of non-pad positions (not by the sequence length). Return the pooled [32] vector.
export function runMeanPool(rows: number[][], mask: number[]): number[] {
// sum rows where mask[i] === 1, divide by the count of those positions
}
Adapter mode: the pooled vector is compared to the reference with allclose, atol 1e-6. The fixture includes padded positions, so the mask handling is exercised.
Dividing by T (the padded sequence length) instead of by the count of non-pad tokens. With padding present, that silently shrinks short sentences toward zero and quietly fails the atol 1e-6 check.