-
Notifications
You must be signed in to change notification settings - Fork 0
Embeddings 0.4.0 pooling
Lodestar.Embeddings 0.4.0. This page is frozen at that release. Read the current documentation for what
mainsays now. A link to a decision or a migration page followsmain, and leaves the archive.
A transformer returns one vector per token. A sentence embedding is one vector. Pooling is the step between, and getting it wrong is the quiet way to make every downstream similarity slightly untrue.
Lodestar.Embeddings.Pooling holds one static class, Pooler, implementing
the recipe sentence-transformers uses: masked mean, then L2 normalization.
| You have | You want | Call |
|---|---|---|
| one sequence | a sentence embedding | MeanPoolAndNormalize |
| a padded batch | one embedding per sequence | MeanPoolAndNormalizeBatch |
| one sequence | the mean only, unnormalized | MeanPool |
| a padded batch | the means only | MeanPoolBatch |
| a vector already pooled | it scaled to unit length | L2Normalize |
The two AndNormalize forms are the ones to reach for. The others exist because a caller who
pools now and normalizes later, or who pools something this package did not produce, should not
have to reimplement half the recipe.
A batch is padded to its longest sequence, and the padding positions carry embeddings — the model computed something for them. Averaging those in would drag every short sequence's vector toward whatever the padding token happens to mean.
The mask is what excludes them, and every call here takes one. The mean divides by the number
of real tokens, not by seqLen: sum(embeddings × mask) / max(sum(mask), 1e-9), which is
sentence-transformers' own formula down to the clamp that keeps an all-padding sequence from
dividing by zero.
Cosine similarity is a dot product only on unit vectors, which is what
EmbeddingIndex relies on. Normalizing at pooling time
means every consumer downstream gets that for free.
This namespace makes the opposite trade from VectorMath. In
L2Normalize the sum of squares is accumulated in double and
deliberately not vectorized: a Vector<float> accumulator would lose precision and make the
result depend on the SIMD width of the machine that ran it. The scaling pass, which is exact
whichever way it is done, is vectorized.
The result is a pooled vector that is bit-identical between net10.0 and netstandard2.0, and
between machines of different vector widths.
| Type | What it is |
|---|---|
Pooler |
The five pooling calls. |
- Embeddings, end to end — where pooling sits in the chain.
- Python → C# equivalence — the pooling rows.
- 0001-target-framework
- 0002-unicode-comparison-unit
- 0003-provenance-and-licensing
- 0004-levenshtein-myers-backlog
- 0005-hamming-jellyfish-divergence
- 0006-ratcliff-autojunk
- 0007-metaphone-scope
- 0008-italian-enza-nltk-divergence
- 0009-sample-consumes-a-local-feed
- 0010-stop-word-list-provenance
- 0011-persistence-format
- 0012-per-package-versioning
- 0013-sentencepiece-parity-scope
- 0014-precompiled-normalizer
- 0015-sonar-rules-in-the-build
- 0016-metrics-package-placement
- 0017-bpe-parity-scope
- 0018-multiclass-roc-auc-parallelism-is-opt-in
- 0019-the-net-analysers-run-in-the-build-too
- 0020-normalize-is-a-projection-not-a-parameter
- 0021-multioutput-is-a-method-not-an-enum
- 0022-added-token-matching-flags
- 0023-byte-level-decode-substitutes
- 0024-weighted-median-averages-within-scikit-learns-epsilon
- 0025-quickselect-replaces-a-full-sort-for-the-median
- 0026-r2-and-explainedvariance-split-their-undefined-cases-differently
- 0027-r2-and-explainedvariance-vectorize-only-a-single-output
- 0028-log1p-is-kahans-identity-not-math-log-1-plus-x
- 0029-balanced-accuracy-adjusted-is-left-to-ieee-754-at-the-edge
- 0030-cohen-kappa-keeps-scikit-learns-expected-matrix-orientation
- 0031-nosamplecorrect-mirrors-numpys-float64-upcast
- 0032-fbeta-substitutes-tp-predicted-and-support-algebraically
- 0033-compensated-sum-is-neumaiers-variant
- 0034-dropout-is-refused-for-want-of-a-user
- 0035-a-null-pre-split-is-removed-with-invert-not-isolated
- 0036-a-member-may-ship-without-an-oracle-if-it-says-so
- 0037-the-guards-run-before-the-commit
- 0038-the-gate-confronts-an-exception-tag-with-the-page-that-documents-it
- 0039-mutual-information-returns-zero-on-an-empty-input
- 0040-a-curve-is-a-sealed-class-per-curve
- 0041-one-sample-file-per-public-class
- 0042-phonetic-encoders-refuse-a-null-word
- 0043-the-equality-table-is-sized-to-the-pattern
- 0044-compression-belongs-to-the-caller
- 0045-a-console-call-carries-its-reason-on-the-line
- 0046-check-adr-immutable-runs-in-ci-only
- 0047-one-gate-per-kernel-not-one-per-alphabet
- 0048-the-gate-depends-on-the-kernel-and-the-alphabet
- 0049-two-gates-per-kernel-tested-where-the-width-is-known
- 0050-the-sentencepiece-bpe-lineage-stays-a-bpe-model
- benchmark_latest
- decisions
- equivalence
- matplotlib
- migration
- nightly_run
- numpy
- pandas
- performance
- pytorch
- seaborn
- sklearn
- statsmodels