"LoRA melatih sekitar 1% dari model" adalah penjelasan satu baris yang biasa kita dengar. Itu benar, tetapi menyembunyikan bagian-bagian yang menentukan apakah satu proses training muat di mesin Anda dan berapa lama waktunya: layer mana yang diberi adapter, apa pengaruh rank terhadap jumlahnya, dalam presisi apa weights yang dibekukan disimpan, dan berapa banyak langkah optimizer yang sebenarnya dihasilkan dataset kecil.
Berikut hitungannya untuk satu proses konkret: Qwen2.5-1.5B-Instruct, rank 16, di Mac Apple Silicon 16GB. Pengaturannya adalah nilai bawaan trainer lokal di aplikasi desktop osFoundry, yang berupa skrip biasa dengan transformers + PEFT + TRL, jadi hitungannya berlaku untuk setup serupa mana pun. (Halaman fine-tuning LoRA di aplikasi menampilkan antarmukanya.)
Satu adapter adalah dua matriks tipis
Untuk matriks weight W berukuran d_out × d_in, LoRA membekukan W dan mempelajari dua matriks, A (r × d_in) dan B (d_out × r). Output layer menjadi W·x + (α/r)·B·A·x. B dimulai dari nol, jadi training berangkat tepat dari model dasar.
Jumlah parameter yang bisa dilatih untuk layer itu adalah r × (d_in + d_out). Jumlahnya naik linear terhadap rank dan lebar layer, bukan terhadap luasnya.
Di mana adapter dipasang
Trainer memasang adapter pada ketujuh proyeksi linear di setiap blok transformer:
target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"]
Qwen2.5-1.5B punya 28 blok, hidden size 1536, ukuran MLP 8960, dan grouped-query attention dengan 2 KV head berdimensi 128, sehingga k dan v memproyeksikan turun ke 256. Per blok, pada r = 16:
| Layer | d_in → d_out | Dapat dilatih |
|---|---|---|
| q_proj | 1536 → 1536 | 49,152 |
| k_proj | 1536 → 256 | 28,672 |
| v_proj | 1536 → 256 | 28,672 |
| o_proj | 1536 → 1536 | 49,152 |
| gate_proj | 1536 → 8960 | 167,936 |
| up_proj | 1536 → 8960 | 167,936 |
| down_proj | 8960 → 1536 | 167,936 |
Totalnya 659,456 per blok, dan 18,464,768 untuk 28 blok: 18.5M parameter yang dilatih dari total 1.54B, atau 1.2%.
Ada dua hal yang menonjol. Proyeksi MLP memegang tiga perempat weight adapter, jadi kalau dilewati (seperti beberapa resep lama yang hanya mengadaptasi q dan v), jumlahnya turun menjadi sekitar 2.2M, seperdelapan dari kapasitas. Dan karena GQA membuat k dan v sempit, adapter untuk keduanya murah.
Sebagian besar memori dipakai weights yang dibekukan
Di GPU Apple, trainer memuat model dalam float32:
torch_dtype = torch.float16 if device == "cuda" else torch.float32
Half precision di MPS cenderung menghasilkan loss NaN, jadi 32-bit adalah pilihan aman di sana. Itu membuat base yang dibekukan berukuran 1.54B × 4 byte ≈ 6.2 GB.
Bagian yang dilatih kecil sekali jika dibandingkan. 18.5M weight dalam fp32 adalah 74 MB, gradiennya 74 MB lagi, dan AdamW menyimpan dua estimasi momen per weight, 148 MB lagi. Totalnya sekitar 0.3 GB. Sisa anggaran dipakai untuk aktivasi, yang skalanya mengikuti batch size dan panjang sekuens (trainer membatasi sekuens sampai 2048 token; balasan chat yang pendek jauh di bawah itu).
Dengan sekitar 12 GB dari Mac 16 GB yang bisa dipakai GPU, model 1.5B dalam fp32 masih menyisakan ruang. Base 7B dalam fp32 sudah sekitar 30 GB bahkan sebelum training dimulai. Itu sebabnya di Mac model dasarnya dijaga tetap kecil, dan itu sebabnya QLoRA (weights beku 4-bit) ada, meski jalur 4-bit dari bitsandbytes membutuhkan GPU NVIDIA, sehingga di Mac yang dipakai LoRA biasa.
Dataset kecil berarti update yang lebih sedikit dari dugaan
Trainer memakai batch size 4 dengan 4 langkah gradient accumulation, jadi setiap update optimizer melihat 16 contoh. Dengan 200 pasangan:
- 200 ÷ 4 = 50 batch forward/backward per epoch
- 50 ÷ 4 ≈ 13 update optimizer per epoch
- 3 epoch ≈ 39 update total, 10 yang pertama berupa warmup learning rate
Tiga puluh sembilan update dengan learning rate 2e-4 dan penurunan linear cukup untuk menggeser gaya bahasa, tetapi tidak cukup untuk mengajarkan pengetahuan baru. Ini sesuai dengan yang terlihat dalam praktik: beberapa ratus balasan mengubah cara model menulis, bukan apa yang diketahuinya.
Apa hasilnya
Setelah training, hanya adapter yang disimpan: A dan B untuk masing-masing dari 196 layer yang diadaptasi, puluhan megabyte. Untuk inferensi, aplikasi mengubahnya menjadi file LoRA GGUF dan llama.cpp menerapkannya di atas salinan base yang sudah dikuantisasi, di sini qwen2.5-1.5b-instruct pada Q4_K_M. Base-nya dipakai bersama, jadi beberapa adapter (satu per gaya bahasa atau tugas) masing-masing hanya memakan beberapa puluh MB.
Perhatikan ketidakcocokan presisinya: adapter dilatih terhadap weights dasar fp32 dan dijalankan terhadap weights 4-bit. Untuk adapter gaya yang kecil, perbedaannya biasanya tidak terasa, tetapi ini salah satu alasan model yang di-merge lalu dikuantisasi ulang bisa berperilaku sedikit berbeda dari adapter yang sama yang diterapkan saat runtime.



Top comments (0)