Frony Embed Arctic V2.5 (medium)

A Korean-first text embedding model for retrieval, built on Snowflake/snowflake-arctic-embed-l-v2.0.

What it's for. Single-stage retrieval where the top-1 hit matters — RAG pipelines that feed one or two passages directly to an LLM, FAQ matching, semantic search over Korean documents. It handles Korean↔English cross-lingual retrieval and was explicitly tuned for Markdown-formatted passages, which is how most modern RAG chunks actually look.

At a glance

Base model Snowflake/snowflake-arctic-embed-l-v2.0
Architecture Bi-encoder, single-vector dense retrieval (mean pooling)
Dimensions 1024, or 512 via Matryoshka truncation (both directly trained)
Max sequence length 8192 tokens (512 recommended — see note below)
Similarity Cosine (outputs are L2-normalized)
Languages Korean, English (ko→ko, ko→en, en→ko)
Training data about 500K query–passage pairs
Training 3 stages — multi-vector → self-distillation → hard negatives
Training hardware Single GPU, 46GB VRAM
License Apache-2.0 (see License and attribution)

Note on sequence length. The base model accepts up to 8192 tokens, but training and evaluation were done at shorter lengths. Quality is only guaranteed up to 512 tokens. Chunk accordingly.


Usage

pip install -U sentence-transformers

The model distinguishes queries from passages using the special tokens <Q> and <P>. These are required — retrieval quality degrades noticeably without them. They are registered as prompts in the published model, so prompt_name adds them for you.

import torch.nn.functional as F
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("FronyAI/frony-embed-medium-arctic-ko-v2.5")

queries  = ["대한민국의 수도는 어디입니까?"]
passages = ["서울은 대한민국의 수도이다.", "부산은 대한민국 제2의 도시이다."]

q = model.encode(queries,  prompt_name="query",   convert_to_tensor=True)
p = model.encode(passages, prompt_name="passage", convert_to_tensor=True)

# Outputs are L2-normalized already, so cosine similarity is a plain dot product.
scores = q @ p.T                      # [1, 2]

# 512 dimensions (Matryoshka): slice the first 512, then re-normalize.
# Truncating a unit vector leaves it shorter than unit length, and skipping the
# second normalization distorts every score that follows. Truncate queries and
# passages to the same width — mixing widths produces meaningless scores.
q512 = F.normalize(q[:, :512], p=2, dim=-1)
p512 = F.normalize(p[:, :512], p=2, dim=-1)
scores_512 = q512 @ p512.T            # [1, 2]

# Outside sentence-transformers, write the tokens inline instead:
#   "<Q>대한민국의 수도는 어디입니까?"
#   "<P>서울은 대한민국의 수도이다."

512 is the only supported truncation. Stage 3 trains the full 1024-dimension output and the 512-dimension prefix jointly, with equal weight. Narrower widths such as 256 or 128 were never part of the training objective and are not expected to hold up.


Training

Training runs in three sequential stages, each initialized from the previous stage's checkpoint:

Three-stage training pipeline Training starts from the base model snowflake-arctic-embed-l-v2.0 and proceeds through three sequential stages: Stage 1 multi-vector training, Stage 2 dense transfer via self-distillation, and Stage 3 hard-negative fine-tuning. The result is this model, frony-embed-medium-arctic-ko-v2.5. Each stage initializes from the previous stage's checkpoint. Base model Stage1: Align Multi-vector training Stage2: Distill Dense transfer via self-distillation Stage3: Refine Hard-negative fine-tuning This model

The reasoning behind the order: preliminary experiments showed multi-vector (late-interaction) retrieval consistently outperforming dense retrieval on our data. Rather than ship a multi-vector model — which costs far more at index and query time — we train that objective first, then transfer what it learned into a single-vector representation, and finally sharpen the result against hard negatives.

Shared setup

Common across all three stages:

