← Kembali ke Blog

Quantize GGUF Sendiri: Pilih Q4, Q5, atau Q6 Pakai Angka, Bukan Feeling

Kamu punya fine-tune 7B yang belum ada GGUF publiknya. GPU-nya satu, 12 GB. Q4_K_M muat dengan lega, Q6_K cuma muat di context pendek, dan semua tutorial di internet bilang hal yang sama: pake Q4_K_M.

Saran itu masuk akal buat model chat. Salah buat pipeline RAG dengan context 32k, karena KV cache bisa makan memori lebih besar dari bobot modelnya sendiri. Dan itu juga tebakan soal domain kamu, karena orang yang bikin quant tersebut kalibrasi di Wikitext, bukan di data kamu.

Panduan ini jalan dari checkpoint Hugging Face sampai beberapa file quantized, lalu ngukur mana yang benar-benar bikin kualitas turun. Semua command diambil dari dokumentasi llama.cpp versi sekarang, dan angka pembandingnya dari scoreboard perplexity resmi proyek itu.

Yang kamu butuhin

  • cmake 3.14 ke atas, compiler C++, git
  • Python 3.10+ di venv, buat script konversi
  • Model dalam format safetensors, folder lokal atau repo id HF
  • Disk sekitar 2.5x ukuran fp16, karena kamu nyimpen GGUF sumber plus tiap hasil quant
  • Teks polos 1 MB sampai 10 MB buat kalibrasi

Langkah 1: build llama.cpp

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp

# pilih satu backend
cmake -B build -DGGML_CUDA=ON       # NVIDIA
# cmake -B build -DGGML_METAL=ON    # Apple Silicon, default nyala di macOS
# cmake -B build -DGGML_VULKAN=ON   # AMD atau Intel di Linux

cmake --build build --config Release -j"$(nproc)"

Binary-nya muncul di build/bin. Yang dipakai di sini: llama-quantize, llama-imatrix, llama-perplexity, llama-bench, dan llama-server. Build terbaru juga ngasih satu binary router llama di mana llama serve dan llama cli manggil program yang sama, jadi dua-duanya jalan.

Langkah 2: konversi ke GGUF bf16

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

python convert_hf_to_gguf.py --outtype bf16 \
  --outfile ./models/mistral-7b-bf16.gguf \
  ./hf/mistral-7b

Kalau bobotnya ada di Hub, hapus path lokal dan pake --remote org/nama-model. Sumbernya harus bf16 atau f16, jangan file yang sudah di-quantize. Requantize dari Q4 hasilnya Q4 yang lebih jelek, karena kerusakan dari pass pertama sudah nempel.

transformers yang dipin script konversi masih versi 4, sementara arsitektur baru minta versi 5. Kalau konversi mati di arsitektur yang gak dikenal, pip install -U transformers lalu ulang.

Langkah 3: kumpulin teks kalibrasi dari korpus kamu

Ini langkah yang sering dilewat, padahal cuma ini yang bikin quant kamu beda dari download publik.

