indigo / README.md
adyoi's picture
docs: komentar inline + README argumen + bugfix & optimasi (7dd92fc)
9a82835 verified
|
Raw
History Blame Contribute Delete
16.4 kB
---
language:
- id
license: mit
tags:
- text-generation
- from-scratch
- gpt
- transformer
- indonesian
datasets:
- custom
---
# Indigo
Model bahasa kecil GPT-style yang dibangun **dari nol** (tanpa library transformers) sebagai proyek pembelajaran. Backend **PyTorch**; tersedia juga varian [TensorFlow/Keras terpisah](https://huggingface.co/adyoi/indigo-tf). Bobot disimpan dalam format aman `.safetensors`.
## Arsitektur
| Komponen | Nilai default |
|---|---|
| Tipe | Decoder-only transformer (pre-LN, SDPA) |
| Parameter | ~0.81M (default) |
| Layer / Head | 4 / 4 |
| Dimensi embedding | 128 |
| Konteks (block_size) | 128 token |
| Tokenizer | `char` (karakter) atau `bpe` (byte-pair encoding) |
| Weight tying | Ya (output head = token embedding) |
| Aktivasi | GELU (MLP) |
| Attention | Multi-head causal self-attention + KV-cache |
## Struktur file
```
indigo/
model.py Arsitektur GPT (CausalSelfAttention, MLP, Block, GPT, sampling)
common.py Utilitas bersama (clean_text, meta save/load, morphological analysis)
bpe.py Tokenizer BPE level-byte minimal
tokenizer.py Tokenizer karakter (char-level)
train.py Training loop (best-checkpoint, resume, split val, cosine LR schedule)
generate.py Generasi teks (top-k, top-p, repetition penalty, guard kamus)
pipeline.py Pipeline end-to-end (data collection β†’ training β†’ manifest β†’ finalize/push)
eval.py Evaluasi batched antar-checkpoint (nats/token, nats/karakter)
audit_kamus.py Audit wordlist kamus terhadap KBBI daring
out/
indigo_best.safetensors Bobot terbaik (dipilih berdasarkan validasi)
indigo_best_meta.json Metadata terbaik
indigo.safetensors Bobot checkpoint akhir
indigo_meta.json Metadata akhir
indigo_optimizer.pt State optimizer (untuk resume)
run_info.json Info run terakhir yang di-finalize
data/
sample.txt Teks contoh Indonesia (training default)
kamus_id.txt Wordlist Indonesia (~70K kata, gitignored)
prefiks.txt Daftar prefiks Indonesia untuk morphological guard
sufixs.txt Daftar sufiks Indonesia untuk morphological guard
```
## Persyaratan
```
torch>=2.0
safetensors>=0.4
```
Install:
```bash
pip install -r requirements.txt
```
---
## `train.py` β€” Training Model
Script utama untuk melatih model GPT dari nol.
### Argumen
| Argumen | Tipe | Default | Deskripsi |
|---|---|---|---|
| `--data` | str (nargs+) | `data/sample.txt` | Path file/folder teks untuk training. Bisa banyak, spasi-separated. Folder akan dicari rekursif untuk file `.txt`. |
| `--out` | str | `out` | Folder output checkpoint (`.safetensors` + `_meta.json` + `_optimizer.pt`). |
| `--steps` | int | `2000` | Jumlah total langkah training. |
| `--batch-size` | int | `32` | Jumlah sampel per batch. Batch lebih besar β†’ gradient lebih stabil tapi butuh lebih banyak VRAM. |
| `--block-size` | int | `128` | Panjang konteks token per sampel (sequence length). Semakin besar β†’ model bisa lihat lebih jauh tapi butuh lebih banyak memori. |
| `--lr` | float | `3e-4` | Learning rate maksimum. Cosine decay dari lr ini ke 10% lr selama training. |
| `--warmup` | int | `100` | Jumlah langkah warmup linear sebelum cosine decay dimulai. |
| `--weight-decay` | float | `0.1` | L2 regularization / weight decay untuk AdamW optimizer. |
| `--dropout` | float | `0.1` | Dropout rate (0.0 = nonaktif). Membantu mencegah overfitting pada data kecil. |
| `--n-layer` | int | `4` | Jumlah blok transformer bertumpuk. Lebih banyak layer β†’ model lebih dalam tapi lebih lambat. |
| `--n-head` | int | `4` | Jumlah head per attention layer. Harus habis membagi `--n-embd`. |
| `--n-embd` | int | `128` | Dimensi embedding / hidden size. Parameter β‰ˆ 12 Γ— n_layer Γ— n_embdΒ². |
| `--tokenizer` | `char`/`bpe` | `char` | Jenis tokenizer. `char` = cepat, `bpe` = lebih efisien untuk teks panjang. |
| `--vocab-size` | int | `512` | Ukuran vocab untuk BPE (diabaikan jika `--tokenizer char`). |
| `--eval-interval` | int | `200` | Evaluasi validasi setiap N langkah. Set `0` untuk skip validasi. |
| `--eval-iters` | int | `20` | Jumlah batch untuk estimasi loss validasi. |
| `--seed` | int | `1337` | Seed random untuk reproduktibilitas. |
| `--val-fraction` | float | `0.1` | Proporsi file untuk validasi (split per-file, bukan per-karakter). |
| `--init-from` | str | `None` | Path checkpoint untuk melanjutkan training (resume). Muat model + optimizer + step. |
| `--device` | `auto`/`cpu`/`cuda` | `auto` | Device training. `auto` = CUDA jika tersedia, else CPU. |
### Contoh
```bash
# Training dasar
python train.py --data data/sample.txt --steps 2000
# Training dengan BPE tokenizer
python train.py --data data/teks.txt --tokenizer bpe --vocab-size 512
# Model lebih besar, training lebih lama
python train.py --data data/ --n-layer 6 --n-head 8 --n-embd 256 --steps 5000
# Resume dari checkpoint
python train.py --init-from out/indigo_best.safetensors --steps 1000
# Tanpa validasi (data hanya 1 file)
python train.py --data data/combined.txt --val-fraction 0 --eval-interval 0
```
### Output
- `out/indigo_best.safetensors` + `out/indigo_best_meta.json` β€” checkpoint terbaik (val loss minimum)
- `out/indigo.safetensors` + `out/indigo_meta.json` β€” checkpoint akhir
- `out/indigo_optimizer.pt` β€” state optimizer (untuk resume)
### Learning Rate Schedule
```
Step 0 β†’ warmup: linear dari 0 β†’ lr_max
Step warmup β†’ total: cosine decay dari lr_max β†’ 0.1 Γ— lr_max
```
Formula cosine: `0.1 Γ— lr + 0.45 Γ— lr Γ— (1 + cos(Ο€ Γ— progress))`
---
## `generate.py` β€” Generasi Teks
Generate teks dari checkpoint Indigo dengan berbagai opsi sampling.
### Argumen
| Argumen | Tipe | Default | Deskripsi |
|---|---|---|---|
| `--ckpt` | str | `out/indigo_best.safetensors` | Path ke file checkpoint model. |
| `--prompt` | str | `""` | Teks awal (prompt) untuk memulai generasi. |
| `--max-new` | int | `300` | Jumlah token baru yang akan dihasilkan (bukan panjang total). |
| `--temperature` | float | `0.8` | Skala randomness: `0.0` β‰ˆ greedy, `0.8` β‰ˆ standar, `>1.0` β‰ˆ random. |
| `--top-k` | int | `40` | Batasi sampling ke k token teratas. `0` = nonaktif. |
| `--top-p` | float | `1.0` | Nucleus sampling: batasi kumulatif probabilitas. `1.0` = nonaktif. |
| `--repetition-penalty` | float | `1.0` | Penalti pengulangan token. `1.0` = nonaktif, `>1.0` = kurangi pengulangan. |
| `--seed` | int | `None` | Seed random. `None` = tidak ditentukan (random setiap kali). |
| `--device` | `auto`/`cpu`/`cuda` | `auto` | Device untuk inferensi. |
| `--guard` | str | `None` | Path file kamus (satu kata per baris). Generate beberapa kandidat β†’ pilih yang rasio kata dikenal tertinggi. |
| `--guard-prefiks` | str | `None` | Path file prefiks Indonesia. Default: `data/prefiks.txt` bila ada. |
| `--guard-sufiks` | str | `None` | Path file sufiks Indonesia. Default: `data/sufiks.txt` bila ada. |
| `--guard-tries` | int | `5` | Jumlah kandidat generate saat `--guard` aktif. |
| `--guard-min` | float | `0.6` | Rasio kata dikenal minimum. Berhenti generate lebih awal jika tercapai. |
### Contoh
```bash
# Generasi dasar
python generate.py --prompt "Indigo" --max-new 300
# Sampling lebih random
python generate.py --prompt "hello" --temperature 1.0 --top-k 60 --top-p 0.9
# Dengan guard kamus (pilih output paling koheren)
python generate.py --prompt "kepekaan" --max-new 120 --guard data/kamus_id.txt
# Repetition penalty untuk mengurangi pengulangan
python generate.py --prompt "cerita" --repetition-penalty 1.2 --max-new 200
```
### Guard Kamus
Saat `--guard` aktif, generate.py akan:
1. Generate `--guard-tries` kandidat teks (seed berbeda tiap kandidat)
2. Untuk setiap kandidat, hitung rasio kata yang dikenal kamus
3. Pilih kandidat dengan rasio tertinggi
4. Berhenti lebih awal jika rasio >= `--guard-min`
Kata berimbuhan dicek lewat formula morfologi:
- Hapus sufiks β†’ cek akar
- Hapus prefiks β†’ cek akar (+ asimilasi: `meny-`β†’`s`, `pem-`β†’`p`)
---
## `pipeline.py` β€” Pipeline End-to-End
Rangkai semua tahap: kumpul data β†’ training β†’ evaluasi β†’ manifest β†’ finalize β†’ push.
### Argumen
| Argumen | Tipe | Default | Deskripsi |
|---|---|---|---|
| `--tag` | str | **wajib** | Nama run. Semua artefak disimpan di `runs/<tag>/`. |
| `--data` | str (nargs*) | `[]` | File/folder teks lokal tambahan (banyak, spasi-separated). |
| `--hf-dataset` | str | `None` | Repo dataset HF untuk menarik file teks (mis. `adyoi/indigo`). |
| `--hf-patterns` | str (nargs*) | `["*.txt", "*.md"]` | Pola file yang diambil dari HF. |
| `--format-qa` | flag | `False` | Auto-convert Alpaca JSON (`instruction`/`output`) ke `.txt` sebelum training. |
| `--runs` | str | `runs` | Folder root untuk semua run. |
| `--device` | str | `None` | Device training (diteruskan ke `train.py`, default: auto). |
| `--finalize` | flag | `False` | Promosikan checkpoint terbaik ke folder `out/` kanonik. |
| `--push` | flag | `False` | Upload checkpoint terbaik ke repo HF. |
| `--repo` | str | `adyoi/indigo` | Repo HF tujuan upload. |
| `--guard` | str | `None` | File kamus untuk metrik rasio ejaan di manifest. |
| `--guard-max-new` | int | `120` | Jumlah token generate untuk evaluasi guard. |
| `--guard-prefiks` | str | `None` | File prefiks Indonesia. |
| `--guard-sufiks` | str | `None` | File sufiks Indonesia. |
**Hyperparameter training** (diteruskan ke `train.py`):
| Argumen | Tipe | Default | Deskripsi |
|---|---|---|---|
| `--steps` | int | `2000` | Jumlah langkah training. |
| `--batch-size` | int | `32` | Batch size. |
| `--block-size` | int | `128` | Panjang konteks. |
| `--n-layer` | int | `4` | Jumlah layer transformer. |
| `--n-head` | int | `4` | Jumlah head attention. |
| `--n-embd` | int | `128` | Dimensi embedding. |
| `--dropout` | float | `0.1` | Dropout rate. |
| `--lr` | float | `3e-4` | Learning rate. |
| `--warmup` | int | `100` | Langkah warmup. |
| `--weight-decay` | float | `0.1` | Weight decay. |
| `--eval-interval` | int | `200` | Evaluasi setiap N langkah. |
| `--eval-iters` | int | `20` | Jumlah batch evaluasi. |
| `--seed` | int | `1337` | Seed random. |
| `--init-from` | str | `None` | Checkpoint untuk resume. |
| `--tokenizer` | `char`/`bpe` | `char` | Jenis tokenizer. |
| `--vocab-size` | int | `512` | Vocab size untuk BPE. |
| `--val-fraction` | float | `0.1` | Proporsi file validasi. |
### Contoh
```bash
# Data lokal saja
python pipeline.py --tag run01 --data data/sample.txt --steps 2000
# Data dari HF + BPE tokenizer
python pipeline.py --tag run02 --data data/teks.txt --hf-dataset adyoi/indigo \
--tokenizer bpe --vocab-size 512 --steps 900 --device cpu
# Dataset Alpaca JSON dari HF
python pipeline.py --tag qa01 \
--hf-dataset rohanrdy/CS-Theory-QA-Dataset \
--format-qa --steps 3000 --n-layer 6 --n-embd 256
# Full pipeline: train + finalize + push
python pipeline.py --tag final01 --data data/ --steps 2000 --finalize --push
# Dengan guard kamus untuk metrik kualitas
python pipeline.py --tag guarded --data data/ --steps 2000 --guard data/kamus_id.txt
```
### Output
- `runs/<tag>/data/` β€” data yang digunakan (copy dari sumber)
- `runs/<tag>/ckpt/` β€” checkpoint training
- `runs/<tag>/manifest.json` β€” statistik + metadata run (termasuk `kamus_ratio` jika `--guard` aktif)
---
## `eval.py` β€” Evaluasi Batched
Bandingkan beberapa checkpoint pada set uji tetap. Metrik:
- **nats/token**: loss rata-rata per token (cross-entropy). Lebih rendah = lebih baik.
- **nats/karakter**: loss per karakter (dikoreksi dengan compression ratio). Memungkinkan perbandingan antar tokenizer.
### Argumen
| Argumen | Tipe | Default | Deskripsi |
|---|---|---|---|
| `--ckpt` | str (nargs+) | **wajib** | Path ke satu atau lebih file checkpoint (`.safetensors`). |
| `--test` | str | `data/sample.txt` | Path ke file teks uji. |
| `--device` | `cpu`/`cuda` | `cpu` | Device untuk evaluasi. |
| `--guard` | str | `None` | Path file kamus; generate teks β†’ hitung rasio kata dikenal. |
| `--guard-max-new` | int | `120` | Jumlah token generate untuk evaluasi guard. |
| `--seed` | int | `42` | Seed untuk generate saat `--guard` aktif. |
| `--batch-size` | int | `32` | Batch size untuk evaluasi. |
### Contoh
```bash
# Evaluasi satu checkpoint
python eval.py --ckpt out/indigo_best.safetensors
# Bandingkan beberapa checkpoint
python eval.py --ckpt out/indigo_best.safetensors runs/*/ckpt/indigo.safetensors \
--test data/sample.txt
# Dengan guard kamus
python eval.py --ckpt out/indigo_best.safetensors --guard data/kampus_id.txt
```
### Contoh output
```
set uji: data/sample.txt (29,123 karakter)
checkpoint nats/tok nat/kar kamus
run/csqa500/ckpt/indigo_best.safetensors 3.962 3.962 32%
run/wiki500/ckpt/indigo_best.safetensors 3.018 3.018 25%
```
---
## `audit_kamus.py` β€” Audit Wordlist Kamus
Verifikasi kata-kata dalam file kamus terhadap KBBI daring (sampling acak).
### Argumen
| Argumen | Tipe | Default | Deskripsi |
|---|---|---|---|
| `--kamus` | str | `data/kamus_id.txt` | Path ke file kamus (satu kata per baris). |
| `--n` | int | `200` | Jumlah kata sampel yang diambil untuk verifikasi. |
| `--seed` | int | `1337` | Seed random untuk sampling. |
| `--delay` | float | `0.8` | Jeda antar-permintaan web (detik). Hindari rate limiting. |
| `--backend` | `auto`/`pypi`/`web` | `auto` | Backend verifikasi. `pypi` = library kbbi, `web` = scraping kbbi.web.id. |
| `--apply` | flag | `False` | Hapus kata TIDAK-DIKENAL dari kamus (backup otomatis ke `.bak.txt`). |
| `--out` | str | `None` | Path output laporan JSON. Default: `runs/kamus_audit_<timestamp>.json`. |
### Contoh
```bash
# Audit dasar
python audit_kamus.py --kamus data/kamus_id.txt --n 200
# Hapus kata tak-valid (backup otomatis)
python audit_kamus.py --kamus data/kamus_id.txt --apply
# Backend web saja, delay lebih lama
python audit_kamus.py --backend web --delay 1.5 --n 100
```
### Output
Laporan JSON berisi:
- `ada`: jumlah kata yang ditemukan di KBBI
- `tidak_dikenal`: jumlah kata yang tidak ditemukan
- `gagal_ceks`: jumlah verifikasi yang gagal (timeout, dll)
- `estimasi_entri_tidak_valid`: proyeksi total entri tak-valid di seluruh kamus
- `kata_ditolak`: daftar kata yang tidak dikenal (akan dihapus jika `--apply`)
---
## Guard Kamus (Sistem Kualitas Ejaan)
Sistem guard membantu menjaga kualitas output model tanpa mengubah bobot:
1. **Wordlist**: `data/kamus_id.txt` β€” ~70K kata Indonesia (satu kata per baris)
2. **Morfologi**: `data/prefiks.txt` + `data/sufiks.txt` β€” pendeteksi imbuhan
3. **Asimilasi**: `meny-`β†’`s`, `peny-`β†’`s`, `pem-`β†’`p` β€” menangani perubahan bunyi
Cara kerja:
- **generate.py**: generate beberapa kandidat β†’ pilih yang rasio kata dikenal tertinggi
- **pipeline.py**: generate sekali β†’ simpan `kamus_ratio` di manifest.json
- **eval.py**: generate sekali β†’ tampilkan rasio di tabel perbandingan
Edit `data/prefiks.txt` dan `data/sufiks.txt` untuk memperluas cakupan tanpa menyentuh kode.
---
## Format Checkpoint
| File | Isi |
|---|---|
| `*.safetensors` | Bobot model (format aman, tanpa pickle) |
| `*_meta.json` | Metadata: config, vocab, step, val_loss, backend, tokenizer info |
| `*_optimizer.pt` | State optimizer AdamW (untuk resume training) |
Format `.safetensors` tidak mengeksekusi kode saat dimuat β€” lebih aman dari format `.pt` lama.
---
## Tips Training
| Skenario | Rekomendasi |
|---|---|
| Data < 100 KB | `--tokenizer char`, 4L/4H/128E, 2000 steps |
| Data 100 KB–1 MB | `--tokenizer char` atau `bpe` (vocab 512), 4L/8H/256E, 3000+ steps |
| Data > 1 MB | `--tokenizer char` (BPE training sangat lambat di CPU >1MB) |
| CPU-only | Model max ~1M params (4L/4H/128E), 5-8 detik/step |
| Resume training | `--init-from runs/xxx/ckpt/indigo_best.safetensors` |
---
## Batasan
- Dilatih pada data sangat kecil β†’ output belum koheren; cocok untuk edukasi bukan produksi.
- Gunakan checkpoint `indigo_best` (terpilih berdasarkan validasi), bukan checkpoint akhir.
- BPE training sangat lambat di CPU untuk data > 1 MB β€” gunakan `--tokenizer char`.
## Keamanan
Bobot disimpan sebagai `.safetensors` (tanpa pickle, tidak mengeksekusi kode saat dimuat). State optimizer (`*_optimizer.pt`) hanya untuk resume lokal β€” jangan dibagikan.