Модель текстовых эмбеддингов на основе архитектуры GigaChat3-10B-A1.8B, адаптированная под двунаправленное (encoder-style) внимание и обученная с контрастивной функцией потерь (InfoNCE). Модель строит плотные эмбеддинги предложений/абзацев для задач поиска (retrieval), семантического сравнения, классификации и кластеризации, показывая высокое качество на русском и английском языках.
Архитектура: DeepSeek-V3 (26 слоёв, скрытая размерность 1536, 32 головы внимания, Multi-head Latent Attention с параметрами kv_lora_rank=512, qk_nope_head_dim=128, qk_rope_head_dim=64, v_head_dim=192; MoE с 64 маршрутизируемыми экспертами, 4 активных на токен, 1 общий (shared) эксперт), механизм self-attention сделан двунаправленным.
Рецепт получения эмбеддингов:усреднение (mean pooling) по токенам без учёта padding-токенов, за которым следует L2-нормализация. Сравнивайте эмбеддинги с помощью косинусного сходства (скалярное произведение нормализованных векторов).
Всего параметров: ~10 млрд (≈1,8 млрд активных на токен). Веса в формате bfloat16.
Пулинг и нормализация
Модель обучалась с mean pooling + L2-нормализацией. Иёспользование CLS/last-token пулинга даст неверные результаты. Если вы используете transformers напрямую, необходимо самостоятельно усреднить (mean-pool) по не-паддинговым токенам и затем применить L2-нормализацию (см. пример ниже). В примерах для sentence-transformers и vLLM это делается автоматически. Сравнивайте эмбеддинги через косинусную близость (скалярное произведение нормированных векторов).
Инструктивность
Модель обучалась в инструктивном стиле: для retrieval и других асимметричных задач необходимо добавлять инструкцию к запросу (query), а документы кодируются как есть, без инструкции. Формат:
Instruct: {описание задачи}
Query: {ваш текст}
Для симметричных задач (STS, дедупликация) можно использовать общую инструкцию либо не использовать её вовсе. Инструкцию выбирают под конкретную задачу — единственного «правильного» промпта не существует. Важно отметить, что инструкцию нужно добавлять только перед запросом, а не перед документом.
FAQ
Нужно ли добавлять инструкции к запросу?
Для асимметричных задач (retrieval) — да, добавьте к запросу инструкцию из одного предложения, описывающую задачу. Документы кодируются как есть, без инструкции. Для симметричных задач (STS, дедупликация) можно использовать общую инструкцию либо не использовать её вовсе.
Какой пулинг использовать?
Mean pooling (усреднение) по не-паддинговым токенам с последующей L2-нормализацией. Использование CLS/last-token пулинга даст неверные результаты.
Почему мои воспроизведённые результаты немного отличаются от указанных в карточке модели?
Разные версии библиотек transformers и pytorch могут вызывать незначительные, но ненулевые различия в результатах.
GigaChat 10B Bidirectional Embedding Model
A text embedding model based on the DeepSeek-V3 (MLA + MoE) architecture, adapted
for bidirectional (encoder-style) attention and trained with a contrastive
(InfoNCE) objective. It produces dense sentence/passage embeddings for retrieval,
semantic similarity, classification and clustering, with strong Russian and English
performance.
Architecture: DeepSeek-V3 (26 layers, hidden 1536, 32 attention heads,
Multi-head Latent Attention with kv_lora_rank=512, qk_nope_head_dim=128,
qk_rope_head_dim=64, v_head_dim=192; MoE with 64 routed experts,
4 active per token, 1 shared expert), self-attention made bidirectional.
Embedding recipe:mean pooling over non-padding tokens, followed by
L2 normalization. Compare embeddings with cosine similarity (dot product of
normalized vectors).
Total parameters: ~10B (≈1.8B active per token). Weights are bfloat16.
Pooling & normalization (important)
This model was trained with mean pooling + L2 normalization. Using CLS/last-token pooling will give wrong results. If you use transformers directly, you must mean-pool over non-padding tokens yourself and then L2-normalize (see the example below). The sentence-transformers and vLLM examples do this for you. Compare embeddings with cosine similarity (dot product of normalized vectors).
Instructions / prompts
The model was trained in the instruction style: for retrieval and other asymmetric tasks, prepend a task instruction to the query (documents are embedded raw). The format is:
Instruct: {task description}
Query: {your text}
For symmetric tasks (STS, deduplication) you can either use a generic instruction or none at all. Choose the instruction per task; there is no single "correct" prompt.
FAQ
Do I need to add instructions to the query?
For asymmetric tasks (retrieval), yes — prepend a one-sentence task instruction to the query. Documents are embedded raw, without an instruction. For symmetric tasks (STS, deduplication) you can use a generic instruction or none at all.
Which pooling should I use?
Mean pooling over non-padding tokens, followed by L2 normalization. Using CLS/last-token pooling will give wrong results.
Why are my reproduced results slightly different from those reported?
Different versions of the transformers and pytorch libraries can cause small but non-zero differences in results.
Metrics*
Benchmark
old 3b
Giga-Embeddings-instruct-3B-0826
Giga-Embeddings-instruct-10B-A1.8B-0826
MTEB (rus)
74.16
74.57
74.99
MTEB (eng)
71.07
71.93
72.23
MTEB (code)
62.37
76.93
78.40
MTEB (multilingual)
55.51
63.9
65.60
Model / backend
512 tok
1024 tok
2048 tok
throughput vs 10B-A1.8B
Giga-Embeddings-instruct-3B-0826 / vLLM
87.9k/s
91.5k/s
90.4k/s
0.8x
Giga-Embeddings-instruct-10B-A1.8B-0826 / vLLM
112.6k/s
114.5k/s
102.3k/s
1.0x
Nemotron 8B / vLLM
42.6k/s
43.2k/s
41.7k/s
0.38x
Qwen3 Embedding 4B / vLLM
70.1k/s
73.2k/s
71.2k/s
0.64x
F2LLM-v2-8B / vLLM
43.2k/s
43.4k/s
42.6k/s
0.38x
NV-Embed-v2 / Transformers
25.6k/s
26.2k/s
25.6k/s
0.23x
* All metrics were measured on an H100 GPU with a batch size of 16.
Usage
Sentence Transformers
python
1from sentence_transformers import SentenceTransformer
23model = SentenceTransformer(4"ai-sage/Giga-Embeddings-instruct-10B-A1.8B-0826",5 trust_remote_code=True,# needed for the bidirectional modeling code6)78instruction ="Given a query, retrieve relevant passages"9queries =[f"Instruct: {instruction}\nQuery: Где столица России?"]10documents =["Москва — столица Российской Федерации.",11"Париж — столица Франции."]1213q_emb = model.encode(queries, normalize_embeddings=True)14d_emb = model.encode(documents, normalize_embeddings=True)15print(model.similarity(q_emb, d_emb))
Transformers (manual mean pooling)
python
1import torch
2import torch.nn.functional as F
3from transformers import AutoModel, AutoTokenizer
45path ="ai-sage/Giga-Embeddings-instruct-10B-A1.8B-0826"6tok = AutoTokenizer.from_pretrained(path, trust_remote_code=True)7model = AutoModel.from_pretrained(path, trust_remote_code=True,8 dtype=torch.bfloat16).cuda().eval()910defencode(texts):11 enc = tok(texts, return_tensors="pt", padding=True, truncation=True, max_length=512)12 enc ={k: v.cuda()for k, v in enc.items()}13with torch.no_grad():14 hidden = model(**enc).last_hidden_state
15 mask = enc["attention_mask"].unsqueeze(-1).to(hidden.dtype)16 emb =(hidden * mask).sum(1)/ mask.sum(1).clamp(min=1e-6)# mean pool17return F.normalize(emb, dim=-1)# L2 normalize1819instr ="Given a query, retrieve relevant passages"20q = encode([f"Instruct: {instr}\nQuery: Где столица России?"])21d = encode(["Москва — столица Российской Федерации.","Париж — столица Франции."])22print((q @ d.T).cpu())
vLLM
Support for this model landed in vLLM via
PR #52948, which is merged into
main but not yet in a tagged release. Until the next vLLM release ships, you must
install vLLM from source (or from a nightly build) to serve it.
The PR is Python-only (it just registers the DeepseekV3BidirectionalModel
architecture and adds an encoder-only attention path to the existing DeepSeek-V2/V3
model code). Because no CUDA/C++ kernels changed, you don't need to compile anything —
you can reuse vLLM's precompiled kernels and only install the updated Python code. This
is vLLM's "Python-only build" and it takes a couple of minutes instead of an hour.
1. Install vLLM from source (recommended: Python-only build)
bash
1# 1. Clone the repo (main already contains the merged PR)2git clone https://github.com/vllm-project/vllm.git
3cd vllm
45# 2. Create an isolated environment6uv venv .venv --python 3.127source .venv/bin/activate
89# 3. Install from source, reusing precompiled CUDA kernels (no compilation)10VLLM_USE_PRECOMPILED=1 uv pip install --editable . --torch-backend=auto
VLLM_USE_PRECOMPILED=1 tells the build to download vLLM's prebuilt kernels for the
matching commit and install only the Python layer in editable mode. --torch-backend=auto
lets uv pick a PyTorch build that matches your GPU/driver (important on Blackwell).
Verify the install and that the new model is registered:
This is the quickest path; use the source build in §2 if you want to modify vLLM.
3. Serve the model
This is an embedding model that uses bidirectional (encoder-only) attention and
mean pooling + L2 normalization. Start the OpenAI-compatible server like this:
Remaps the repo's custom deepseek_v3_bidirec type to vLLM's built-in deepseek_v3 config, and clears auto_map so vLLM uses its own (fast) implementation instead of the HF remote-code path. The is_causal=false field in the model config still triggers the bidirectional attention path.
Mean-pools token embeddings and applies L2 normalization (use_activation: true), matching the model's reference recipe.
Note: --pooler-config uses use_activation (the old normalize key was removed);
use_activation: true is what applies L2 normalization for embedding models.
4. Query the endpoint
The server exposes the OpenAI /v1/embeddings API.
curl:
bash
1curl -s http://localhost:8000/v1/embeddings \2 -H "Content-Type: application/json"\3 -d '{
4 "model": "ai-sage/Giga-Embeddings-instruct-10B-A1.8B-0826",
5 "input": ["Instruct: Given a query, retrieve relevant passages\nQuery: What is the capital of France?",
6 "Paris is the capital and most populous city of France."]
7 }'| python3 -c "import sys,json; d=json.load(sys.stdin); print('vectors:', len(d['data']), 'dim:', len(d['data'][0]['embedding']))"
The change is pure Python (no CUDA/kernel rebuild needed).
Once the PR is merged this whole guide collapses to "use a recent SGLang
release." Until then, use one of the two methods below.
1. Official SGLang Docker + apply the PR patch (recommended)
The official image already ships SGLang as an editable install with all CUDA
kernels prebuilt. The PR is pure Python, so you just patch the 9 files in
place — nothing is compiled, and the Python source stays matched to the
image's kernels. This is the method that was verified end-to-end for this guide.
bash
1# 1. Start the verified image (its SGLang is editable at /sgl-workspace/sglang).2docker run --gpus all -it --shm-size 16g \3 -p 30000:30000 \4 -v ~/.cache/huggingface:/root/.cache/huggingface \5 --entrypoint /bin/bash \6 lmsysorg/sglang:nightly-dev-20260818-c0b6474b
78# --- everything below runs INSIDE the container ---910# 2. Download the PR diff.11curl -fL -H "Accept: application/vnd.github.v3.diff"\12 -o /tmp/pr35532.diff \13 https://api.github.com/repos/sgl-project/sglang/pulls/35532
1415# 3. Apply it onto the image's editable source tree (takes effect immediately).16cd /sgl-workspace/sglang
17git apply -v /tmp/pr35532.diff # or: patch -p1 < /tmp/pr35532.diff1819# 4. Sanity check: the new architecture must resolve to the native class.20python3 -c "from sglang.srt.models.registry import ModelRegistry; \
21c,a=ModelRegistry.resolve_model_cls('DeepseekV3BidirectionalModel'); \
22print('OK:', a, '->', c.__module__)"23# Expect: OK: DeepseekV3BidirectionalModel -> sglang.srt.models.deepseek_v2_embedding
2. Alternative: Build from source (no Docker)
Use this on a bare CUDA machine (or a plain PyTorch container). It builds the
matching kernels, so it is heavier but fully self-contained.
--is-embedding — serve as an embedding model (the arch is auto-classified as
non-generative anyway, but this is explicit and safe).
--trust-remote-code — required (custom config class in the checkpoint).
The Triton backend / disabled CUDA graph / disabled radix cache are applied
automatically — do not override --attention-backend.
Multi-GPU: add --tp-size N if you want to shard across GPUs.
The server is ready when you see: The server is fired up and ready to roll!
4. Test it
cURL
bash
1curl -s http://localhost:30000/v1/embeddings \2 -H "Content-Type: application/json"\3 -d '{
4 "model": "ai-sage/Giga-Embeddings-instruct-10B-A1.8B-0826",
5 "input": "What is the capital of France?"
6 }'| python3 -c "import sys,json; d=json.load(sys.stdin); \
7e=d['data'][0]['embedding']; print('dim:', len(e), 'first5:', e[:5])"
Fine-tune guide
Finetuning Giga-Embeddings with ms-swift
This guide shows how to contrastively finetune the Giga-Embeddings models with
ms-swift using an InfoNCE loss, for both
LoRA and full-parameter training.
All are mean-pooling SentenceTransformer models, so we load them through ms-swift's
SentenceTransformersLoader.
Requirements
bash
1# ms-swift from main — the current PyPI release does not yet contain the2# SentenceTransformer full-parameter save fix.3pip install"git+https://github.com/modelscope/ms-swift.git"45# sentence-transformers pinned to 5.3.0.6pip install"sentence-transformers==5.3.0"
torch and transformers are pulled in automatically. A CUDA GPU is required for training.
For full-parameter finetuning of the 10B MoE model (multi-GPU, DeepSpeed ZeRO-3),
also install DeepSpeed:
pip install deepspeed
1. Register the model
The Giga-Embeddings architectures aren't in ms-swift's built-in registry, so register
them once and point them at SentenceTransformersLoader. Save this as
custom_register.py:
python
1# custom_register.py2from swift.model import Model, ModelGroup, ModelMeta, register_model
3from swift.model.register import SentenceTransformersLoader
4from swift.template import TemplateType
56# Qwen3-bidirectional models (480M, 3B)7register_model(ModelMeta(8'giga_embeddings',9[ModelGroup([10 Model('ai-sage/Giga-Embeddings-instruct-480M-0826','ai-sage/Giga-Embeddings-instruct-480M-0826'),11 Model('ai-sage/Giga-Embeddings-instruct-3B-0826','ai-sage/Giga-Embeddings-instruct-3B-0826'),12])],13 SentenceTransformersLoader,14 template=TemplateType.dummy,15 architectures=['Qwen3BidirectionalModel'],16))1718# DeepSeek-V3-bidirectional MoE model (10B-A1.8B)19#20# Under DeepSpeed ZeRO-3, MoE routing makes different ranks run different expert21# submodules, which breaks ZeRO-3's per-parameter cross-rank coordination22# ("Detected a disagreement on list length between rank0 and rankN"). The fix is to23# mark the MoE block as a ZeRO-3 leaf module. We do it in a small loader subclass24# (harmless when ZeRO-3 / DeepSpeed isn't used — only needed for the 10B on ZeRO-3).25classGigaMoESentenceTransformersLoader(SentenceTransformersLoader):26defget_model(self, model_dir, config, processor, model_kwargs):27 model =super().get_model(model_dir, config, processor, model_kwargs)28try:29from deepspeed.utils import set_z3_leaf_modules
30 set_z3_leaf_modules(model,['DeepseekV3MoE'])31except Exception:32pass33return model
3435register_model(ModelMeta(36'giga_embeddings_moe',37[ModelGroup([38 Model('ai-sage/Giga-Embeddings-instruct-10B-A1.8B-0826','ai-sage/Giga-Embeddings-instruct-10B-A1.8B-0826'),39])],40 GigaMoESentenceTransformersLoader,41 template=TemplateType.dummy,42 architectures=['DeepseekV3BidirectionalModel'],43))
Pass it to any swift sft command with --custom_register_path custom_register.py.
2. Prepare your dataset
One JSON object per line. Each row is a query with one positive document and any
number of hard negatives:
json
1{"messages":[{"role":"user","content":"What is the capital of France?"}],2"positive_messages":[[{"role":"assistant","content":"Paris is the capital of France."}]],3"negative_messages":[[{"role":"assistant","content":"Berlin is the capital of Germany."}],4[{"role":"assistant","content":"The Eiffel Tower is a landmark."}]]}
messages — the query / anchor.
positive_messages — list of positive documents (≥ 1).
negative_messages — list of hard negatives (optional but recommended).
ms-swift lays each row out as anchor + positive + negatives with labels
[1, 0, 0, …], which is what the InfoNCE loss consumes.
If your retrieval task uses an instruction prefix, prepend it to the query text (the
models were trained with Instruct: <task>\nQuery: <query>).
3. Train
The examples use these InfoNCE environment variables (they are read from the
environment, not passed as CLI flags):
bash
1exportINFONCE_TEMPERATURE=0.052exportINFONCE_USE_BATCH=True # in-batch negatives (see note below)3exportINFONCE_HARD_NEGATIVES=7# hard negatives per query (match your data)
In-batch negatives (INFONCE_USE_BATCH): keep True for general retrieval data
where each query has a distinct positive. Set it to False if your dataset has a
small set of shared positive documents (e.g. many queries mapping to the same
handful of answers) — otherwise another query's positive becomes a false negative for
yours and hurts training. When False, rely on the curated hard negatives.
(Requires pip install deepspeed.) LoRA on the 10B fits on a single 80 GB GPU
without DeepSpeed — use --tuner_type lora as in §3.
Expert routing. ms-swift's embedding trainer does not add a MoE load-balancing
auxiliary loss. For long full-parameter runs, monitor expert utilization.
5. Verify a finetuned checkpoint
Reload with sentence-transformers and confirm positives score higher than negatives.
A correct full-parameter checkpoint reloads with no "missing keys" warnings:
python
1from sentence_transformers import SentenceTransformer
23m = SentenceTransformer("./output/<run>/checkpoint-<N>", trust_remote_code=True, device="cuda")4emb = m.encode(5["What is the capital of France?",6"Paris is the capital of France.",7"Berlin is in Germany."],8 convert_to_tensor=True, normalize_embeddings=True,9)10print("cos(query, positive) =",float(emb[0] @ emb[1]))# should be clearly higher11print("cos(query, negative) =",float(emb[0] @ emb[2]))
Full-parameter output is a complete SentenceTransformer checkpoint — load the
output directory directly.
LoRA output is an adapter. Load the base model and apply the adapter, or merge
first with swift export --adapters ./output/<run>/checkpoint-<N> --merge_lora true.
InfoNCE options reference
Set via environment variables:
Variable
Default
Meaning
INFONCE_TEMPERATURE
0.1
Softmax temperature (lower = sharper).
INFONCE_USE_BATCH
True
Use in-batch (and cross-GPU) negatives.
INFONCE_HARD_NEGATIVES
–
Hard negatives kept per query.
INFONCE_MASK_FAKE_NEGATIVE
False
Mask in-batch negatives scoring above the positive (guards against false negatives).
INFONCE_INCLUDE_QQ / INFONCE_INCLUDE_DD
False
Add query-query / doc-doc terms to the denominator (Qwen3-Embedding style).
Key swift sft flags:
Flag
Meaning
--task_type embedding
Enable embedding training (ST pooling + embedding trainer).
--loss_type infonce
InfoNCE contrastive loss.
--tuner_type lora / full
LoRA (default) vs full-parameter.
--use_hf true
Resolve --model from the HuggingFace Hub.
--custom_register_path
Path to custom_register.py.
--split_dataset_ratio 0.0
No automatic validation split.
Troubleshooting
AttributeError: 'NoneType' object has no attribute 'items' during save — you're
on the PyPI release of ms-swift; install from main (see Requirements).
Reloaded model gives base-model quality / "missing keys" on load after full
finetuning — your sentence-transformers is newer than 5.3; pin ==5.3.0.
Model downloads from ModelScope instead of HuggingFace (or is not found) — add
--use_hf true.