Skip to content

model: add JMullings/spectral-embed-v2-140m - #5716

Open
jmullings wants to merge 14 commits into
embeddings-benchmark:mainfrom
jmullings:add-spectral-embed-v2
Open

jmullings wants to merge 14 commits into
embeddings-benchmark:mainfrom
jmullings:add-spectral-embed-v2

Conversation

@jmullings

Copy link
Copy Markdown
Contributor

Adds JMullings/spectral-embed-v2-140m (supersedes v1, which is already in MTEB).

Model: CPU-only English embedding model. Hashed n-gram featurisation (BLAKE2b, Porter stemming + stopwords, BM25-style term weighting, bucket IDF) followed by a learned linear projection to 2048 dimensions. Model card: https://huggingface.co/JMullings/spectral-embed-v2-140m

Changes

  • Add SpectralEmbedV2Model / SpectralFeaturizer and the spectral_embed_v2_140m ModelMeta
  • Set superseded_by on v1
  • Requires nltk for stemming

Loader verification: the loader's output matches the original artifact's served embeddings at ~0.994 mean cosine (n=200). A single serving-only weights file (~537 MB) is used.

Training data: TODO (corpus, teacher model, and whether any labelled/evaluation data was used).

Results: to be submitted in embeddings-benchmark/results#789, regenerated against this implementation and revision 6943d55e.

Draft until the training-data section is complete and the results are regenerated.



spectral_embed_v2_140m = ModelMeta(
loader=SpectralEmbedV2Model,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you merge v2 version with v1?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call @Samoed. I've refactored spectral_models.py to merge SpectralEmbedV2Model into SpectralEmbedModel. Both v1 and v2 now use the same unified loader class and share identical encode, batching, and similarity methods, while dispatching to the appropriate featurizer based on the model name. Pushed in the latest commit.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this make v1 results non-reproducible?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this just adds head to swap between v1 and v2

Comment thread mteb/models/model_implementations/spectral_models.py Outdated
Comment thread mteb/models/model_implementations/spectral_models.py Outdated
Comment thread mteb/models/model_implementations/spectral_models.py Outdated

@KennethEnevoldsen KennethEnevoldsen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my biggest concern it reproducibility of v1.

Comment on lines +504 to +507
# ============================================================================
# MODEL METADATA DEFINITIONS
# ============================================================================

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Comment on lines +413 to +415
# ============================================================================
# UNIFIED MODEL LOADER (Serves both V1 and V2)
# ============================================================================

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

self._pre = f"{seed}:"

try:
from nltk.stem import PorterStemmer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is stemming really worth it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My experience with it is that is consistently make the model generalize worse, but mostly worked in non-english languages, but would still be surprised if you couldn't do better than stemming.

@jmullings

Copy link
Copy Markdown
Contributor Author

Thanks both. On v1 reproducibility (@KennethEnevoldsen):

  • The v1 featurization code (normalisation, feature keys, hashing, weights) is unchanged from main. v1 now goes through the shared _embed/encode path, and the v1 ModelMeta differs from main only by superseded_by. Name and revision (39169ba7aa8d1de29da32d97a6129bca790e9193) are unchanged.
  • I checked this against the real v1 weights at that revision: embeddings from the unified SpectralEmbedModel are bit-identical to those from main's v1 featurizer (max abs diff 0.0). So existing v1 results should not change.
  • One behavioural change: similarity() on two single 1-D vectors now returns a 0-d array instead of a (1, 1) matrix. The values are equal, and 2-D inputs are unchanged. This is for the summarization-style call path.

On stemming: the v2 weights were trained on the Porter-stemmed, stopword-filtered featurization (spectral_v6), so stemming is part of the model rather than an inference option. Removing it would need retraining.

I also removed the banner comments. Re-requesting review from you and @Samoed.

@jmullings

Copy link
Copy Markdown
Contributor Author

@Samoed FYI:
On macOS, scikit-learn 1.6.1 produces 0.05358 (passes locally).
On Linux, scikit-learn 1.6.1 produces 0.05445 (fails in CI).

@Samoed

Samoed commented Oct 20, 2026

Copy link
Copy Markdown
Member

Yes, I know about that issue, but linux test on lower dependecy is flaky

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants