| --- |
| 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. |
|
|