Sentence Similarity
MLX
Safetensors
ministral3
apple-silicon
embeddings
text-embeddings
feature-extraction
retrieval
semantic-search
rag
mteb
nemotron
quantized
4-bit precision
information-retrieval
dense-retrieval
vector-search
matryoshka
arxiv:2502.13595
Eval Results (legacy)
Instructions to use shadowrock-io/Nemotron-3-Embed-8B-Community-MLX-4bit with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use shadowrock-io/Nemotron-3-Embed-8B-Community-MLX-4bit with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir Nemotron-3-Embed-8B-Community-MLX-4bit shadowrock-io/Nemotron-3-Embed-8B-Community-MLX-4bit
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
| """P3b — convert nvidia/Nemotron-3-Embed-8B-BF16 to a quantized MLX checkpoint. | |
| The NVIDIA HF checkpoint's weight names (embed_tokens/layers.N/norm, no "model." | |
| prefix) and config keys match the proven bidirectional MLX implementation bundled | |
| with the mlx-community 1B port exactly (see runs/mlx_contract_1b_bf16.json: the | |
| implementation is 0.9999-exact vs Transformers). Conversion is therefore: | |
| mmap-load shards -> load into NemotronEmbedModel -> nn.quantize -> save. | |
| One recipe per process invocation (24GB unified memory: 16GB bf16 mmap + the | |
| quantized materialization must not coexist across recipes). | |
| Usage: | |
| source ~/.venvs/nq-mlx/bin/activate | |
| python mlx/convert_8b.py --bits 4 --group-size 32 \ | |
| --src ~/.cache/nq-models/nv-8b-bf16 --out ~/.cache/nq-models/mlx-8b-4bit-gs32 | |
| python mlx/convert_8b.py --bits 4 --group-size 32 --mixed \ | |
| --out ~/.cache/nq-models/mlx-8b-mixed-gs32 # embeddings kept 8-bit | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import importlib.util | |
| import json | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| MLX_1B_DIR = Path.home() / ".cache/nq-models/mlx-1b-4bit" # source of the impl | |
| def load_impl(): | |
| spec = importlib.util.spec_from_file_location( | |
| "nemotron3_embed_mlx", MLX_1B_DIR / "nemotron3_embed_mlx.py" | |
| ) | |
| mod = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(mod) | |
| return mod | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--src", default=str(Path.home() / ".cache/nq-models/nv-8b-bf16")) | |
| ap.add_argument("--out", required=True) | |
| ap.add_argument("--bits", type=int, required=True) | |
| ap.add_argument("--group-size", type=int, required=True) | |
| ap.add_argument( | |
| "--mixed", | |
| action="store_true", | |
| help="keep embed_tokens at 8-bit (body at --bits)", | |
| ) | |
| args = ap.parse_args() | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| from mlx_lm.models.ministral3 import ModelArgs | |
| src, out = Path(args.src).expanduser(), Path(args.out).expanduser() | |
| out.mkdir(parents=True, exist_ok=True) | |
| mod = load_impl() | |
| cfg = json.loads((src / "config.json").read_text()) | |
| model = mod.NemotronEmbedModel(ModelArgs.from_dict(cfg)) | |
| weights = {} | |
| for shard in sorted(src.glob("model-*.safetensors")): | |
| weights.update(mx.load(str(shard))) # mmap — not materialized yet | |
| print( | |
| f"loaded {len(weights)} tensors from {len(list(src.glob('model-*.safetensors')))} shards" | |
| ) | |
| model.load_weights(list(weights.items())) | |
| if args.mixed: | |
| def class_predicate(path, module): | |
| if not hasattr(module, "to_quantized"): | |
| return False | |
| if "embed_tokens" in path: | |
| return {"group_size": args.group_size, "bits": 8} | |
| return True | |
| nn.quantize( | |
| model, | |
| group_size=args.group_size, | |
| bits=args.bits, | |
| class_predicate=class_predicate, | |
| ) | |
| quant_cfg = { | |
| "group_size": args.group_size, | |
| "bits": args.bits, | |
| "mode": "affine", | |
| "overrides": {"embed_tokens": {"bits": 8}}, | |
| } | |
| else: | |
| nn.quantize(model, group_size=args.group_size, bits=args.bits) | |
| quant_cfg = {"group_size": args.group_size, "bits": args.bits, "mode": "affine"} | |
| mx.eval(model.parameters()) | |
| from mlx.utils import tree_flatten | |
| flat = dict(tree_flatten(model.parameters())) | |
| mx.save_safetensors( | |
| str(out / "model.safetensors"), flat, metadata={"format": "mlx"} | |
| ) | |
| cfg_out = dict(cfg) | |
| cfg_out["quantization"] = quant_cfg | |
| cfg_out["quantization_config"] = quant_cfg | |
| (out / "config.json").write_text(json.dumps(cfg_out, indent=2)) | |
| for f in ("tokenizer.json", "tokenizer_config.json"): | |
| shutil.copy(src / f, out / f) | |
| shutil.copy(MLX_1B_DIR / "nemotron3_embed_mlx.py", out / "nemotron3_embed_mlx.py") | |
| size_gb = (out / "model.safetensors").stat().st_size / 1e9 | |
| print( | |
| json.dumps( | |
| { | |
| "out": str(out), | |
| "quantization": quant_cfg, | |
| "n_tensors": len(flat), | |
| "size_gb": round(size_gb, 2), | |
| } | |
| ) | |
| ) | |
| if __name__ == "__main__": | |
| main() | |