NTA1802's picture
Shorten entity types section
acd5187 verified
---
language:
- vi
license: mit
tags:
- token-classification
- ner
- vietnamese
- legal
- electra
- transformers
base_model: NlpHUST/ner-vietnamese-electra-base
datasets:
- NTA1802/NER-Completing-With-Legal-Dataset
metrics:
- seqeval
pipeline_tag: token-classification
---
# 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`](https://huggingface.co/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ể****125 nhãn** (1 `O` + các tag B-/I-; riêng `ARTICLE``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``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.
```python
import torch
from pyvi import ViTokenizer
from transformers import AutoTokenizer, AutoModelForTokenClassification
MODEL_DIR = "NTA1802/NER-Completing-With-Legal-Dataset"
tok = AutoTokenizer.from_pretrained(MODEL_DIR, use_fast=True)
mdl = AutoModelForTokenClassification.from_pretrained(MODEL_DIR).eval()
id2label = mdl.config.id2label
@torch.no_grad()
def extract_entities(text: str):
words = ViTokenizer.tokenize(text).split()
enc = tok(words, is_split_into_words=True, return_tensors="pt", truncation=True)
pred_ids = mdl(**enc).logits[0].argmax(-1).tolist()
word_ids = enc.word_ids(0)
word_label = [None] * len(words)
seen = set()
for j, wid in enumerate(word_ids):
if wid is None or wid in seen:
continue
seen.add(wid)
word_label[wid] = id2label[pred_ids[j]]
entities, cur = [], None
for w, lab in zip(words, word_label):
if lab in (None, "O"):
if cur: entities.append(cur); cur = None
continue
prefix, _, etype = lab.partition("-")
if prefix == "B" or cur is None or cur["type"] != etype:
if cur: entities.append(cur)
cur = {"type": etype, "text": w.replace("_", " ")}
else:
cur["text"] += " " + w.replace("_", " ")
if cur: entities.append(cur)
return entities
print(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)