mkdir -p calib
pdftotext -layout docs/*.pdf calib/raw.txt
# atau, buat agent yang banyak log:
# cat /var/log/myapp/*.log > calib/raw.txt

wc -c calib/raw.txt

Satu sampai sepuluh megabyte sudah cukup. Importance matrix nyatet seberapa kuat tiap bobot ngaruh ke aktivasi output buat teks yang kamu kasih, jadi kalibrasi pakai dokumentasi, tiket, atau SQL jauh lebih relevan daripada pakai artikel ensiklopedia kalau itu yang kamu layani.

Sisihin sebagian teks yang sama sebagai file uji buat langkah 6. Jangan ukur perplexity di teks yang persis sama dengan data kalibrasi.

Satu catatan jujur sebelum kamu habiskan sehari buat ngurus korpus: scoreboard llama.cpp sendiri ngetes matrix dari 1k, 10k, 100k, 1m, dan 10m token Wikitext, dan hasilnya gak konsisten membaik seiring korpus membesar. Yang penting domainnya cocok. Ukuran di atas beberapa megabyte biasanya gak ngaruh.

Langkah 4: bikin importance matrix

./build/bin/llama-imatrix \
  -m ./models/mistral-7b-bf16.gguf \
  -f calib/raw.txt \
  -o imatrix.gguf \
  -ngl 99

-ngl 99 mindahin semua layer ke GPU, bedanya antara beberapa menit dan sejam. Output defaultnya format GGUF. Tambah --no-ppl kalau gak butuh log perplexity yang dia cetak sambil jalan.

Langkah 5: quantize beberapa level

Jangan bikin satu file lalu berharap. Bikin tiga, terus ukur.

for q in Q4_K_M Q5_K_M Q6_K; do
  ./build/bin/llama-quantize --imatrix imatrix.gguf \
    ./models/mistral-7b-bf16.gguf \
    ./models/mistral-7b-$q.gguf \
    $q
done

Kalau mau resep campuran, flag per-tensor di sinilah tempat kualitas ekstranya. Output embedding dan token embedding murah buat disimpan di presisi lebih tinggi karena porsinya kecil dari total bobot:

./build/bin/llama-quantize --imatrix imatrix.gguf \
  --output-tensor-type q6_k \
  --token-embedding-type q4_k \
  ./models/mistral-7b-bf16.gguf \
  ./models/mistral-7b-mix.gguf \
  Q4_K_M

Dua flag yang perlu kamu tahu: --pure matiin pencampuran k-quant jadi semua tensor dapet tipe yang sama, dan --allow-requantize ngizinin quantize ulang file yang sudah quantized, yang justru diperingatin dokumentasinya karena alasan kualitas.

Langkah 6: ukur kerusakannya

Perplexity dulu, di file uji:

./build/bin/llama-perplexity -m ./models/mistral-7b-bf16.gguf -f calib/test.txt
./build/bin/llama-perplexity -m ./models/mistral-7b-Q4_K_M.gguf -f calib/test.txt

Perplexity sendirian tumpul. KL divergence ngasih tahu seberapa jauh distribusi token model quantized geser dari fp16, dan itu angka yang layak dibandingin. Rekam logit fp16 sekali, lalu pake ulang file-nya:

./build/bin/llama-perplexity -m ./models/mistral-7b-bf16.gguf \
  -f calib/test.txt --kl-divergence-base base.kld

./build/bin/llama-perplexity -m ./models/mistral-7b-Q4_K_M.gguf \
  -f calib/test.txt --kl-divergence-base base.kld --kl-divergence

File logit-nya besar. Dokumentasi nyebut 11 GiB buat model LLaMA 2 dan 37 GiB buat LLaMA 3 dengan test set Wikitext-2, jadi cek disk dulu sebelum jalanin.

Buat pembanding, ini scoreboard resmi llama.cpp untuk Llama 3 8B di satu RTX 4090 dengan backend CUDA. Ukuran dalam GiB, perplexity, dan rata-rata KL divergence terhadap fp16:

  • f16, 14.97 GiB, PPL 6.233 (referensi)
  • q8_0, 7.96 GiB, PPL 6.234, KLD 0.001355
  • q6_K, 6.14 GiB, PPL 6.253, KLD 0.005452
  • q5_K_M, 5.33 GiB, PPL 6.289, KLD 0.010762
  • q4_K_M pakai imatrix, 4.58 GiB, PPL 6.383, KLD 0.028152
  • q4_K_M tanpa imatrix, 4.58 GiB, PPL 6.407, KLD 0.031273
  • q3_K_M pakai imatrix, 3.74 GiB, PPL 6.734, KLD 0.084358
  • q3_K_M tanpa imatrix, 3.74 GiB, PPL 6.889, KLD 0.101913
  • q2_K pakai imatrix, 2.96 GiB, PPL 8.648, KLD 0.332223
  • q2_K tanpa imatrix, 2.96 GiB, PPL 9.752, KLD 0.445132
  • iq1_S pakai imatrix, 1.88 GiB, PPL 58.1, KLD 2.211

Tiga hal langsung kelihatan dari tabel itu.

Dari q6_K turun ke q4_K_M, KL divergence naik sekitar lima kali sementara perplexity cuma geser dari 6.253 ke 6.383. Perubahannya nyata tapi kecil, dan itu sebabnya Q4_K_M jadi default.

Di bawah 4 bit per bobot kurvanya berbalik. q3_K_M menggandakan divergence lagi, dan q2_K sudah bukan model bahasa yang layak dirilis, dengan perplexity 8.6 lawan 6.2.

Imatrix berguna justru saat bit-nya sempit: KLD turun dari 0.0313 ke 0.0282 di q4_K_M, dari 0.1019 ke 0.0844 di q3_K_M, dan dari 0.445 ke 0.332 di q2_K. Di q5 dan q6 keuntungannya masuk kategori noise.

Peringatan waktu baca angka ini: perplexity gak bisa dibandingin antar model dengan tokenizer berbeda, dan fine-tune biasanya skornya lebih tinggi dari base model-nya meski jawabannya lebih bagus. Pakai angka ini buat bandingin quant dari model yang sama, gak lebih.

Langkah 7: hitung lawan RAM kamu

Bobot cuma sebagian tagihan. KV cache ikut panjang context dan, buat model dengan grouped query attention, lebih murah dari dugaan banyak orang:

byte per token = 2 (K dan V) x jumlah layer x kv_heads x head_dim x byte per elemen

Buat Llama 3 8B dengan KV fp16: 2 x 32 x 8 x 128 x 2 = 131.072 byte, atau 128 KiB per token. Di context 8k itu 1 GiB. Di 32k jadi 4 GiB, hampir sebesar bobot q4-nya.

Jadi anggarannya begini, dengan jatah 1 GiB buat buffer komputasi dan fragmentasi:

  • Mesin 16 GB, context 8k: Q5_K_M 5.33 GiB plus KV 1 GiB plus overhead 1 GiB mendarat di sekitar 7.3 GiB. Semua muat, dan turun ke Q3 gak ngasih apa pun yang kamu butuh.
  • Mesin 8 GB, context 4k: Q4_K_M 4.58 GiB plus KV 0.5 GiB plus overhead 0.75 GiB sekitar 5.8 GiB. Ketat tapi jalan.
  • GPU 12 GB dengan context 32k: Q4_K_M plus KV cache 4 GiB sudah 8.6 GiB sebelum buffer. Q3_K_M dengan imatrix jadi pilihan jujur di sini, dan sekarang kamu tahu harganya.

Langkah 8: serve dan cek throughput

./build/bin/llama-bench -m ./models/mistral-7b-Q4_K_M.gguf -p 512 -n 128

./build/bin/llama serve -m ./models/mistral-7b-Q4_K_M.gguf \
  --host 127.0.0.1 --port 8080 \
  -c 8192 -ngl 99

llama serve buka endpoint kompatibel OpenAI di /v1/chat/completions plus UI browser, jadi apa pun yang ngomong API OpenAI bisa diarahkan ke situ tanpa ubah kode.

Throughput adalah sisi lain dari tradeoff ini. Di scoreboard Llama 3 8B yang sama, generasi teks 128 token jalan di 76.7 t/s buat Q4_K_S, 50.9 t/s buat Q8_0, dan 29.2 t/s buat f16 di 4090 itu. Naikin presisi motong kecepatan generasi sepertiga dibanding Q4. Apakah 0.0014 KL divergence layak dibayar segitu tergantung pekerjaan kamu, dan sekarang itu keputusan, bukan default.

Kapan cara ini justru salah

Kalau kamu cuma mau jalanin model yang sudah ada quant publiknya, download saja. Unsloth dan Bartowski punya quant buat hampir semua model populer, dan Q4_K_M dari mereka bakal ngalahin percobaan pertama kamu.

Kalau gak ada GPU buat langkah imatrix, Space GGUF-my-repo di Hugging Face bikin quant gratis dan sync dari main llama.cpp tiap enam jam.

Kalau kamu mau manajemen siklus model, Ollama ngasih pull, run, dan serve dalam satu command, dengan konsekuensi kamu gak ngatur campuran tensor-nya.

Bikin quant sendiri masuk akal kalau modelnya fine-tune tanpa GGUF publik, kalau kamu butuh bits-per-weight tertentu buat muat di kartu tertentu, atau kalau korpus kalibrasinya justru inti masalahnya.

Setelah ini

Pilih KLD terkecil yang masih kamu terima, bukan file terkecil. Jalanin pipeline-nya sekali, catat tiga angka (GiB di disk, rata-rata KLD, token per detik), lalu berhenti nebak dari nama quant.

Simpan file imatrix-nya. Ukurannya kecil, bisa dipake ulang buat semua quant berikutnya dari base model yang sama, dan bikin ulang itu bagian yang lambat.

Referensi

Butuh Bantuan Implementasi?

Saya membantu tim mendesain dan membangun infrastruktur cloud scalable, pipeline DevOps, dan sistem production-grade.

Konsultasi Gratis