Class EmbeddingPooler
- Namespace
- Qavren.Edge.Embeddings.Onnx
- Assembly
- Qavren.Edge.Embeddings.Onnx.dll
Allocation-free pooling over a session output span. Public because it is worth testing alone.
public static class EmbeddingPooler
- Inheritance
-
EmbeddingPooler
- Inherited Members
Remarks
The order is fixed and it matters: pool, then LayerNorm(Span<float>, float) when the preset asks for it, then L2Normalize(Span<float>). Layer-norming a unit vector is not the same operation as unit-normalising a layer-normed one, and only the second matches nomic-embed-text-v1.5's reference pipeline.
Methods
- ClsPool(ReadOnlySpan<float>, int, int, Span<float>)
Copies row zero, the
[CLS]position. bge-small-en-v1.5 pools this way.
- L2Normalize(Span<float>)
Divides by the Euclidean norm, in place. A zero vector is left alone: the all-padding MeanPool(ReadOnlySpan<float>, ReadOnlySpan<long>, int, int, Span<float>) guard produces one, and dividing by a zero norm here would turn a defined zero vector into NaNs one call later.
- LayerNorm(Span<float>, float)
Mean/variance normalisation over the pooled vector, matching PyTorch
F.layer_norm(x, (dim,))with no learned weight or bias: subtract the mean, divide bysqrt(variance + epsilon). Required by nomic-embed-text-v1.5 and by nothing else here; see PostPoolLayerNorm.
- MeanPool(ReadOnlySpan<float>, ReadOnlySpan<long>, int, int, Span<float>)
The masked mean:
sum(h[t] * mask[t]) / max(1, sum(mask)). The denominator'smax(1, ...)is what keeps an all-padding row a defined zero vector rather thandimensionsNaNs.