Pooling Mean pooling over tokens, attention-mask weighted
Max sequence length 512
Temperature 0.02
Loss InfoNCE (cross-entropy over similarity scores)
Optimizer 8-bit AdamW (bitsandbytes)
Schedule Polynomial decay, power=1.0
Weight decay 1e-3, excluding biases and LayerNorm
Precision fp16
Gradient accumulation 4
Gradient clipping 1.0
Framework PyTorch Lightning
Model selection Early stopping on validation loss

The validation set is held fixed across all three stages, so loss curves are directly comparable from one stage to the next.

Query and passage tokens. <Q> and <P> are added to the vocabulary as genuine special tokens in Stage 1, with the embedding matrix resized accordingly, and their embeddings are trained through all three stages. They are not string prefixes in the manner of "query: " — they are single learned tokens, which is why they cost one token rather than several and why omitting them degrades retrieval noticeably.


Stage1: Align — Multi-vector training

What it's for. Learn token-level interaction before anything is compressed into a single vector.

Preliminary experiments on our data showed multi-vector (late-interaction) retrieval consistently beating dense retrieval. Shipping a multi-vector model would multiply index and query cost, so we train that objective first and transfer it into a single vector later. This stage exists to produce something worth transferring.

Scoring. ColBERT-style MaxSim over the full token sequence, no pooling applied: each query token takes its best-matching passage token, and those maxima are averaged across the query. Training is InfoNCE over these scores with in-batch negatives.

Vocabulary change. <Q> and <P> are added here as genuine special tokens, with the embedding matrix resized accordingly, and their embeddings train through all three stages.

Cumulative batching. Multi-vector scoring materializes a token-by-token similarity matrix for every pair, which makes large batches expensive — and batch size is exactly what determines how many in-batch negatives the contrastive loss sees. Rather than paying for a larger batch, each step maintains a FIFO queue of the four most recent passage batches and scores the current query batch against all of them, giving a negative pool of 32 passages from a batch of 8. The queued passages are re-encoded at every step rather than cached, so gradients flow through all 32 — a memory-for-compute trade, not a memory bank in the MoCo sense (He et al., 2020, arXiv:1911.05722), since the negatives stay current with respect to the model's parameters.

Note that two numbers here collide by coincidence: batch size 8 with gradient accumulation 4 gives an effective batch of 32, and the negative pool is also 32. Gradient accumulation is the ordinary kind and only affects how often the optimizer steps, while cumulative batching only affects how many passages each query is scored against. This scheme was also arrived at independently while working around memory limits — gradient caching (Gao et al., 2021, arXiv:2101.06983) solves the same problem more efficiently by caching gradients with respect to the embeddings and replaying the encoder in sub-batches, which decouples pool size from activation memory and is the most direct way to widen the pool in a future version.

Configuration

Initialized from snowflake-arctic-embed-l-v2.0
Batch size 8
Negative pool 32 passages (8 × 4 queued batches)
Learning rate 1e-5 → 1e-6
Warmup 5% of total steps
Early stopping patience 5
Validation interval every 10% of an epoch

Stage2: Distill — Dense transfer via self-distillation

What it's for. Move what Stage 1 learned into the pooled single-vector representation — the one the released model actually uses at inference.

What changed. Batching, learning rate, and negative pool all carry over unchanged from Stage 1. What changes is the objective: the pooled vector becomes the thing being trained, and the token-level scoring shifts from being the training target to being the teacher.

Start with what went wrong. The first version of this stage optimized dense objectives alone. The token loss held for a while, then began climbing partway through training — the multi-vector ability acquired in Stage 1 was decaying under dense-only optimization. That matters more than it first appears, because the distillation targets are produced by that same token-level scoring: degrading it degrades the signal being distilled. The fix was a small hard-label loss on the token vectors, weighted 0.10, whose only job is to anchor the representation the distillation depends on.

This is also why the pipeline has three stages rather than two. Stage 1's contribution has to be actively maintained during Stage 2, not merely inherited from it.

