Instructions to use qrizan/pii-masking-qwen2.5-3b-dpo_beta0_5 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use qrizan/pii-masking-qwen2.5-3b-dpo_beta0_5 with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("unsloth/qwen2.5-3b-instruct-unsloth-bnb-4bit") model = PeftModel.from_pretrained(base_model, "qrizan/pii-masking-qwen2.5-3b-dpo_beta0_5") - Notebooks
- Google Colab
- Kaggle
PII Masking - Qwen2.5-3B SFT+DPO (Bahasa Indonesia)
Model 3B untuk masking data pribadi (NAMA, NIK, NOMOR_HP) di teks administratif Bahasa Indonesia. Fine-tuned dari Qwen2.5-3B-Instruct dengan SFT + DPO (QLoRA 4-bit). Adapter only - butuh base model Qwen/Qwen2.5-3B-Instruct.
Tugas
Diberikan teks Bahasa Indonesia yang mengandung PII, model mengembalikan teks yang sama dengan setiap PII diganti tag:
| Tipe | Tag |
|---|---|
| Nama orang | [NAMA] |
| NIK (16 digit) | [NIK] |
| Nomor HP (awalan 08) | [NOMOR_HP] |
Distraktor TIDAK di-mask: nomor rekening, nomor transaksi, kode referensi, dan angka lain yang bukan data pribadi.
Inference (copy-paste, self-contained)
import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
# 1. Load base model (fp16 - ~6GB VRAM)
base = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-3B-Instruct",
torch_dtype=torch.float16,
device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-3B-Instruct")
# 2. Load DPO adapter
model = PeftModel.from_pretrained(base, "qrizan/pii-masking-qwen2.5-3b-dpo_beta0_5")
model.eval()
# 3. System prompt
SYSTEM_PROMPT = (
"Anda adalah sistem masking PII (data pribadi) untuk teks Bahasa Indonesia.\n"
"Tugas Anda: kembalikan teks yang SAMA PERSIS, tetapi ganti setiap data pribadi "
"dengan tag yang sesuai.\n\n"
"Aturan masking:\n"
"- Nama orang -> [NAMA]\n"
"- NIK (16 digit) -> [NIK]\n"
"- Nomor HP (diawali 08) -> [NOMOR_HP]\n\n"
"JANGAN di-mask (biarkan apa adanya): nomor rekening bank, nomor transaksi, "
"kode referensi, dan angka lain yang bukan data pribadi.\n\n"
"Ketentuan keluaran:\n"
"- Keluarkan HANYA teks hasil masking.\n"
"- Jangan tambahkan kalimat pengantar, penjelasan, catatan, atau tanda kutip.\n"
"- Pertahankan seluruh kata, tanda baca, dan spasi lain seperti aslinya.\n"
"- Jika sebuah data pribadi ditulis dengan spasi/pemisah di tengah digit, tetap "
"ganti seluruhnya dengan satu tag."
)
def mask_pii(text: str, max_new_tokens: int = 256) -> str:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Mask PII pada teks berikut:\n{text}"},
]
inputs = tokenizer.apply_chat_template(
messages, tokenize=True, add_generation_prompt=True,
return_tensors="pt", return_dict=True,
).to(model.device)
with torch.no_grad():
output_ids = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
do_sample=False,
pad_token_id=tokenizer.eos_token_id,
)
prompt_len = inputs["input_ids"].shape[1]
result = tokenizer.decode(output_ids[0][prompt_len:], skip_special_tokens=True)
return result.strip()
# Contoh
print(mask_pii("Halo, saya Budi Santoso, NIK 3273010101010001, HP 081234567890."))
# Output: Halo, saya [NAMA], NIK [NIK], HP [NOMOR_HP].
print(mask_pii("Transfer ke rekening 1234567890 atas nama Siti Rahayu."))
# Output: Transfer ke rekening 1234567890 atas nama [NAMA].
# (Nomor rekening tidak di-mask - ini distraktor, bukan PII)
Evaluasi
Semua metrik diukur di eval.jsonl (100 record sintetis Bahasa Indonesia, 15 di antaranya template holdout yang tidak pernah dilihat saat training - genuinely held-out).
| Metrik | Base (zero-shot) | SFT | DPO | Frontier (GPT-4o-mini) |
|---|---|---|---|---|
| leak_rate | 18.70% | 0.00% | 0.00% | 3.48% |
| over_mask_rate | 47.42% | 7.22% | 0.00% | 0.00% |
| breach_rate | 30.00% | 0.00% | 0.00% | 8.00% |
| exact_match | 0.0% | 93.0% | 97.0% | 89.0% |
| mean_jaccard | 0.749 | 0.951 | 0.984 | 0.973 |
Template 8 focus (glued-text, genuinely held-out, 7 record):
| Metrik | SFT | DPO (ini) |
|---|---|---|
| over_mask_rate | 100% (7/7) | 0% (0/7) |
| leak_rate | 0% | 0% |
| exact_match | 0% (0/7) | 57.1% (4/7) |
| mean_jaccard | 0.304 | 0.771 |
DPO menurunkan over-mask di template glued-text dari 100% โ 0%, tapi 3/7 record masih punya tag hallucination minor (mis. [NAMA] โ [FAMA]) - bukan leak literal, tapi tidak exact match. Tidak ada regresi di 12 template lain (non-holdout tetap 100% exact match).
Training
| SFT | DPO | |
|---|---|---|
| Base | Qwen2.5-3B-Instruct |
SFT adapter |
| Method | Unsloth QLoRA 4-bit | TRL DPOTrainer + PEFT |
| LoRA | r=16, alpha=16 | r=16, alpha=16 |
| Data | 400 train / 80 dev | 12 preference pairs (template 8) |
| Epochs | 5 | 40 |
| LR | 2e-4 | 5e-5 |
| Beta (KL) | - | 0.5 |
| Infra | Kaggle T4 | Kaggle T4 |
Limitasi
- PII scope terbatas - hanya NAMA, NIK, NOMOR_HP. Email, alamat, nomor rekening sebagai PII belum ditangani.
- Data sintetis - 13 template generator. Generalisasi ke teks dunia nyata belum diuji.
- Tag hallucination residual - 3/7 record glued-text punya tag minor error.
over_mask_rate/leak_ratebuta terhadap ini - cekexact_matchuntuk gambaran lengkap. - Model 3B - mungkin kurang kapabel untuk konteks sangat kompleks (narasi panjang, multi-entitas).
Adapter terkait
- SFT adapter:
qrizan/pii-masking-qwen2.5-3b-sft - Repo kode: github.com/qrizan/llm-posttraining-experiments (
05_pii_masking_id/)
- Downloads last month
- 4