File size: 16,350 Bytes
045f351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c7c5a39
045f351
 
 
9a82835
045f351
c7c5a39
9a82835
045f351
9a82835
 
 
 
 
 
045f351
9a82835
045f351
 
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
045f351
 
9a82835
045f351
 
c7c5a39
9a82835
 
 
 
 
 
 
045f351
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
045f351
 
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c7c5a39
045f351
9a82835
 
 
 
 
 
 
 
 
 
 
045f351
9a82835
045f351
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bda21c4
9a82835
bda21c4
 
9a82835
 
 
 
 
 
 
bda21c4
 
9a82835
 
bda21c4
 
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
466c3d2
9a82835
6f91ecf
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6f91ecf
 
9a82835
 
 
 
 
6f91ecf
 
9a82835
 
 
 
 
 
 
 
 
 
6f91ecf
 
9a82835
 
 
 
 
 
 
a0673e9
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0673e9
 
9a82835
 
 
 
a0673e9
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0673e9
 
9a82835
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
045f351
 
c7c5a39
 
9a82835
045f351
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
---
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.