Downloads · 30 days
92
17% of all-time downloads
AmarettoLabs/amaretto-embed-148m
amaretto-embed-148m is a sentence similarity model from AmarettoLabs. Use it when you need a score for how close two texts are. It is set up for sentence-transformers. The card lists the license as gemma.
A 148M-parameter EmbeddingGemma, specialised for 8 Latin-script languages + code — a general-purpose drop-in at half the size and ≈40% of the RAM.
Downloads · 30 days
92
17% of all-time downloads
All-time downloads
529
Public
Parameters
148M
1.2 GB on disk
Likes
1
Public
Click a slice to open those files.
.safetensors611 MB · 99%
From the Hugging Face model README
A 148M-parameter EmbeddingGemma, specialised for 8 Latin-script languages + code — a general-purpose drop-in at half the size and ≈40% of the RAM.
amaretto-embed-148m is google/embeddinggemma-300m with its vocabulary sliced from 262,144 → 60,497 tokens, and the resulting damage healed by distillation against the original model.
The insight: EmbeddingGemma spends two-thirds of its parameters (201M of 303M) on a token-embedding table sized for 100+ languages. If you only need a few languages, most of that table is dead weight. Cutting it down to the tokens that 8 Latin-script languages and code actually use halves the model — while leaving the transformer layers, where the understanding happens, completely untouched.
Result (real MTEB, controlled A/B vs EmbeddingGemma): ≥99.3% on retrieval and STS across all 8 languages, ≈98% on classification — the latter at the theoretical limit of a sliced vocabulary.
| EmbeddingGemma | amaretto-embed-148m | |
|---|---|---|
| Parameters (encoder) | 302,863,104 | 147,998,208 (49%) |
| Parameters (full pipeline, incl. Dense head) | 307,581,696 | 152,716,800 |
| Vocabulary | 262,144 | 60,497 |
| Embedding table | 201.3M | 46.5M |
| Transformer backbone | 101.5M | 101.5M (identical) |
| Embedding dim | 768 (Matryoshka) | 768 (Matryoshka) |
| Peak RAM, Jetson Orin Nano (f16 GGUF) | ≈1846 MB | ≈729 MB (2.5× less) |
(Deployment footprint is architecture-determined; the RAM figure is for the identical-architecture f16 GGUF.)
If you run google/embeddinggemma-300m and only need these 8 languages, swap this in and halve your memory. It was distilled against EmbeddingGemma itself with absolute-vector MSE, which preserves the original embedding space (not merely the ranking), so its vectors mean what EmbeddingGemma's mean — the same task prefixes, 768 dims, Matryoshka sizes, and SentenceTransformer API.
Text embeddings — retrieval, semantic search, RAG, clustering, classification, STS — for English, Spanish, Portuguese, French, German, Italian, Dutch, Polish, and source code, especially where memory is tight (edge / Jetson / cheap CPU).
Re-index when you switch. This is a drop-in behaviourally, not bit-compatibly — at ≈0.98–0.99 cosine to the original the vectors are close but not identical, so don't query an existing EmbeddingGemma-built index with this model. Re-encode your corpus.
The vocabulary for every non-target language was deleted. Text in Chinese, Japanese, Korean, Arabic, Hebrew, Cyrillic, Greek, Indic scripts, Thai, etc. will not error — it falls back to byte-level tokens — but sequences become drastically longer and embedding quality degrades severely. Do not use this model outside the 8 listed languages. For broad multilingual coverage, use the original google/embeddinggemma-300m.
Requires the same task prefixes as EmbeddingGemma — the model was trained with them.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("AmarettoLabs/amaretto-embed-148m")
query = model.encode("task: search result | query: how do I sort a list in python?")
docs = model.encode([
"title: none | text: sorted() returns a new sorted list from an iterable.",
"title: none | text: De kat zat op de mat.",
])
from sentence_transformers.util import cos_sim
print(cos_sim(query, docs))
Trained to preserve EmbeddingGemma's Matryoshka property at 768 / 512 / 256 / 128. Truncate and re-normalise:
import torch.nn.functional as F
emb_256 = F.normalize(embeddings[:, :256], p=2, dim=1)
Truncation is free at encode time — it only reduces storage and search cost. 256 is a strong default.
Companion repos for running the model without PyTorch. Both are verified against this model's own embeddings, not against each other:
| repo | for | sizes | fidelity vs this model |
|---|---|---|---|
| GGUF | llama.cpp, edge / Jetson | 113–293 MB | f16 exact; Q8_0 ≈0.999; Q6_K ≈0.996 |
| ONNX | onnxruntime, CPU serving | 297–612 MB | fp32 exact; int8 ≈0.999 |
The ONNX builds are end-to-end graphs — pooling, both Dense heads, and normalization are inside the
graph, so onnxruntime + tokenizers is the whole runtime dependency. That also means they are not the
SentenceTransformer(..., backend="onnx") layout; for that workflow use this repo with PyTorch.
Releases are tagged (e.g. v1.0.0). Pin a revision for reproducible embeddings — if you build a vector index with this model, load the same tag so future updates can't shift your vectors:
model = SentenceTransformer("AmarettoLabs/amaretto-embed-148m", revision="v1.0.0")
Loading without a revision tracks main, which may move. The GGUF and ONNX companion repos are versioned in lockstep.
1. Vocabulary slicing (surgery, no training). Token frequencies were counted over Wikipedia (8 languages) + code. The keep-set is the top ≈60k by a balanced per-language mix, plus force-keeps: all special/control tokens, all 256 byte-fallback pieces, every single-char Latin-1 + Polish (ą ć ę ł ń ś ź ż) + æ piece, and the task-prompt tokens. Surviving embedding rows are index-selected, so kept tokens keep their exact original weights.
2. Distillation recovery. Slicing changes tokenization at the seams, which shifts the pooled embedding. The sliced model was trained to regress onto EmbeddingGemma's exact embedding vectors with an un-normalized Matryoshka MSE loss — which preserves the absolute embedding structure that linear-probe tasks (classification) depend on, not just the direction that retrieval needs.
Measured on MTEB (v2), as a controlled A/B: both this model and EmbeddingGemma were run through the same MTEB version and are reported as % of EmbeddingGemma (never against externally-published numbers, which would confound library/prompt differences).
| lang | EmbeddingGemma | amaretto | % |
|---|---|---|---|
| de | 0.9446 | 0.9420 | 99.7% |
| en | 0.9652 | 0.9596 | 99.4% |
| es | 0.9511 | 0.9526 | 100.2% |
| fr | 0.9541 | 0.9524 | 99.8% |
| it | 0.9480 | 0.9464 | 99.8% |
| nl | 0.9402 | 0.9409 | 100.1% |
| pl | 0.9226 | 0.9229 | 100.0% |
| pt | 0.9516 | 0.9483 | 99.7% |
| MEAN | 0.9472 | 0.9456 | 99.8% |
| lang | EmbeddingGemma | amaretto | % |
|---|---|---|---|
| de | 0.8482 | 0.8422 | 99.3% |
| en | 0.8799 | 0.8741 | 99.3% |
| es | 0.8569 | 0.8493 | 99.1% |
| fr | 0.8488 | 0.8445 | 99.5% |
| it | 0.8373 | 0.8334 | 99.5% |
| nl | 0.8314 | 0.8254 | 99.3% |
| pl | 0.8021 | 0.7954 | 99.2% |
| pt | 0.8385 | 0.8336 | 99.4% |
| MEAN | 0.8429 | 0.8372 | 99.3% |
| lang | EmbeddingGemma | amaretto | % |
|---|---|---|---|
| de | 0.7898 | 0.7668 | 97.1% |
| en | 0.8613 | 0.8527 | 99.0% |
| es | 0.7947 | 0.7762 | 97.7% |
| fr | 0.8163 | 0.7946 | 97.3% |
| it | 0.7685 | 0.7492 | 97.5% |
| nl | 0.7766 | 0.7643 | 98.4% |
| pl | 0.7493 | 0.7325 | 97.8% |
| pt | 0.7951 | 0.7828 | 98.4% |
| MEAN | 0.7940 | 0.7774 | 97.9% |
On classification's ≈98%: this is the ceiling, not a shortfall. The vocabulary slicing itself — deleting ≈200k embedding rows — costs classification quality that no amount of training can recover: the untrained sliced model already floors at ≈97.7% of EmbeddingGemma on this task. amaretto's 97.9% is at that limit. Retrieval and STS depend on the relative geometry of the embedding space, which slicing barely perturbs (hence ≥99.3%); classification's linear probe depends on the absolute feature structure, which the reduced vocabulary coarsens. This is the physics of the compression, and the un-normalized-MSE objective was chosen specifically to get classification as close to that ceiling as possible.
Natural-language docstring → code-function retrieval, across 6 programming languages (same controlled A/B):
| language | EmbeddingGemma | amaretto | % |
|---|---|---|---|
| python | 0.9564 | 0.9519 | 99.5% |
| go | 0.9265 | 0.9246 | 99.8% |
| php | 0.8800 | 0.8767 | 99.6% |
| ruby | 0.8638 | 0.8604 | 99.6% |
| javascript | 0.8233 | 0.8012 | 97.3% |
| java | 0.8970 | 0.8634 | 96.3% |
| MEAN | 0.8912 | 0.8797 | 98.7% |
98.7% of EmbeddingGemma on code retrieval. Note Ruby holds 99.6% even though it was not in amaretto's training code mix (python/java/javascript/php/go) — evidence the retained backbone generalizes beyond the trained languages. Java is the softest (96.3%).
The model was trained with ≈21% long documents (256–512 tokens) to support long-form retrieval, and accepts the full 2048-token context. Measured on MLDR (genuine multi-thousand-token documents) for English and Spanish, same controlled A/B (both models, mteb 2.18.3, 2048-token context), reporting the test split:
| lang | EmbeddingGemma | amaretto | % |
|---|---|---|---|
| en | 0.3674 | 0.3439 | 93.6% |
| es | 0.5970 | 0.5862 | 98.2% |
| MEAN | 0.4822 | 0.4651 | 96.4% |
Long-form retrieval is amaretto's softest axis — ≈96% of EmbeddingGemma on the test split, versus ≥99.3% on short retrieval and STS. Spanish holds up strongly (≈98%); English long-doc is the weak spot (≈94%). This is expected: long-form fidelity is the hardest thing to preserve through vocabulary slicing and distillation. The low absolute nDCG reflects MLDR's intrinsic difficulty (it's the same for the teacher) — the meaningful figure is the ratio to EmbeddingGemma. If long English documents are your primary workload, benchmark on your own data before switching.
The full pipeline — vocabulary slicing, distillation training, the MTEB evaluation harness, per-model SHA-256 checksums, and the raw result JSONs behind every number above — is open at github.com/AmarettoLabs/amaretto-embed. You can re-derive the comparison tables from the committed results, or verify the model files you download here against the published checksums.
Gemma is provided under and subject to the Gemma Terms of Use found at ai.google.dev/gemma/terms
amaretto-embed-148m is a Gemma Model Derivative of google/embeddinggemma-300m. Your use, reproduction, and distribution of it are governed by the Gemma Terms of Use, a copy of which is distributed with this model in the LICENSE file. By using this model you accept those terms.
You may not use amaretto-embed-148m, or any model derivative of it, in violation of the Gemma Prohibited Use Policy, which is incorporated into the Gemma Terms of Use by reference and forms an enforceable condition of this model's license.
The Gemma Terms (§3.1) require you to:
LICENSE file.NOTICE.You may add your own terms for your own modifications, provided they do not conflict with the Gemma Terms.
Full detail in NOTICE:
| file | modification |
|---|---|
model.safetensors | embed_tokens.weight reduced from 262,144 to 60,498 rows by index-selection (60,497 kept vocabulary pieces plus one reserved row); all weights then fine-tuned by distillation against the unmodified original. Layer topology unchanged. |
tokenizer.model / tokenizer.json | vocabulary reduced 262,144 → 60,497 pieces; survivors renumbered; merges/added-tokens remapped. |
tokenizer_config.json, config.json | regenerated for the reduced vocabulary (vocab_size 262144 → 60498). |
2_Dense/, 3_Dense/ | weights fine-tuned by the distillation above. |
This model is not affiliated with, endorsed by, or sponsored by Google. No rights to Google trademarks are granted. References to "Gemma" and "EmbeddingGemma" are descriptive, identifying the upstream model this work derives from.
Built on EmbeddingGemma by Google DeepMind.
@misc{amaretto_embed_148m,
author = {{AmarettoLabs}},
title = {amaretto-embed-148m: a vocabulary-sliced, distillation-recovered EmbeddingGemma for 8 languages and code},
year = {2026},
url = {https://huggingface.co/AmarettoLabs/amaretto-embed-148m},
note = {Model derivative of google/embeddinggemma-300m},
}