Stage 2 self-distillation: three loss terms combined into one objective A single encoder forward pass feeds two representations. A dense vector is produced by mean pooling, while the token vectors are left unpooled, one per token. Three loss terms follow: a hard-label loss on the dense vector, a distillation loss scoring the dense vector against soft targets from the token vectors, and a hard-label loss on the token vectors. The three are combined as a weighted average into one scalar total loss. encoder one pass dense vector mean pooled token vectors one per token dense loss hard labels distillation loss soft targets token loss hard labels weighted average total loss one scalar

How the two representations work. No separate teacher model is involved. A single forward pass produces one set of hidden states, and two representations are read off it — the dense vector, produced by mean pooling and scored by dot product, and the token vectors, left unpooled and scored by the same MaxSim as Stage 1. Both come from the same encoder in the same step, so the model teaches itself.

Three terms combine as a weighted sum, with effective weights dense 0.81, distillation 0.09, token 0.10. The distillation term supplies the dense vector with soft targets drawn from the token vectors' score distribution, so the dense representation is pulled toward the ranking that multi-vector scoring produces rather than only toward the correct answer. Both representations receive gradient from that term, which makes it a consistency constraint between the two rather than one-way transfer from a frozen teacher.

Configuration

Initialized from Stage 1 checkpoint
Batch size 8
Negative pool 32 passages (8 × 4 queued batches)
Learning rate 1e-5 → 1e-6
Warmup 5% of total steps
Early stopping patience 5
Validation interval every 10% of an epoch

Stage3: Refine — Hard-negative fine-tuning

What it's for. Sharpen discrimination at the top of the ranking, and make the 512-dimension truncation a first-class output rather than an afterthought.

What changed — most of it. This is the sharpest break in the pipeline. Cumulative batching is dropped and in-batch negatives are removed entirely. The learning rate picks up where Stage 2 ends and drops an order of magnitude below it, and warmup is omitted because the model arrives already converged. Validation runs twice as often as in earlier stages, and unlike Stages 1 and 2 the released weights come from the best checkpoint rather than the last.

The training signal changes character along with it. Each query is scored against exactly its own positive and its own four mined hard negatives — a five-way softmax per query, with no easy negatives in the mix. Every gradient step is spent on distinctions the model is likely to get wrong, which is what produces this model's characteristic profile: strong Accuracy@1, at some cost to recall further down the ranking.

Matryoshka objective. The same loss is computed twice, once on the full 1024 dimensions and once on the first 512, and the two are averaged with equal weight. This covers two widths only — not the geometric ladder (768/512/256/128) used in some Matryoshka implementations. The 512-dimension output is directly optimized and evaluated; narrower truncations were never trained and should not be assumed to work.

Hard negative mining. Negatives were mined with intfloat/multilingual-e5-large against a relative threshold rather than an absolute one. For each query, the positive passage's own similarity score sets the reference; candidates are admitted only if they score below 99% of it, and the top 4 admitted candidates become that query's hard negatives. If the positive scores 0.90, the cutoff is 0.891 and mining takes the highest scorers sitting just below it.

Both halves of that rule do specific work. Anchoring to the positive keeps negative difficulty consistent across the dataset, since raw cosine scores vary widely from query to query — a fixed cutoff like 0.85 would be too permissive where the positive scores 0.95 and too strict where it scores 0.80. The 1% margin then removes false negatives: candidates scoring at or near the positive's level are usually relevant passages themselves, and training on them teaches the model to push apart texts it should be pulling together. What survives is the intended target — passages close enough to be genuinely confusable, but far enough to be actually wrong.

Configuration

Initialized from Stage 2 checkpoint
Batch size 8
Negatives per query 4 mined hard negatives, no in-batch negatives
Learning rate 1e-6 → 1e-7
Warmup None
Early stopping patience 10
Validation interval every 5% of an epoch
Model selection Best validation checkpoint

Data and augmentation

