Masked mean pooling turns many token embeddings into one embedding that represents an entire sentence or document. For example:
- classifying an entire document
- creating one embedding for semantic search
- finding the cosine similarity between two sentences or documents
A model often produces one vector per token, . Pause: what do these dimensions represent?

Mean pooling converts this into a tensor of shape , and padding must not affect the result.
Let be our mask, which tells us which positions are real and which are padding.
The numerator
- equals 0 if the position is padding, 1 if it's a real token.
- is a vector of length — the token embedding.
The denominator
- just avoids dividing by 0.
- gives the number of valid tokens, so dividing gives us the "mean".
In code:
def masked_mean_pool(x: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
"""Mean-pool valid tokens from x [B, T, D] using mask [B, T]."""
# x : [B, T, D]
# mask : [B, T]
B, T, D = x.shape
mask_unsqueezed = mask.unsqueeze(-1) # [B, T] -> [B, T, 1]
masked_x = x * mask_unsqueezed # [B, T, D] * [B, T, 1] -> [B, T, D]
numerator = masked_x.sum(dim=1) # [B, T, D] -> [B, D]
mask_sum = mask.sum(dim=1).clamp(min=1) # [B, T] -> [B]
denominator = mask_sum.unsqueeze(-1) # [B] -> [B, 1]
return numerator / denominator # [B, D] / [B, 1] -> [B, D]