ko-vdr-preview / README.md
johnandru's picture
Update README.md
8c92c48 verified
|
Raw
History Blame Contribute Delete
5.37 kB
---
license: apache-2.0
language:
- ko
- en
base_model:
- Qwen/Qwen3-VL-Embedding-2B
pipeline_tag: image-text-to-text
library_name: sentence-transformers
tags:
- sentence-transformers
- feature-extraction
- visual-document-retrieval
- matryoshka
- lora
- korean
---
# ko-vdr-preview
Korean visual document retrieval — 6 MTEB multimodal tasks (text→image).
A LoRA fine-tune of [`Qwen/Qwen3-VL-Embedding-2B`](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B) trained on a mixed Korean/English VDR corpus with hard negatives mined by `Qwen3-VL-Embedding-8B`. Supports [Matryoshka embeddings](https://huggingface.co/blog/matryoshka) down to 128 dimensions (default: 2048).
### Summary of Findings
* **Significant Improvement over 2B:** `ko-vdr-preview` shows a massive performance uplift compared to the `Qwen3-VL-2B` baseline (e.g., ~0.48 vs ~0.35 avg nDCG@10).
* **Closing the Gap with 8B:** The model's performance is remarkably close to the `Qwen3-VL-8B` model, offering near-state-of-the-art accuracy with much greater efficiency.
## Usage
### Install Dependencies
```bash
pip install -U sentence-transformers>=5.4.1
### Python code
Then you can load this model and run inference.
```python
from sentence_transformers import SentenceTransformer
# Download from the 🤗 Hub
model = SentenceTransformer("johnandru/ko-vdr-preview")
# Run inference
queries = [
'30인 이상 상용근로자를 보유한 기업의 1인당 평균 월별 법정외 복지비용이 10~29인 규모 기업보다 높은지 판단해 주세요'
]
documents = [
'ko-vdr-public/3818.png',
'ko-vdr-public/7753.png',
'ko-vdr-public/3760.png'
]
query_embeddings = model.encode_query(queries)
document_embeddings = model.encode_document(documents)
print(query_embeddings.shape, document_embeddings.shape)
# [1, 2048] [3, 2048]
# Get the similarity scores for the embeddings
similarities = model.similarity(query_embeddings, document_embeddings)
print(similarities)
```
### Matryoshka truncation
The model supports shortened embeddings via Matryoshka training.
Supported dimensions: 2048, 1536, 1024, 768, 512, 256, 128
model = SentenceTransformer("johnandru/ko-vdr-preview", truncate_dim=512)
## Model Details
|Property| Value
| --- | ---:|
|Base model| Qwen/Qwen3-VL-Embedding-2B|
|Fine-tuning method| LoRA (r=32, alpha=32, no dropout)|
|LoRA target modules| q_proj, k_proj, v_proj, up_proj, down_proj, gate_proj|
|Embedding dimension| 2048 (Matryoshka: 1536 / 1024 / 768 / 512 / 256 / 128)|
|Precision| bfloat16|
|Attention| Flash Attention 2|
|Max image pixels| 1280 × 28 × 28|
|Framework| sentence-transformers==5.4.1, peft>=0.19.1|
## Training
### Data
Training used a multi-source Korean/English VDR dataset with hard negatives mined offline:
|Source| Language| Type
| --- | --- | ---: |
|NomaDamas/ko-vdr-train-public-v2.0 |Korean |Query–page pairs|
|whybe-choi/ko-vdr-train-private-v0.1| Korean |Query–page pairs|
|vidore/colpali_train_set| English |Query–page pairs|
|tomaarsen/llamaindex-vdr-en-train-preprocessed| English |Query–page pairs|
|Ko/En text retrieval corpus| Korean + English| Text pairs|
Hard negatives were mined with Qwen/Qwen3-VL-Embedding-8B using absolute_margin=0.05 and 7 negatives per pair (top sampling).
### Loss
MatryoshkaLoss(SelfGuideCachedMultipleNegativesRankingLoss):
InfoNCE with cosine similarity (scale=20), cached mini-batches (mini_batch_size=4), and Matryoshka multi-granularity weighting.
## Evaluation
### Task abbreviations
| Short | MTEB task |
| --- | --- |
| SDS-T2IT | `SDSKoPubVDRT2ITRetrieval` |
| SDS-T2I | `SDSKoPubVDRT2IRetrieval` |
| KV-Cyber | `KoVidore2CybersecurityRetrieval` |
| KV-Econ | `KoVidore2EconomicRetrieval` |
| KV-Energy | `KoVidore2EnergyRetrieval` |
| KV-Hr | `KoVidore2HrRetrieval` |
### Results - nDCG@10
| rank | model_name | SDS-T2IT_nDCG@10 | SDS-T2I_nDCG@10 | KV-Cyber_nDCG@10 | KV-Econ_nDCG@10 | KV-Energy_nDCG@10 | KV-Hr_nDCG@10 | avg_nDCG@10 |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 1 | Qwen/Qwen3-VL-Embedding-8B | 0.6999 | 0.6136 | 0.6857 | 0.2008 | 0.5415 | 0.2661 | **0.5013** |
| 2 | (**Ours**) johnandru/ko-vdr-preview | 0.6732 | 0.5623 | 0.6540 | 0.2139 | 0.5061 | 0.2975 | **0.4845** |
| 3 | Qwen/Qwen3-VL-Embedding-2B | 0.6605 | 0.2923 | 0.5359 | 0.1246 | 0.3565 | 0.1498 | **0.3533** |
### Results - Recall@10
| rank | model_name | SDS-T2IT_Recall@10 | SDS-T2I_Recall@10 | KV-Cyber_Recall@10 | KV-Econ_Recall@10 | KV-Energy_Recall@10 | KV-Hr_Recall@10 | avg_Recall@10 |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 1 | Qwen/Qwen3-VL-Embedding-8B | 0.9033 | 0.7817 | 0.7527 | 0.2975 | 0.6059 | 0.3433 | **0.6141** |
| 2 | (**Ours**) johnandru/ko-vdr-preview | 0.8533 | 0.7500 | 0.7538 | 0.2868 | 0.5940 | 0.3847 | **0.6038** |
| 3 | Qwen/Qwen3-VL-Embedding-2B | 0.8650 | 0.4317 | 0.6012 | 0.1858 | 0.4166 | 0.1962 | **0.4494** |
### Notes
- All Qwen3-VL-Embedding family models loaded with `max_pixels = 1280 * 28 * 28`, bf16, flash-attention-2.
- Prompt usage:
- Qwen3-VL-Embedding 2B / 8B and our LoRA fine-tune: training prompt `"Represent the user's input."` (matches train.py).
- LoRA fine-tune used `peft 0.19.1` workaround in `loader.py` to inject `lora_B` weights
(transformers 5.5.4 silently dropped them on `from_pretrained` for headless models — see PR huggingface/transformers#45428).