Roughly 500,000 query–passage pairs from multiple sources, including AI Hub.

Because a growing share of retrieval corpora is LLM-generated and Markdown-formatted, part of the training data was converted into Markdown-style passages. Three augmentations were applied, each targeting a different failure mode:

Augmentation Targets
Pair concatenation Multi-part queries and multi-passage contexts
Language transfer (ko ↔ en) Cross-lingual retrieval
Style transfer (plain → Markdown) Structured, LLM-generated passages

Augmentation was performed with Gemma-3-12B.


Evaluation

Setup

Five dataset groups:

  • 3 groups — subsets extracted from AI Hub datasets
  • 1 group — synthetic queries paired with Markdown-style passages, generated by GPT-4o-mini from a sports regulation PDF
  • 1 group — a concatenation of the four groups above, as a mixed-domain set

Train/eval separation. Evaluation sets were split off from their source groups before training and were never seen during any of the three stages. Although AI Hub appears in both the training corpus and the evaluation groups, the specific query–passage pairs used for evaluation were held out and excluded from training.

Each query has a single correct passage, so Accuracy@k here is equivalent to Hit Rate@k. Reported numbers are the average across all five groups.

The mixed group is a concatenation of the other four, so it is not statistically independent of them. The five-group average consequently weights the first four groups twice. Read the average as an aggregate summary rather than as five independent measurements.

Results

Architecture Open/Closed Acc@1 Acc@3 Acc@5 Acc@10
FronyAI/frony-embed-medium-arctic-ko-v2.5 Open 0.6942 0.8361 0.8807 0.9197
FronyAI/frony-embed-medium-arctic-ko-v2.5 (half dim) Open 0.6778 0.8277 0.8726 0.9129
dragonkue/snowflake-arctic-embed-l-v2.0-ko Open 0.6612 0.8396 0.8931 0.9390
nlpai-lab/KURE-v1 Open 0.6434 0.8240 0.8788 0.9285
upstage-large Closed 0.6323 0.8522 0.9068 0.9459
BAAI/bge-m3 Open 0.5849 0.7763 0.8420 0.8985
intfloat/multilingual-e5-large Open 0.5764 0.7630 0.8267 0.8891
Snowflake/snowflake-arctic-embed-l-v2.0 Open 0.5726 0.7591 0.8232 0.8917
jinaai/jina-embeddings-v3 Open 0.5270 0.7242 0.7953 0.8644
openai-text-embedding-3-large Closed 0.4903 0.6621 0.7316 0.8149

On public benchmarks

No public benchmark scores are reported here, and this is a deliberate choice rather than an omission.

The training corpus was assembled from a broad mix of Korean sources, and we cannot currently rule out overlap with the evaluation splits of common public benchmarks. Reporting numbers under those conditions would produce scores that look strong for the wrong reason. We would rather publish an internal evaluation we can vouch for than a public one we can't.

Auditing the training corpus for benchmark contamination is planned. Public results will be added once the training set can be certified clean against the specific benchmarks reported — not before.

In the meantime, treat the table above as a comparison conducted under consistent conditions across all listed models, and validate on your own data before committing to any of them.


References

The three-stage pipeline was assembled from ideas in the following work. Each entry notes what was taken from it.

Multi-stage pipeline structure

  • LG AI Research (2025). EXAONE 4.0: Unified Large Language Models Integrating Non-reasoning and Reasoning Modes. arXiv:2507.11407 — the shape of a sequential pipeline where each stage initializes from the previous checkpoint with a different objective.

Token-level similarity (Stages 1 and 2)

  • Khattab, O., Zaharia, M. (2020). ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT. SIGIR 2020. arXiv:2004.12832 — the MaxSim late-interaction score.

Self-distillation (Stage 2)

  • Chen, J., Xiao, S., Zhang, P., Luo, K., Lian, D., Liu, Z. (2024). BGE M3-Embedding: Multi-Lingual, Multi-Functionality, Multi-Granularity Text Embeddings Through Self-Knowledge Distillation. arXiv:2402.03216 — the idea of using one retrieval functionality's relevance scores as the teacher signal for another, within a single model. Stage 2 applies this between the token and dense representations.

