CoolFace
Modelpublic

LocalDoc/colbert-az

sourceHugging Faceapache-2.0updated 5mo agoView on Hugging Face
0likes3downloads
Model Card

ColBERT-AZ

A late-interaction retrieval model for Azerbaijani built on top of mmBERT-base-en-az. Trained via cross-encoder distillation from bge-reranker-v2-m3 on a mix of native Azerbaijani and translated retrieval data.

ColBERT-AZ uses late interaction (token-level MaxSim scoring) rather than dense single-vector retrieval, providing higher precision in retrieval compared to bi-encoder models of similar or larger size.

Model Details

PropertyValue
Parameters165M
Embedding dim128 (per token)
BackbonemmBERT-base-en-az (ModernBERT)
ArchitectureLate interaction (ColBERT)
Query max length32 tokens
Document max length256 tokens
LanguagesAzerbaijani, English
Training epochs1

Training

Data

ColBERT-AZ was trained on 3 million triplets sampled from a weighted mix of four reranked datasets:

All datasets include reranker scores from bge-reranker-v2-m3, used as teacher signal for knowledge distillation.

Recipe

HyperparameterValue
OptimizerAdamW
Learning rate1e-6
Weight decay0.01
Warmup ratio0.10
ScheduleCosine
Batch size16 (effective 32 via gradient accumulation)
Negatives per query (K)8
False negative filter threshold0.9 × pos_score
Distillation alpha (KL weight)0.7
Contrastive temperature0.05
Teacher temperature1.0
Mixed precisionBF16
Epochs1
HardwareNVIDIA RTX 5090 (32GB)

Loss

Combined KL distillation + InfoNCE:

L = α × KL(softmax(student_scores) || softmax(teacher_scores)) + (1 − α) × InfoNCE

where α = 0.7 and student scores are computed via MaxSim over [pos, neg1, ..., negK].

Evaluation

Held-out validation

Evaluated on 4,500 held-out triplets (1,500 per native source). Each query is ranked among 1 positive and 8 hard negatives.

SourceR@1R@3MRRNDCG@10
Books0.53870.76930.68210.7584
Legislation0.66330.84330.76790.8234
Retriever (general)0.83400.93270.89010.9167
Macro average0.67870.84840.78000.8328

AZ-MIRAGE benchmark

Evaluated on the AZ-MIRAGE retrieval benchmark (7,373 queries, 40,448 document pool):

MetricScore
P@10.3058
R@50.7518
R@100.8054
NDCG@50.5528
NDCG@100.5704
MRR@100.4930
F1@100.1464

Comparison with bi-encoder models on AZ-MIRAGE:

ModelParamsNDCG@10MRR@10P@1
ColBERT-AZ (this model)165M0.57040.49300.3058
BAAI/bge-m3568M0.50790.42040.2310
google/gemini-embedding-2-previewAPI0.53090.43720.2338
perplexity/pplx-embed-v1-4bAPI0.52250.43610.2470
microsoft/harrier-oss-v1-0.6b600M0.51680.43210.2535
intfloat/multilingual-e5-large560M0.48750.40430.2264
intfloat/multilingual-e5-base278M0.46720.38520.2116
sentence-transformers/LaBSE471M0.24720.19440.0943

Usage

This repository contains:

  • config.json, model.safetensors, tokenizer.* — encoder backbone (mmBERT-base-en-az)
  • projection.pt — ColBERT linear projection layer (768 → 128, no bias)

ColBERT requires both the backbone and the projection layer for correct inference.

Loading the model

python
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModel

class ColBERT(nn.Module):
    def __init__(self, model_name: str, embedding_dim: int = 128):
        super().__init__()
        self.backbone = AutoModel.from_pretrained(model_name)
        self.projection = nn.Linear(self.backbone.config.hidden_size, embedding_dim, bias=False)

    @torch.no_grad()
    def encode(self, input_ids, attention_mask, keep_mask=None):
        out = self.backbone(input_ids=input_ids, attention_mask=attention_mask, return_dict=True)
        emb = self.projection(out.last_hidden_state)
        emb = F.normalize(emb, p=2, dim=-1)
        eff_mask = attention_mask if keep_mask is None else attention_mask * keep_mask
        emb = emb * eff_mask.unsqueeze(-1).float()
        return emb, eff_mask

# Load
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tokenizer = AutoTokenizer.from_pretrained("LocalDoc/colbert-az")

# Add ColBERT special tokens
tokenizer.add_special_tokens({"additional_special_tokens": ["[Q]", "[D]"]})

model = ColBERT("LocalDoc/colbert-az")
model.backbone.resize_token_embeddings(len(tokenizer))

# Load projection layer
from huggingface_hub import hf_hub_download
proj_path = hf_hub_download(repo_id="LocalDoc/colbert-az", filename="projection.pt")
model.projection.load_state_dict(torch.load(proj_path, map_location="cpu"))

model = model.to(device).eval()

Encoding queries and documents

python
# Tokenization helpers
def tokenize_query(text: str, max_len: int = 32):
    text = f"[Q] {text}"
    enc = tokenizer(text, padding="max_length", truncation=True,
                    max_length=max_len, return_tensors="pt")
    # ColBERT trick: replace pad with mask for query expansion
    pad_mask = enc["input_ids"] == tokenizer.pad_token_id
    enc["input_ids"][pad_mask] = tokenizer.mask_token_id
    enc["attention_mask"] = torch.ones_like(enc["input_ids"])
    return enc

def tokenize_doc(text: str, max_len: int = 256):
    text = f"[D] {text}"
    return tokenizer(text, padding=True, truncation=True,
                     max_length=max_len, return_tensors="pt")

# Compute MaxSim score between query and a single document
def maxsim_score(query: str, document: str) -> float:
    q_enc = {k: v.to(device) for k, v in tokenize_query(query).items()}
    d_enc = {k: v.to(device) for k, v in tokenize_doc(document).items()}

    q_emb, _ = model.encode(q_enc["input_ids"], q_enc["attention_mask"])
    d_emb, d_mask = model.encode(d_enc["input_ids"], d_enc["attention_mask"])

    # MaxSim: for each query token, take max similarity over doc tokens, then sum
    sim = torch.einsum("qld,bnd->qlbn", q_emb, d_emb)
    sim = sim.masked_fill(~d_mask.unsqueeze(0).unsqueeze(0).bool(), float("-inf"))
    max_per_token, _ = sim.max(dim=-1)
    score = max_per_token.sum(dim=1).item()
    return score

# Example
query = "Azərbaycan mədəniyyətinin tarixi"
doc = "Azərbaycan mədəniyyəti zəngin tarixə malikdir və qədim dövrlərdən başlayaraq inkişaf edib."
print(f"Score: {maxsim_score(query, doc):.4f}")

Recommended retrieval pipeline

For production retrieval, use ColBERT-AZ with a proper indexing library that supports late interaction:

  • PLAID — official ColBERT indexing
  • pylate — modern ColBERT framework

These libraries handle efficient indexing, scalable MaxSim retrieval, and quantization for production deployment.

Citation

bibtex
@misc{colbert-az-2026,
  title  = {ColBERT-AZ: Late-Interaction Retrieval for Azerbaijani},
  author = {LocalDoc},
  year   = {2026},
  url    = {https://huggingface.co/LocalDoc/colbert-az}
}

License

Apache 2.0

Acknowledgements