Achieves
NDCG@10 = 0.8488 on the
Zalo AI Legal Text Retrieval benchmark,
setting a new state-of-the-art on Vietnamese legal text retrieval.
Evaluated on
MTEB ZacLegalTextRetrieval (61.4K corpus documents, 818 test queries) using
pytrec_eval (same evaluation library as MTEB).
1 import torch
2 import torch . nn . functional as F
3 from transformers import AutoModel , AutoTokenizer
4
5 model_name = "darklethelong/vnlegal-lal"
6 tokenizer = AutoTokenizer . from_pretrained ( model_name )
7 model = AutoModel . from_pretrained ( model_name , torch_dtype = torch . float16 , device_map = "cuda" )
8 model . eval ( )
9
10 QUERY_PREFIX = "Instruct: Given a Vietnamese legal question, retrieve relevant legal passages that answer the question\nQuery: "
11
12 def encode ( texts , is_query = False , max_len = 2048 , batch_size = 32 ) :
13 all_embs = [ ]
14 for i in range ( 0 , len ( texts ) , batch_size ) :
15 batch = texts [ i : i + batch_size ]
16 if is_query :
17 batch = [ QUERY_PREFIX + t for t in batch ]
18 inputs = tokenizer ( batch , padding = True , truncation = True ,
19 max_length = max_len , return_tensors = "pt" ) . to ( "cuda" )
20 with torch . no_grad ( ) :
21 out = model ( ** inputs )
22 last_idx = inputs [ "attention_mask" ] . sum ( dim = 1 ) - 1
23 embs = out . last_hidden_state [ torch . arange ( len ( batch ) , device = "cuda" ) , last_idx ]
24 embs = F . normalize ( embs , p = 2 , dim = 1 )
25 all_embs . append ( embs . cpu ( ) )
26 return torch . cat ( all_embs , dim = 0 )
27
28 # Encode
29 query_embs = encode ( [ "Quy định về hợp đồng lao động" ] , is_query = True )
30 doc_embs = encode ( [ "Điều 13. Hợp đồng lao động..." ] )
31
32 # Cosine similarity
33 similarity = ( query_embs @ doc_embs . T ) . item ( )
SentenceTransformer(
(0): Transformer({'max_seq_length': 2048, 'do_lower_case': False}) with Transformer model: Qwen3Model
(1): Pooling({'word_embedding_dimension': 1024, 'pooling_mode_lasttoken': True})
(2): Normalize()
)
Stage 1: BM25 Index Construction
| Build lexical index over 61.4K corpus documents
|
Stage 2: Iterative ANCE Training (3 rounds)
| For each round:
| - Encode corpus + queries with current model
| - Mine hard negatives (dense + BM25 hybrid)
| - Filter false negatives (TopK-PercPos)
| - Train with InfoNCE loss + LoRA
|
Stage 3: Merge & Evaluate
| Merge LoRA adapter into base model
| -> Final model (NDCG@10 = 0.8488)
1 @misc{vnlegal-lal,
2 title={VNLegal-LAL: Vietnamese Legal Domain Embedding Model},
3 author={Le The Long},
4 year={2025},
5 url={https://huggingface.co/darklethelong/vnlegal-lal}
6 }