Deteksi & lokalisasi pemalsuan ijazah, transkrip, dan sertifikat kompetensi Indonesia
Machine learning untuk dokumen legacy · blockchain anchoring untuk dokumen terverifikasi
- Masalah
- Pendekatan
- Status proyek
- Arsitektur
- Quickstart
- Dataset
nevu_v0 - Operasi pemalsuan
- Kondisi tangkap (augmentasi)
- Split & protokol evaluasi
- Reproduktibilitas
- Konfigurasi
- Testing
- Struktur repositori
- Roadmap
- Batasan & risiko
- Etika, privasi & lisensi
- FAQ
Pemalsuan ijazah di Indonesia bukan kasus pinggiran — ia punya pasar, harga, dan rantai pasok sendiri. Dua hal membuatnya sulit dilawan:
- Dokumen legacy tidak punya jejak digital. Puluhan juta ijazah terbit sebelum sistem verifikasi apa pun ada. Tidak ada hash, tidak ada tanda tangan digital, tidak ada QR. Yang tersisa hanya citra — foto atau hasil scan.
- Verifikasi manual tidak menskala. HRD menerima ratusan lamaran; memeriksa satu per satu ke registry institusi tidak realistis, dan banyak institusi tidak punya endpoint verifikasi sama sekali.
Sementara itu, alat pemalsuan makin murah: inpainting berbasis diffusion bisa mengganti nama atau nilai pada scan dengan mulus, tanpa keahlian desain.
NEVU menjawab dua sisi masalah dengan dua jalur yang saling mengisi:
| Jalur ML (inti) | Jalur blockchain (bungkus) | |
|---|---|---|
| Untuk | dokumen lama yang hanya ada sebagai citra | dokumen yang sudah lolos verifikasi |
| Menjawab | "apakah citra ini menunjukkan tanda manipulasi, dan di mana?" | "apakah dokumen ini tercatat, oleh siapa, kapan, dan masih berlaku?" |
| Keluaran | skor probabilistik + heatmap + field OCR | hash on-chain + status (aktif / dicabut) |
| Sifat | probabilistik — alat triase | deterministik — catatan permanen |
Important
Model menghasilkan probabilitas; blockchain menyimpan pencatatan. Skor ML tidak pernah ditulis on-chain sebagai kebenaran. Yang di-anchor hanya dokumen yang sudah di-approve oleh manusia atau institusi penerbit. Ini menjaga agar ketidakpastian statistik tidak berubah jadi klaim permanen.
Urutan pembangunan bersifat mengikat: kualitas model dulu (M0–M2), aplikasi
kemudian (M3–M5). app/ dan chain/ tidak dikerjakan sebelum gate akurasi M2
terlampaui dan dilaporkan apa adanya.
| Milestone | Isi | Status |
|---|---|---|
| M0 | Fondasi repo, generator dataset sintetis + ground-truth mask, splits, dataset card | ✅ |
| M1 | Model v1 (image-level) & v2 (+lokalisasi), baseline ELA/CNN, leaderboard.md |
⬜ |
| M2 | Cabang OCR + fusi multimodal, ablation, robustness — gate F1 ≥ 0.90 | ⬜ |
| M3 | Backend FastAPI /predict (skor + heatmap + OCR) |
🔒 |
| M4 | NevuRegistry.sol, deploy testnet, /anchor + /verify |
🔒 |
| M5 | Frontend Next.js, Verify Page + QR, docker compose up |
🔒 |
🔒 = terkunci sampai gate M2 lolos. app/ dan chain/ sengaja masih kosong.
┌──────────────── FASE 1 — ML (inti) ────────────────┐
│ │
Upload │ Preprocess ──┬── Visual branch (ELA/SRM + CNN) ─┐ │ score
dokumen │ (deskew, │ │ │ + heatmap
─────►│ normalize) ├── Localization head (U-Net/FPN) ─┼─┼──► + field OCR
│ │ │ │
│ └── Text branch (OCR + konsistensi ─┘ │
│ tipografi/format) → Fusion │
└─────────────────────────────────────────────────────┘
│
FastAPI │ POST /predict
▼
┌──────────────── FASE 2 — dApp (bungkus) ───────────┐
│ Next.js frontend │
│ upload → hasil ML → [Amankan ke blockchain] │
│ │ POST /anchor │
│ ▼ │
│ NevuRegistry.sol ──── keccak256(dokumen) ──► ⛓ │
│ anchor / revoke / getStatus testnet│
│ ▲ │
│ Verify Page (publik, tanpa login) ── GET /verify ───┘
│ scan QR / paste hash → status tercatat │
└─────────────────────────────────────────────────────┘
Pemisahan yang dijaga ketat di UI: "hasil analisis ML (perkiraan)" dan "status tercatat di blockchain (fakta pencatatan)" ditampilkan sebagai dua blok terpisah dengan bahasa berbeda. Tidak ada cap "ASLI"/"PALSU" mutlak.
Prasyarat: Python 3.9+ (3.11 direkomendasikan), ~500 MB ruang disk untuk dataset default.
git clone https://github.com/itsnevu/credential-forgery-detection.git
cd credential-forgery-detection
python3 -m venv --system-site-packages .venv
.venv/bin/pip install -r ml/requirements.txt
# 1. generate dataset (400 sampel, seed 1337, ±2 menit)
.venv/bin/python -m ml.src.forge.generate
# 2. lihat hasilnya — contact sheet dengan mask di-overlay merah
.venv/bin/python -m ml.src.forge.viz --n 12
open ml/datasets/nevu_v0/preview.png
# 3. jalankan test
.venv/bin/python -m pytest ml/tests -qDataset yang dihasilkan ada di ml/datasets/nevu_v0/ (gitignored):
nevu_v0/
├── images/000000.jpg … # citra dokumen (asli & palsu)
├── masks/000000.png … # ground-truth mask biner per piksel
├── labels.jsonl # satu record per sampel
├── splits.json # train / val / test / test_cross_source / test_genai
├── stats.json # ringkasan komposisi
└── config.used.yaml # config persis yang menghasilkan dataset ini
Satu record labels.jsonl:
{
"id": "000042",
"image": "images/000042.jpg",
"mask": "masks/000042.png",
"label": 1,
"template": "ijazah_classic",
"op": "text_replace",
"aug": "phone",
"split": "train",
"fields": { "nama": "Indah Suryani, S.E.", "institusi": "Universitas Tirta Kencana",
"tahun": "2019", "nomor": "412/UN/B/2019", "program": "Manajemen",
"kota": "Medan" },
"forgery": { "op": "text_replace", "ok": true, "field": "program",
"old": "Akuntansi", "new": "Manajemen", "font_changed": false }
}fields adalah ground truth untuk evaluasi OCR di M2; forgery mencatat persis
apa yang diubah, sehingga metrik bisa dipecah per tipe serangan.
Belum ada dataset publik berisi ijazah/transkrip/sertifikat Indonesia dengan
mask pemalsuan per piksel. Dokumen asli tidak bisa dikumpulkan dan dirilis
tanpa membocorkan data pribadi (UU PDP No. 27/2022). Karena itu nevu_v0
dibangkitkan sepenuhnya: dokumen bersih dirender, bukan dikumpulkan — ground
truth diketahui persis, dan tidak ada orang sungguhan di dalamnya.
Contact sheet: area merah = ground-truth mask. Label menunjukkan
ASLI/PALSU, operasi pemalsuan, kondisi tangkap, template family, dan split.
| Dimensi | Distribusi |
|---|---|
| Label | asli 203 · palsu 197 |
| Template | ijazah_classic 125 · transkrip 96 · ijazah_modern 92 · sertifikat_lsp 87 |
| Operasi | text_replace 62 · font_substitution 41 · splice 37 · copy_move 32 · local_recompress 25 |
| Kondisi | scan 113 · phone 108 · clean 102 · recompressed 77 |
| Split | train 221 · test_cross_source 87 · test 53 · val 39 |
Setiap family adalah sumber yang berbeda — beda tipografi, tata letak, ornamen, dan ukuran halaman. Ini yang membuat uji generalisasi cross-source bermakna.
| Family | Jenis | Halaman | Karakteristik |
|---|---|---|---|
ijazah_classic |
Ijazah | Potret 1000×1400 | Border ganda, stempel bundar, tanda tangan, serif, rata tengah |
ijazah_modern |
Ijazah | Potret 1000×1400 | Minimalis, ornamen sudut, field berlabel, sans + mono, rata kiri |
transkrip |
Transkrip akademik | Potret 1000×1400 | Tabel mata kuliah / SKS / nilai, garis pemisah, IPK |
sertifikat_lsp |
Sertifikat kompetensi | Lanskap 1400×1000 | Format BNSP/LSP, masa berlaku, campuran serif & sans |
Dokumen dirender di atas kertas prosedural: warna dasar + noise frekuensi rendah (serat) + noise halus. Latar yang benar-benar rata akan membuat splicing terdeteksi secara trivial dan menggelembungkan semua metrik.
Setiap operasi di ml/src/forge/ops.py mematuhi kontrak
yang sama:
input: Document bersih (citra + kotak field) output:
(citra RGB, mask uint8 0/255, info dict)invarian: mask menutupi setiap piksel yang mungkin diubah operasi itu
Invarian ini diuji (test_op_changes_image_and_mask_covers_change, kebocoran
< 1%). Kalau sebuah operasi menyentuh piksel di luar mask-nya, segmentation head
akan belajar mengabaikan bukti yang nyata.
| Operasi | Mekanisme | Kenapa ada di set ini |
|---|---|---|
text_replace (2×) |
Hapus satu field, sintesis kertas pengganti dari cincin di sekitarnya, gambar ulang teks berbeda. 35% kemungkinan memakai sibling font | Serangan yang benar-benar penting: ganti nama, nilai, tahun, nomor |
copy_move |
Duplikasi satu region ke tempat lain di halaman yang sama (skala ±8%, rotasi ±2.5°) | Sumber & target berbagi riwayat noise/JPEG yang sama — menuntut deteksi korelasi, bukan sekadar anomali |
splice |
Tempel region dari dokumen lain, 50% dengan pencocokan tone | Komposisi lintas dokumen; pemalsu yang teliti akan menyamakan warna |
font_substitution |
String yang sama, face berbeda dan/atau tracking diubah | Kelas paling halus — menguji penalaran konsistensi tipografi, bukan artefak piksel |
local_recompress |
Encode ulang satu region pada kualitas JPEG lebih rendah | Jejak kompresi ganda tanpa perubahan semantik — mendeteksi "region ini pernah disentuh" |
Kenapa text_replace dibobot 2×: pada ijazah, serangan yang punya nilai
ekonomi hampir selalu penggantian teks. Copy-move dan splicing lebih umum di
literatur forensik citra alam, tapi lebih jarang dipakai pada dokumen.
Detail inpainting. _background() tidak mengisi kotak dengan warna solid —
itu akan jadi artefak yang bisa "dihafal" model sebagai jalan pintas. Sebaliknya
ia mengambil cincin piksel di sekeliling field, memakai persentil ke-70
kecerahannya sebagai warna kertas, lalu menambahkan noise gaussian yang sepadan.
Diterapkan setelah pemalsuan, karena begitulah urutannya di dunia nyata:
dokumen dipalsukan, lalu dicetak/di-scan/difoto. Setiap fungsi menerima dan
mengembalikan (citra, mask) — langkah geometrik mentransportasikan mask
dengan resampling nearest-neighbour, dan mask di-binerisasi ulang di akhir.
| Pipeline | Rantai | Mensimulasikan |
|---|---|---|
clean |
JPEG tunggal | PDF/ekspor digital langsung |
scan |
downscale→upscale, kontras, noise sensor, JPEG ×2 | Cetak lalu scan; kehilangan resolusi + kompresi berulang |
phone |
warp perspektif, gradien cahaya, blur, noise, JPEG | Foto kamera HP: miring, satu sumber cahaya, fokus tidak sempurna |
recompressed |
JPEG ×3 | Dokumen yang beredar lewat WhatsApp/email berkali-kali |
Kondisi tangkap adalah bagian tersulit dari masalah ini: kompresi dan resampling
menghapus persis jejak forensik yang ingin dideteksi model. Sebuah detektor yang
hanya bagus pada clean tidak berguna di lapangan.
Ditulis ke splits.json:
| Split | Isi | Fungsi |
|---|---|---|
train / val / test |
In-distribution, dari 3 family non-holdout | Pengembangan & metrik utama |
test_cross_source |
Seluruh sampel family yang di-holdout (default sertifikat_lsp) |
Generalisasi ke layout, font, dan ukuran halaman yang tidak pernah dilihat saat latih |
test_genai |
Kosong di M0 | Ketahanan terhadap pemalsuan generatif (inpainting/diffusion) |
Penugasan split adalah hash dari indeks sampel, bukan pengacakan sekali
jalan. Konsekuensinya: sampel tetap berada di split yang sama ketika
n_samples diubah — kamu bisa menaikkan ukuran dataset tanpa mencemari test set
dengan sampel yang dulu ada di train. Ini diuji
(test_split_assignment_is_stable_across_dataset_size).
test_cross_source — family sertifikat_lsp,
lanskap, layout dan font yang tidak ada di training.
Warning
test_genai sengaja dibiarkan kosong di M0. Tidak ada model generatif di
pipeline saat ini, dan mengisinya dengan pemalsuan buatan sendiri yang
disebut "GenAI" akan menghasilkan angka robustness yang menyesatkan. Letakkan
berkas di ml/data/genai_dropin/ untuk di-ingest pada M2. Jangan laporkan
angka ketahanan GenAI sebelum split ini terisi.
- Deteksi (level dokumen): Accuracy, Precision, Recall, F1, AUC-ROC
- Lokalisasi (level piksel): IoU, F1-piksel
- Robustness: delta metrik per pipeline augmentasi dan pada
test_cross_source - Per-serangan: rincian recall untuk tiap operasi — angka agregat menyembunyikan
kelemahan pada kelas halus seperti
font_substitution
Gate M2: F1 ≥ 0.90 pada test in-distribution dan laporan jujur penurunan pada cross-source & GenAI. Fase 2 tidak dimulai sebelum keduanya terpenuhi.
Tiga jaminan, semuanya ditegakkan lewat test:
- RNG per sampel. Setiap sampel menurunkan generatornya sendiri dari
SeedSequence([seed, index]). Sampel ke-i byte-identik terlepas dari ukuran dataset atau urutan pembangkitan. Tidak ada state global yang bocor antar sampel. - Config ikut disimpan. Setiap run menyalin config yang sudah di-resolve ke
config.used.yamldi sebelah data. Sebuah dataset selalu bisa dilacak balik ke setelan persis yang menghasilkannya. - Split stabil. Berbasis hash indeks, bukan shuffle — lihat bagian di atas.
# dua perintah ini menghasilkan berkas yang identik bit demi bit
.venv/bin/python -m ml.src.forge.generate --seed 1337 --n 50 --out /tmp/a
.venv/bin/python -m ml.src.forge.generate --seed 1337 --n 50 --out /tmp/b
diff -r /tmp/a/images /tmp/b/images && echo "identik"Note
Satu sumber non-determinisme lintas mesin: kumpulan font sistem. Generator
memakai font yang tersedia di host (macOS /System/Library/Fonts/Supplemental,
Linux DejaVu/Liberation). Mesin dengan font lebih sedikit menghasilkan
keragaman tipografi lebih rendah, dan font_substitution bisa merosot jadi
perubahan tracking saja. Catat kumpulan font kamu saat membandingkan run
antar-mesin.
ml/configs/forge_default.yaml — semua yang
bisa disetel:
seed: 1337
n_samples: 400
out_dir: ml/datasets/nevu_v0
forged_frac: 0.5 # proporsi sampel yang dipalsukan
cross_source_families: # family yang di-holdout sepenuhnya
- sertifikat_lsp
cross_source_frac: 0.2 # seberapa sering menarik dari family holdout
val_frac: 0.15
test_frac: 0.15
op_weights: # bobot relatif, dinormalisasi otomatis
text_replace: 2.0 # serangan yang paling penting
copy_move: 1.0
splice: 1.0
font_substitution: 1.0
local_recompress: 0.6
aug_weights:
clean: 1.0
scan: 1.2
phone: 1.2
recompressed: 0.8
max_side: 1000 # sisi terpanjang setelah resize
save_quality: 92Override lewat CLI tanpa mengedit berkas:
.venv/bin/python -m ml.src.forge.generate --n 5000 --seed 42 --out ml/datasets/nevu_v1| Flag | Fungsi |
|---|---|
--config |
Path config YAML (default ml/configs/forge_default.yaml) |
--n |
Override n_samples |
--seed |
Override seed |
--out |
Override out_dir |
Untuk training M1 naikkan n_samples secara signifikan — 400 hanya cukup
untuk memvalidasi pipeline. Sekitar 5.000 sampel butuh ±25 menit satu proses.
.venv/bin/python -m pytest ml/tests -q # 20 passed, ±25 detikTest-nya bukan basa-basi — mereka menjaga dua properti yang menjadi tumpuan seluruh proyek:
| Test | Menjaga |
|---|---|
test_op_changes_image_and_mask_covers_change |
Mask ⊇ piksel yang berubah, per operasi, kebocoran < 1%. Ini kontrak ground truth lokalisasi |
test_sample_is_deterministic_by_index |
Sampel ke-i identik pada seed yang sama |
test_template_is_deterministic |
Renderer tidak punya state tersembunyi |
test_split_assignment_is_stable_across_dataset_size |
Menambah data tidak mencemari test set |
test_held_out_family_only_appears_in_cross_source_split |
Kebocoran cross-source mustahil secara konstruksi |
test_mask_is_binary_after_augmentation |
Mask tetap {0,255} melalui semua pipeline geometrik |
test_clean_document_has_empty_mask |
Sampel asli tidak punya mask palsu-positif |
test_no_pii_in_corpus_meta |
Klaim "tidak ada data pribadi" terperiksa secara mekanis |
credential-forgery-detection/
├── ml/ # FASE 1 — inti
│ ├── configs/
│ │ └── forge_default.yaml # config eksperimen berversi
│ ├── data/
│ │ ├── dataset_card.md # asal, komposisi, batasan, etika
│ │ └── genai_dropin/ # slot sampel GenAI (diisi M2)
│ ├── datasets/ # hasil generate — gitignored
│ ├── src/forge/
│ │ ├── corpus.py # kosakata Indonesia sintetis (audit "no PII")
│ │ ├── fonts.py # resolusi font + pemilihan sibling face
│ │ ├── render.py # primitif gambar: kertas, border, stempel, teks ber-tracking
│ │ ├── templates.py # 4 template family
│ │ ├── ops.py # 5 operasi pemalsuan + mask
│ │ ├── augment.py # 4 pipeline kondisi tangkap
│ │ ├── generate.py # orkestrasi CLI + penulisan split
│ │ └── viz.py # contact sheet dengan overlay mask
│ ├── benchmark/ # M1: skrip eval + leaderboard.md
│ ├── tests/ # 20 test
│ └── requirements.txt
├── app/ # M3–M5: FastAPI backend + Next.js frontend
├── chain/ # M4: NevuRegistry.sol + deploy testnet
├── docs/ # aset README
└── README.md
Setiap modul di src/forge/ berkisar 70–210 baris dan punya satu tanggung jawab.
Keputusan non-obvious ditandai komentar # why: di kode.
Backbone (EfficientNet/ResNet via timm) di atas jejak forensik (ELA, noise
residual, filter SRM) untuk klasifikasi level dokumen; lalu segmentation head
(U-Net/FPN) untuk heatmap piksel. Baseline pembanding: ELA + classifier klasik,
CNN vanilla. Keluaran: benchmark/leaderboard.md berisi angka nyata.
Cabang teks: OCR Bahasa Indonesia (PaddleOCR/Tesseract) → fitur konsistensi
tipografi, alignment baris, validitas format nomor/tanggal → fusi dengan cabang
visual. Ablation (visual-only / teks-only / fusi) untuk membuktikan tiap komponen
membayar biayanya. Uji robustness + ingest test_genai. Gate: F1 ≥ 0.90
in-distribution + laporan jujur cross-source & GenAI.
FastAPI POST /predict memuat model terekspor (TorchScript/ONNX) →
{ score, heatmap_png_base64, ocr_fields, verdict_hint }.
NevuRegistry.sol di EVM testnet (Base Sepolia / Polygon Amoy, swappable via
env):
function anchor(bytes32 docHash, bytes32 metaHash) external;
function revoke(bytes32 docHash) external;
function getStatus(bytes32 docHash) external view
returns (bool exists, bool revoked, uint256 ts, address issuer);
event Anchored(bytes32 indexed docHash, address indexed issuer, uint256 ts);
event Revoked(bytes32 indexed docHash, uint256 ts);Append-only, hanya hash — tidak ada data pribadi on-chain. Gas dibayar akun relayer aplikasi, sehingga pengguna tidak perlu punya kripto atau wallet.
Next.js: upload → skor + overlay heatmap + field OCR → "Amankan ke blockchain" →
Verify Page publik tanpa login (paste hash atau scan QR). docker compose up
menyalakan Postgres + backend + frontend. Target: clone segar sampai demo jalan
dalam 60 detik.
Mainnet produksi · verifikasi fitur fisik (hologram, mikroprint, UV) · integrasi resmi PDDikti/BNSP (hanya disediakan hook) · skalabilitas enterprise.
Bagian ini sengaja ditulis lebih panjang daripada bagian klaim.
Deteksi bersifat probabilistik, bukan penentuan. Keluaran model adalah skor dan heatmap. Ia mempersempit tumpukan dokumen yang perlu diperiksa manusia. Ia bukan bukti hukum, dan UI tidak boleh menampilkannya sebagai cap "ASLI"/"PALSU".
Dataset masih sepenuhnya sintetis. Dokumen yang dirender bukan kertas yang
difoto. Scan nyata membawa halftone, tekstur serat, rembesan tinta, dan dithering
printer yang hanya didekati oleh kertas prosedural kami. Angka pada nevu_v0
adalah batas atas dan tidak boleh dikutip sebagai akurasi lapangan.
Mask berbentuk kotak. Mask adalah bounding box region yang diedit, bukan mask ketat pada level glyph. IoU piksel karena itu diukur terhadap target yang sedikit longgar; perlakukan sebagai perkiraan.
Kumpulan font tergantung host. Lihat catatan di Reproduktibilitas.
Arms race dengan GenAI. Pemalsuan berbasis diffusion berkembang lebih cepat
daripada detektor. Split test_genai ada supaya penurunan performa terlihat,
bukan tersembunyi di balik angka agregat. Model apa pun yang dilatih di sini akan
menua dan perlu dilatih ulang.
Model bisa salah ke dua arah. False positive menuduh pemegang ijazah asli; false negative meloloskan yang palsu. Keduanya punya biaya nyata dan tidak simetris tergantung pemakaian. Threshold operasional harus dipilih per konteks, bukan ditetapkan sekali di kode.
Blockchain tidak memverifikasi kebenaran. Anchoring membuktikan sebuah hash tercatat pada waktu tertentu oleh alamat tertentu. Ia tidak membuktikan dokumen itu benar — hanya bahwa ia dicatat. Kualitas jaminan sepenuhnya bergantung pada siapa yang berhak melakukan anchor.
Kebenaran akademik faktual di luar cakupan. Apakah orang ini benar kuliah di sana adalah pertanyaan untuk registry institusi (hook cross-check), bukan untuk model forensik citra.
Research-only. Generator ini ada untuk melatih detektor. Ia sengaja tidak memuat teknik dan penyetelan kualitas yang akan membuat keluarannya berguna untuk benar-benar menipu verifier manusia. Isu, PR, dan diskusi yang mengarah ke peningkatan kualitas pemalsuan di luar kebutuhan deteksi tidak akan dilayani.
Tidak ada data pribadi. Semua nama, institusi, nomor, dan tanggal adalah
rekaan. Seluruh kosakata ada di ml/src/forge/corpus.py
dan bisa diperiksa siapa pun. Nama institusi tidak berkorespondensi dengan
universitas atau LSP nyata mana pun di Indonesia. Klaim ini ditegakkan oleh test.
UU PDP No. 27/2022. Tidak ada data pribadi yang ditulis on-chain — hanya hash dokumen. Metadata off-chain disimpan di Postgres di bawah kendali operator, bukan di ledger publik yang tidak bisa dihapus.
Artefak tidak diredistribusi. Citra hasil generate ada di .gitignore.
Bangkitkan ulang dari config, jangan sebarkan berkasnya.
Lisensi: research-only sampai ditentukan lain. Lihat
ml/data/dataset_card.md §6.
Kenapa ML dulu, blockchain belakangan? Karena blockchain adalah bagian yang mudah. Anchoring hash ke smart contract adalah pekerjaan beberapa hari dan sudah dipecahkan berkali-kali. Yang belum dipecahkan — dan yang menentukan apakah proyek ini berguna — adalah mendeteksi manipulasi pada citra dokumen legacy. Membangun dApp lebih dulu akan menghasilkan demo yang mengesankan di atas model yang tidak berfungsi.
Kenapa dataset sintetis, bukan dokumen nyata? Ijazah nyata memuat data pribadi. Mengumpulkan dan merilisnya melanggar UU PDP, dan tanpa rilis tidak ada reproduktibilitas. Sintesis juga memberi ground-truth mask yang persis — sesuatu yang mustahil didapat dari dokumen nyata tanpa pelabelan manual yang mahal dan tetap perkiraan. Konsekuensinya (gap sintetis → nyata) dicatat terbuka di Batasan.
Kenapa skor ML tidak disimpan on-chain? Karena blockchain bersifat permanen dan skor ML bersifat sementara. Model versi berikutnya akan memberi skor berbeda untuk dokumen yang sama. Menuliskan probabilitas ke ledger yang tidak bisa diubah membekukan ketidakpastian menjadi klaim, dan tidak ada cara memperbaikinya nanti.
Apakah ini bisa dipakai produksi sekarang? Belum. M0 baru menghasilkan dataset. Tidak ada model terlatih, tidak ada API, tidak ada kontrak. Ikuti tabel Status proyek.
Bisa dipakai untuk jenis dokumen lain?
Arsitekturnya umum — tambahkan template family di
templates.py dan operasi pemalsuan mengikuti
otomatis. Yang spesifik Indonesia hanya kosakata dan format nomor.
Butuh GPU? Tidak untuk M0 — generator berjalan di CPU. M1 dan seterusnya butuh GPU untuk training dalam waktu wajar.