NER — Vietnamese Legal Domain (Electra-base)
Mô hình
Named Entity Recognition (NER) cho văn bản pháp lý tiếng Việt, fine-tune từ
NlpHUST/ner-vietnamese-electra-base trên bộ dữ liệu pháp lý tự xây dựng với
BIO tagging scheme,
63 loại thực thể và
125 nhãn (1
O + các tag B-/I-; riêng
ARTICLE và
LOCATION chỉ xuất hiện dạng
B-).
Phạm vi: 3 luật — Luật Viễn thông, Luật An ninh mạng, Luật Công nghệ thông tin (cùng các văn bản hướng dẫn liên quan).
Các loại thực thể nhận dạng
125 nhãn BIO (1 O + 62 loại × 2 prefix B/I) thuộc 7 nhóm: văn bản pháp lý, chủ thể, khái niệm pháp lý, an ninh mạng, hạ tầng & kỹ thuật, tiêu chuẩn & quy định, và các thực thể khác (địa điểm, thời gian, mức độ...).
Kỹ thuật fine-tune
- Subword label alignment — chỉ gán nhãn cho first subtoken, các subtoken còn lại dùng
-100
- Mixed precision (bf16) + TF32 — tối ưu tốc độ trên GPU Ampere/Hopper
- Layer-wise Learning Rate Decay (LLRD) với decay factor
0.9 — backbone LR 3e-5, classifier head LR 1e-4
- Linear warmup + decay (
warmup_ratio=0.1), weight decay 0.01, gradient clipping 1.0
- Label smoothing
0.1
- Early stopping theo
eval_f1 (seqeval entity-level), patience 5
- Dynamic padding với
DataCollatorForTokenClassification
- Reproducible seed
42
Dữ liệu huấn luyện
| Split | Số câu |
|---|
| Train | 6 403 |
| Validation | 1 279 |
| Test | 1 288 |
Dataset: JSONL format, mỗi dòng gồm tokens (list) và labels (list BIO labels) — ngoài ra có thêm id và sentence. Lưu ý tokens đã được word-tokenize sẵn (token dạng Bộ_Công_an, an_ninh_mạng).
Kết quả đánh giá
Train 30 epoch (early stopping theo eval_f1), GPU NVIDIA RTX PRO 6000 Blackwell, ~262 s — train_loss ≈ 1.09 (đã gồm label smoothing 0.1). Metric seqeval entity-level:
| Split | Precision | Recall | F1 |
|---|
| Validation | 0.9965 | 0.9970 | 0.9968 |
| Test | 0.9960 | 0.9976 | 0.9968 |
Trên tập test (7 804 thực thể), theo classification_report:
| Precision | Recall | F1 |
|---|
| micro avg | 0.9960 | 0.9976 | 0.9968 |
| macro avg | 0.9896 | 0.9963 | 0.9926 |
| weighted avg | 0.9961 | 0.9976 | 0.9968 |
Phần lớn entity type đạt F1 = 1.0. Các type có support lớn: OBLIGATION (1079, F1 1.00), LEGAL_ACTOR (1025, F1 0.996), LEGAL_ACTION (940, F1 0.997), LAW (690, F1 1.00), TIME (556, F1 0.998), SANCTION (485, F1 0.998), SYSTEM (327, F1 0.989). Các type F1 thấp nhất đều thuộc nhóm support rất nhỏ: AUTHORIZATION (0.857, support 3), AUDIT (0.933, 7), LICENSE (0.947, 19), LEGAL_CONCEPT (0.952, 31), PERMISSION (0.964, 28).
⚠️ F1 ≈ 0.997 phản ánh dữ liệu được sinh khá theo khuôn mẫu (template) — số liệu trên domain văn bản pháp lý thực tế, đa dạng hơn, sẽ thấp hơn đáng kể.
Cách sử dụng
⚠️ Quan trọng: model được train trên dữ liệu đã word-tokenize (token dạng
Bộ_Công_an, an_ninh_mạng, ...). Khi inference bắt buộc phải word-tokenize trước
rồi truyền với is_split_into_words=True — nếu truyền raw text thẳng vào tokenizer/pipeline,
entity sẽ lệch hoàn toàn.
1import torch
2from pyvi import ViTokenizer
3from transformers import AutoTokenizer, AutoModelForTokenClassification
4
5MODEL_DIR = "NTA1802/NER-Completing-With-Legal-Dataset"
6tok = AutoTokenizer.from_pretrained(MODEL_DIR, use_fast=True)
7mdl = AutoModelForTokenClassification.from_pretrained(MODEL_DIR).eval()
8id2label = mdl.config.id2label
9
10@torch.no_grad()
11def extract_entities(text: str):
12 words = ViTokenizer.tokenize(text).split()
13 enc = tok(words, is_split_into_words=True, return_tensors="pt", truncation=True)
14 pred_ids = mdl(**enc).logits[0].argmax(-1).tolist()
15 word_ids = enc.word_ids(0)
16
17 word_label = [None] * len(words)
18 seen = set()
19 for j, wid in enumerate(word_ids):
20 if wid is None or wid in seen:
21 continue
22 seen.add(wid)
23 word_label[wid] = id2label[pred_ids[j]]
24
25 entities, cur = [], None
26 for w, lab in zip(words, word_label):
27 if lab in (None, "O"):
28 if cur: entities.append(cur); cur = None
29 continue
30 prefix, _, etype = lab.partition("-")
31 if prefix == "B" or cur is None or cur["type"] != etype:
32 if cur: entities.append(cur)
33 cur = {"type": etype, "text": w.replace("_", " ")}
34 else:
35 cur["text"] += " " + w.replace("_", " ")
36 if cur: entities.append(cur)
37 return entities
38
39print(extract_entities("Khoản 1 Điều 46 Luật An ninh mạng quy định về bảo vệ dữ liệu cá nhân."))
Thông tin mô hình
- Base model:
NlpHUST/ner-vietnamese-electra-base (ELECTRA-base, 12 layers, hidden 768)
- Số nhãn: 125 (BIO scheme — 63 entity types)
- Max sequence length: 512
- Framework: PyTorch + HuggingFace Transformers ≥ 4.41
- Ngôn ngữ: Tiếng Việt (văn bản pháp lý — viễn thông, an ninh mạng, công nghệ thông tin)