Model reference · open weights
USER is an open-weight embedding model from deepvk. USER-base (FP32) weighs 248 MB; the smallest configuration that runs it is RTX 3060 12 GB.
For self-hosting, compare this build's requirements below with the GPU you plan to rent.
Summary of the deepvk/USER-base model card, 2026-10-01
What it is
| Released by | deepvk |
|---|---|
| Released | 2024-06-10 |
| Parameters | 124M |
| VRAM | 248 MB for the weights |
What it runs on
| Card | Runs | Memory |
|---|---|---|
| RTX 3060 12 GB | fits | 11.6 GB |
| RTX 4060 Ti 16 GB | fits | 15.4 GB |
| RTX 3090 24 GB | fits | 23.4 GB |
| RTX 4090 24 GB | fits | 23.4 GB |
| RTX 5090 32 GB | fits | 31.0 GB |
| L40S 48 GB | fits | 44.0 GB |
| A100 80 GB | fits | 78.2 GB |
| H100 80 GB | fits | 78.1 GB |
| RTX PRO 6000 Blackwell 96 GB | fits | 93.8 GB |
| DGX Spark (GB10) 128 GB unified | fits | 107 GB |
| H200 141 GB | fits | 138 GB |
| B200 180 GB | fits | 176 GB |
From the model card
Universal Sentence Encoder for Russian (USER) is a sentence-transformer model for extracting embeddings exclusively for Russian language. It maps sentences & paragraphs to a 768 dimensional dense vector space and can be used for tasks like clustering or semantic search.
This model is initialized from deepvk/deberta-v1-base and trained to work exclusively with the Russian language. Its quality on other languages was not evaluated.
Using this model becomes easy when you have sentence-transformers installed:
pip install -U sentence-transformers
Then you can use the model like this:
from sentence_transformers import SentenceTransformer
queries = [
"Когда был спущен на воду первый миноносец «Спокойный»?",
"Есть ли нефть в Удмуртии?"
]
passages = [
"Спокойный (эсминец)\nЗачислен в списки ВМФ СССР 19 августа 1952 года.",
"Нефтепоисковые работы в Удмуртии были начаты сразу после Второй мировой войны в 1945 году и продолжаются по сей день. Добыча нефти началась в 1967 году."
]
model = SentenceTransformer("deepvk/USER-base")
# Prompt should be specified according to the task (either 'query' or 'passage').
passage_embeddings = model.encode(passages, normalize_embeddings=True, prompt_name='passage')
# For tasks other than retrieval, you can simply use the `query` prompt, which is set by default.
query_embeddings = model.encode(queries, normalize_embeddings=True)
However, you can use model directly with transformers
import torch.nn.functional as F
from torch import Tensor, inference_mode
from transformers import AutoTokenizer, AutoModel
def average_pool(
last_hidden_states: Tensor,
attention_mask: Tensor
) -> Tensor:
last_hidden = last_hidden_states.masked_fill(
~attention_mask[..., None].bool(), 0.0
)
return last_hidden.sum(dim=1) / attention_mask.sum(dim=1)[..., None]
# You should manually add prompts when using the model directly. Each input text should start with "query: " or "passage: ".
# For tasks other than retrieval, you can simply use the "query: " prefix.
input_texts = [
"query: Когда был спущен на воду первый миноносец «Спокойный»?",
"query: Есть ли нефть в Удмуртии?",
"passage: Спокойный (эсминец)\nЗачислен в списки ВМФ СССР 19 августа 1952 года.",
"passage: Нефтепоисковые работы в Удмуртии были начаты сразу после Второй мировой войны в 1945 году и продолжаются по сей день. Добыча нефти началась в 1967 году."
]
tokenizer = AutoTokenizer.from_pretrained("deepvk/USER-base")
model = AutoModel.from_pretrained("deepvk/USER-base")
batch_dict = tokenizer(
input_texts, padding=True, truncation=True, return_tensors="pt"
)
with inference_mode():
outputs = model(**batch_dict)
embeddings = average_pool(
outputs.last_hidden_state, batch_dict["attention_mask"]
)
embeddings = F.normalize(embeddings, p=2, dim=1)
# Scores for query-passage
scores = (embeddings[:2] @ embeddings[2:].T) * 100
# [[55.86, 30.95],
# [22.82, 59.46]]
print(scores.round(decimals=2))
⚠️ Attention ⚠️
Each input text should start with "query: " or "passage: ". For tasks other than retrieval, you can simply use the "query: " prefix.
We aimed to follow the bge-base-en model training algorithm, but we made several improvements along the way.
Initialization: deepvk/deberta-v1-base
First-stage: Contrastive pre-training with weak supervision on the Russian part of mMarco corpus.
Second-stage: Supervised fine-tuning two different models based on data symmetry and then merging via LM-Cocktail:
We modified the instruction design by simplifying the multilingual approach to facilitate easier inference.
For symmetric data (S1, S2), we used the instructions: "query: S1" and "query: S2", and for asymmetric data, we used "query: S1" with "passage: S2".
Since we split the data, we could additionally apply the AnglE loss to the symmetric model, which enhances performance on symmetric tasks.
Finally, we combined the two models, tuning the weights for the merger using LM-Cocktail to produce the final model, USER.
During model development, we additional collect 2 datasets:
deepvk/ru-HNP and
deepvk/ru-WANLI.
| Symmetric Dataset | Size | Asymmetric Dataset | Size |
|---|---|---|---|
| AllNLI | 282 644 | MIRACL | 10 000 |
| MedNLI | 3 699 | MLDR | 1 864 |
| RCB | 392 | Lenta | 185 972 |
| Terra | 1 359 | Mlsum | 51 112 |
| Tapaco | 91 240 | Mr-TyDi | 536 600 |
| Opus100 | 1 000 000 | Panorama | 11 024 |
| BiblePar | 62 195 | PravoIsrael | 26 364 |
| [RudetoxifierDataDetox] |
Quoted from the model card on Hugging Face. The full card is behind the Hugging Face link above.