Hard-negative mining and training parameters (Stage 3)

  • Yu, P., Merrick, L., Nuti, G., Campos, D. (2024). Arctic-Embed 2.0: Multilingual Retrieval Without Compromise. arXiv:2412.04506 — hard-negative mining with a tuned false-positive cutoff, and much of the training configuration. This is also the base model for this work.

Matryoshka objective (Stage 3)

  • Kusupati, A., et al. (2022). Matryoshka Representation Learning. NeurIPS 2022. arXiv:2205.13147 — the nested-dimension training objective.

Contrastive objective (all stages)

  • van den Oord, A., Li, Y., Vinyals, O. (2018). Representation Learning with Contrastive Predictive Coding. arXiv:1807.03748 — the InfoNCE loss.

Models and tools used

  • Snowflake/snowflake-arctic-embed-l-v2.0 — base model (cited above).
  • intfloat/multilingual-e5-large — teacher model for hard-negative mining in Stage 3. Wang, L., Yang, N., Huang, X., Yang, L., Majumder, R., Wei, F. (2024). Multilingual E5 Text Embeddings: A Technical Report. arXiv:2402.05672
  • google/gemma-3-12b-it — data augmentation (pair concatenation, ko↔en language transfer, plain→Markdown style transfer). Subject to the Gemma Terms of Use.
  • gpt-4o-mini — synthetic evaluation queries for one of the five evaluation groups. Not used to generate training data.
  • bitsandbytes — 8-bit Adam. Dettmers, T., Lewis, M., Shleifer, S., Zettlemoyer, L. (2022). 8-bit Optimizers via Block-wise Quantization. ICLR 2022. arXiv:2110.02861
  • sentence-transformers — packaging and inference. Reimers, N., Gurevych, I. (2019). Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP 2019. arXiv:1908.10084
  • PyTorch Lightning — training framework.

License and attribution

Model weights are released under Apache-2.0.

Training data. The training corpus includes datasets from AI Hub, operated by the National Information Society Agency (한국지능정보사회진흥원, NIA) under the Ministry of Science and ICT. Per AI Hub's usage policy, this work is acknowledged as follows:

본 모델의 학습에는 과학기술정보통신부와 한국지능정보사회진흥원의 「지능정보산업 인프라 조성」 사업의 일환으로 구축된 AI 허브 데이터가 활용되었습니다.

This model was trained in part on AI Hub datasets constructed under the Intelligent Information Industry Infrastructure Development project of the Ministry of Science and ICT and the National Information Society Agency (NIA), Republic of Korea.

Note the following, which concern the underlying data rather than these weights:

  • AI Hub data may be used for research and development, commercial and non-commercial alike. However, selling the datasets or other direct commercial use of the data requires separate agreement with the constructing institution.
  • No AI Hub data is redistributed in this repository. Only trained model weights are published.
  • Rights to AI Hub data remain with the constructing and participating institutions and NIA. The Apache-2.0 license on these weights does not grant any rights to that data.
  • Organizations and individuals located outside Korea require separate agreement with the constructing institution and NIA to use AI Hub data, and transferring the data abroad requires separate agreement as well. These conditions apply to the data itself; if your use case involves obtaining or handling AI Hub data, review them directly.

This summary is provided for convenience and is not legal advice. Verify current terms at the AI Hub usage policy, and consult your own counsel for deployments where the answer matters.

Contact

Open an issue or pull request with questions or suggestions, or email flash659@gmail.com.

Downloads last month
212
Safetensors
Model size
0.6B params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for FronyAI/frony-embed-medium-arctic-ko-v2.5

Finetuned
(35)
this model

Papers for FronyAI/frony-embed-medium-arctic-ko-v2.5