- KVSplit เป็นโปรเจ็กต์ที่มุ่งให้ LLM บน Apple Silicon ใช้ KV cache ของ attention ด้วยความละเอียดการควอนไทซ์ที่ต่างกันระหว่าง key และ value เพื่อให้รันคอนเท็กซ์ได้ยาวขึ้นและใช้โมเดลที่ใหญ่ขึ้นภายใต้งบหน่วยความจำเท่าเดิม
- ผลลัพธ์หลักคือคอนฟิก K8V4 ซึ่งที่ 8K โทเค็น ลดจาก FP16 176.00MB เหลือ 71.50MB เพิ่มความเร็วประมวลผลโทเค็นจาก 54,360 tokens/sec เป็น 57,438 tokens/sec และมีการเปลี่ยนแปลง perplexity อยู่ที่ +0.86%
- จากผลที่พบว่า key ไวต่อการควอนไทซ์มากกว่า value จึงสรุปได้ว่า K4V8 ซึ่งใช้จำนวนบิตรวมเท่ากัน มีคุณภาพลดลงมากกว่า K8V4 ราว 7 เท่า
- ฟีเจอร์ที่มีให้ได้แก่การแพตช์ llama.cpp, บิลด์พร้อมรองรับ Metal, เบนช์มาร์กหน่วยความจำ·ความเร็ว·perplexity, บันทึกผลเป็น CSV/JSON, เครื่องมือสร้างภาพ และการจับภาพการประหยัดหน่วยความจำผ่าน Activity Monitor
- คอนฟิกที่แนะนำคือ K8V4 เพื่อสมดุลระหว่างคุณภาพและการประหยัดหน่วยความจำ ส่วนถ้าต้องการลดหน่วยความจำสูงสุด ก็มีตัวเลือก K4V4 ที่ลดได้ 72% โดยยอมรับการสูญเสียคุณภาพราว 6%
ปัญหาที่ KVSplit พยายามแก้
- KVSplit เป็นโปรเจ็กต์สำหรับลดหน่วยความจำของ KV cache ระหว่างการอนุมาน LLM บน Mac ที่ใช้ Apple Silicon
- ใช้ ความละเอียดการควอนไทซ์ที่ต่างกันระหว่าง key และ value ใน KV cache ของกลไก attention
- เป้าหมายมีดังนี้
- ลดการใช้หน่วยความจำได้สูงสุด 72%
- รัน คอนเท็กซ์ที่ยาวขึ้น 2-3 เท่า ภายใต้งบหน่วยความจำเท่าเดิม
- รักษาหรือเพิ่มความเร็วการอนุมานเมื่อเทียบกับ FP16
- รองรับ Metal ที่ปรับให้เหมาะกับ Apple Silicon
ผลเบนช์มาร์กหลัก
- ผลลัพธ์ของแต่ละคอนฟิกที่ 8K โทเค็นมีดังนี้
- FP16: 176.00MB, 54,360 tokens/sec
- K8V8: 93.50MB, 51,503 tokens/sec, perplexity +0.03%
- K8V4: 71.50MB, 57,438 tokens/sec, perplexity +0.86%
- K4V8: 71.50MB, 58,690 tokens/sec, perplexity +6.06%
- K4V4: 49.50MB, 55,193 tokens/sec, perplexity +6.15%
- ในตารางการประหยัดหน่วยความจำ K8V4 ลดได้ 59% ที่ 8K โทเค็น และ K4V4 ลดได้ 72%
- ในตารางประสิทธิภาพ K8V4 เร็วกว่า FP16 อยู่ +5.7%, K4V8 +8.0%, และ K4V4 +1.5%
- K8V8 แม้จะใช้หน่วยความจำน้อยกว่า FP16 แต่ความเร็วลดลง -5.3%
การใช้หน่วยความจำตามความยาวซีเควนซ์
- ยิ่งความยาวคอนเท็กซ์มากขึ้น ผลการลดหน่วยความจำของ KV cache ก็ยิ่งชัดเจน
- การใช้หน่วยความจำที่ 8192 โทเค็นมีดังนี้
- FP16: 176.00MB
- K8V8: 93.50MB
- K8V4: 71.50MB
- K4V8: 71.50MB
- K4V4: 49.50MB
- ที่ 4096 โทเค็น K8V4/K4V8 ใช้ 35.75MB และ K4V4 ใช้ 24.75MB เทียบกับ FP16 ที่ 88.00MB
- ที่ 128 โทเค็น ระบุไว้ว่า FP16 ใช้ 5.50MB, K8V4/K4V8 ใช้ 2.23MB, และ K4V4 ใช้ 1.55MB
ความไม่สมมาตรของ key และ value
- หน่วยความจำของ KV cache ถูกครอบงำโดยการเก็บ เวกเตอร์ key และเวกเตอร์ value ของแต่ละโทเค็น
- ข้อสังเกตหลักของโปรเจ็กต์คือ key ไวต่อการควอนไทซ์มากกว่า value อย่างชัดเจน
- K8V4 ใช้ key แบบ 8-bit และ value แบบ 4-bit เพื่อให้ได้จุดสมดุลดังนี้
- คุณภาพลดลงด้าน perplexity เพียง 0.86% เมื่อเทียบกับ FP16
- ประหยัดหน่วยความจำ 59%
- ความเร็วการอนุมานสูงกว่า FP16
- K4V8 ใช้จำนวนบิตรวมเท่ากับ K8V4 แต่สรุปว่าคุณภาพลดลงมากกว่า K8V4 ราว 7 เท่า
- โปรเจ็กต์อธิบายว่าความไม่สมมาตรนี้ทำให้ฮาร์ดแวร์ระดับผู้บริโภครองรับคอนเท็กซ์ที่ยาวขึ้นและโมเดลที่ใหญ่ขึ้นได้
การติดตั้งและการผสานรวม
- การติดตั้งทำได้โดย clone รีโพซิทอรีแล้วรัน
scripts/install_kvsplit.sh
git clone https://github.com/dipampaul17/KVSplit.git
cd kvsplit
chmod +x scripts/install_kvsplit.sh
./scripts/install_kvsplit.sh
- สคริปต์ติดตั้งให้เลือกวิธีตั้งค่าสภาพแวดล้อม Python ได้
- Virtual Environment: สร้างสภาพแวดล้อม Python แยกภายในโฟลเดอร์โปรเจ็กต์
- System Python: ใช้ Python ที่ติดตั้งอยู่แล้วในระบบ
- Skip Python Setup: ให้ผู้ใช้จัดการสภาพแวดล้อม Python เอง
- ยังเลือกวิธีผสานรวมกับ llama.cpp ได้ด้วย
- แบบมาตรฐาน: clone llama.cpp แล้วใช้แพตช์ KV split
- แบบ Git submodule: เพิ่ม llama.cpp เป็น submodule สำหรับนักพัฒนาหรือผู้ใช้ขั้นสูง
- ขั้นตอนติดตั้งรวมถึงการตั้งค่า llama.cpp ที่รองรับ Metal สำหรับ Apple Silicon, เปิดใช้ differentiated KV cache quantization, ดาวน์โหลดโมเดลทดสอบแบบเลือกได้ และตั้งค่าเครื่องมือสร้างภาพ
ตัวอย่างการใช้งานและตัวเลือก CLI
- การเปรียบเทียบแบบรวดเร็วสามารถรันกับโมเดล GGUF ที่ผู้ใช้มีได้
python scripts/quick_compare.py --model models/your-model.gguf
- การเปรียบเทียบครอบคลุม FP16, K8V8, K8V4, K4V8, K4V4 และแสดงทั้งหน่วยความจำ ความเร็ว และตัวชี้วัดคุณภาพ
- ตัวอย่างใน README ใช้
llama-cli ร่วมกับ --flash-attn และตัวเลือก KV quantization
./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "Your prompt" \
-t 8 --flash-attn --kvq 8
- ตัวอย่าง K4V8 ระบุจำนวนบิตของ key และ value แยกกัน
./llama.cpp/build/bin/llama-cli -m models/your-model.gguf -p "Your prompt" \
-t 8 --flash-attn --kvq-key 4 --kvq-val 8
- ตัวอย่างคอนเท็กซ์ 32K ระบุว่า FP16 ต้องใช้ราว 1.4GB ขณะที่ K8V4 ใช้ราว 400MB
./llama.cpp/build/bin/llama-cli -m models/your-model.gguf \
-c 32768 -n 4096 -t 8 --flash-attn --kvq 8 \
-f your-long-document.txt
- แฟล็ก CLI หลักมีดังนี้
-t 8: จำนวนเธรด โดยแนะนำ 8 สำหรับชิป Apple Silicon ส่วนใหญ่
--flash-attn: เปิดใช้ attention แบบปรับแต่งประสิทธิภาพ แนะนำบน Apple Silicon
--kvq N: ตั้งค่าจำนวนบิตของ key และ value
--kvq-key N: ตั้งค่าบิตของ key เท่านั้น
--kvq-val N: ตั้งค่าบิตของ value เท่านั้น
-c N: ขนาดคอนเท็กซ์
-n N: จำนวนโทเค็นที่จะสร้าง
-f FILE: ไฟล์อินพุต
-m MODEL: พาธไฟล์โมเดล .gguf
เครื่องมือเบนช์มาร์กและการสร้างภาพ
- เบนช์มาร์กเต็มรันด้วย
scripts/benchmark_kvsplit.py
python scripts/benchmark_kvsplit.py
python scripts/benchmark_kvsplit.py --config K8V4 --seq-len 4096
- การสร้างภาพใช้
scripts/visualize_results.py
python scripts/visualize_results.py
- เบนช์มาร์กจะวัดรายการต่อไปนี้
- Memory Usage: VRAM และหน่วยความจำของ KV cache
- Performance: tokens/sec ตามความยาวซีเควนซ์
- Quality: perplexity โดยใช้
llama-perplexity
- Scaling: การเปลี่ยนแปลงของหน่วยความจำและประสิทธิภาพตามความยาวซีเควนซ์
- ผลลัพธ์จะถูกบันทึกเป็น CSV/JSON พร้อมสร้างสถิติสรุปอัตโนมัติและกราฟภาพ
capture_memory.sh เป็นเครื่องมือสำหรับจับภาพการประหยัดหน่วยความจำจาก Activity Monitor
การปรับแต่งสำหรับ Apple Silicon และข้อจำกัด
- KVSplit ถูกปรับให้เหมาะกับ Metal framework ของ Apple
- เน้นประสิทธิภาพด้านหน่วยความจำบนอุปกรณ์ที่มีข้อจำกัดด้านหน่วยความจำ เช่น Apple Silicon ซีรีส์ M
- README ระบุว่าเนื่องจาก 256B page alignment ของ llama.cpp การประหยัดหน่วยความจำจริงอาจต่างจากค่าคำนวณเชิงทฤษฎีเล็กน้อย
- รองรับชิป M1, M2, M3 และ M4
คอนฟิกที่แนะนำและโรดแมป
- คอนฟิกที่แนะนำคือ K8V4
- key 8-bit, value 4-bit
- ประหยัดหน่วยความจำ 59%
- สูญเสียคุณภาพ 0.86%
- ความเร็วการอนุมาน +5.7% เมื่อเทียบกับ FP16
- การประหยัดหน่วยความจำสูงสุดคือ K4V4
- key 4-bit และ value 4-bit
- ประหยัดหน่วยความจำ 72%
- สูญเสียคุณภาพราว 6%
- ระบุว่าเหมาะกับแอปพลิเคชันที่ไวต่อคุณภาพน้อยกว่า
- สำหรับคอนเท็กซ์ที่ยาวมาก แนะนำ K8V4 หรือ K4V4 และยิ่งคอนเท็กซ์ยาว ผลการประหยัดหน่วยความจำก็ยิ่งสะสมมากขึ้น
- แผนในอนาคตมีดังนี้
- Adaptive Precision ตามความสำคัญของโทเค็น
- Layer-Specific Quantization ที่ใช้ความละเอียดต่างกันในแต่ละเลเยอร์
- การปรับแต่งรายโมเดลสำหรับ Mistral, Phi-3 เป็นต้น
- เว็บเดโม
- รองรับ iOS และ iPadOS
- ใบอนุญาตคือ MIT และเปิดรับการมีส่วนร่วมผ่าน issue หรือ pull request
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
น่าสนใจ อยากรู้ว่ามี สัญชาตญาณ อะไรเกี่ยวกับว่าทำไมถึงได้ผลลัพธ์แบบนี้ไหม และอยากรู้ด้วยว่าค้นพบจากสัญชาตญาณนั้น หรือเจอจากการทดลองแบบสุ่ม
ในขั้นตอน "apply patch" ของสคริปต์ติดตั้ง ดูเหมือนยังมี placeholder เหลืออยู่ น่าจะเป็นมิตรกับผู้ใช้มากกว่าถ้า fork llama.cpp แล้วรวมไว้เป็น Git submodule แทนที่จะให้
git cloneแล้วค่อย apply patchอีกอย่าง การตั้งค่า Python ในเครื่องของแต่ละคนก็แตกต่างกันไป ถ้าทำให้แยกส่วนที่เกี่ยวกับ llama.cpp กับส่วนที่เกี่ยวกับ Python ออกจากกันได้ แทนที่จะผูก dependency ของ Homebrew Python ไว้ตายตัว ก็น่าจะดี
Key ใช้ตัดสินใจว่าจะให้ความสนใจกับ token ใด และสร้างรูปแบบ attention จริงผ่านการคำนวณความคล้ายกัน ส่วน Value เพียงเก็บข้อมูลที่จะส่งต่อหลังจากตัดสิน attention แล้ว
ถ้า quantize เวกเตอร์ Key รุนแรงเกินไป การคำนวณความคล้ายกันของปฏิสัมพันธ์ระหว่าง token ทั้งหมดจะเพี้ยนไป ความคลาดเคลื่อนเล็กน้อยใน Key อาจทำให้ attention หันไปหา token ที่ผิดไปโดยสิ้นเชิง
Value ทนได้มากกว่ามาก ความคลาดเคลื่อนจากการ quantize เวกเตอร์ Value จะกระทบเฉพาะเนื้อหาข้อมูลของ token เดียวนั้น หลังจากที่รูปแบบ attention ถูกกำหนดไว้แล้ว
คล้ายกับความต่างระหว่างระบบรายการหนังสือในห้องสมุดกับตัวหนังสือเอง ถ้าหมายเลขรายการ (Key) เสีย ก็จะไปดูชั้นหนังสือที่ผิดไปโดยสิ้นเชิง แต่ถ้าคำบางคำในหนังสือ (Value) เลอะเลือนไป ก็ยังอ่านหนังสือเล่มที่ถูกอยู่ เพียงแค่มีสัญญาณรบกวนบ้างเป็นบางครั้ง
ในเชิงคณิตศาสตร์ Key เข้าไปอยู่ในการคำนวณ softmax และความคลาดเคลื่อนเล็กน้อยจะถูกขยายแบบเอ็กซ์โปเนนเชียลในกระบวนการ normalize ส่วน Value ผ่านแค่ weighted average แบบเชิงเส้น ดังนั้นความคลาดเคลื่อนจึงมีแนวโน้มจะหักล้างกัน
ตอนแรกได้เจอความไม่สมมาตรนี้จากงานวิจัยอย่าง "More for Keys, Less for Values" และ "KV-AdaQuant" แล้วอยากวัดเชิงปริมาณว่าในการ inference บน Apple Silicon มันมีผลมากแค่ไหน สิ่งที่น่าประทับใจคือในหน่วยความจำเท่ากัน คุณภาพของ K8V4 กับ K4V8 ต่างกันถึง 7 เท่า
ขอบคุณสำหรับ feedback เรื่องการติดตั้งด้วย จะไปแก้ placeholder และทำให้ dependency ของ Python ยืดหยุ่นขึ้น
arg.cppตั้งแต่ 8 เดือนก่อนแล้วแต่เหตุผลที่ไม่เป็นไรคือ option สำหรับตั้งค่า quantization ของ K และ V ถูก เพิ่มเข้าไปใน llama.cpp ตั้งแต่ปี 2023 แล้ว
ไม่เข้าใจว่าทำไม patch นี้ถึงมีอยู่ นอกจากจะพยายามทำให้การตั้งค่าที่มีอยู่แล้วดูเหมือนใหม่ด้วยการเปลี่ยนเป็น argument บรรทัดคำสั่งอีกแบบ ก็ไม่เห็นเหตุผลอื่น
ขอแนะนำอย่างยิ่งว่าอย่าให้ใครรันไฟล์
install.shของ repository ใหม่ ๆ แบบนี้ โดยเฉพาะเมื่อไม่จำเป็นสำหรับเรื่องง่าย ๆ อย่างการ apply patch ไฟล์เดียวนี่ต่างจากการใช้
--cache-type-kกับ--cache-type-vไหม?ความแปลกอื่น ๆ ใน repository ผมเขียนไว้บางส่วนในคอมเมนต์อื่นแล้ว
ดังนั้นด้วยวิธี
type_k/vแบบเดิมและบน Apple GPU ค่าต่ำสุดที่ลดลงไปได้คงเป็น 16-bit f16/bf16 อย่างไรก็ตาม ผมไม่ใช่ผู้เชี่ยวชาญภายในของ llama.cpp จึงอาจผิดก็ได้สงสัยว่า patch นี้ทำใน MLX ได้ไหม เพราะ MLX ทำความเร็วได้ดีกว่าอยู่แล้ว ถ้ารวมกับแนวทางนี้ได้ ผู้ใช้ Mac ก็น่าจะคุยยาว ๆ ด้วยความเร็วที่ใช้งานได้
โดยส่วนตัว สิ่งที่ผมตั้งตารอมากที่สุด ฟังดูไม่น่าเชื่อก็คือ Haskell binding เมื่อไม่กี่วันก่อนมีคนชี้ว่า lazy evaluation ของ Haskell ค่อนข้างเข้ากับ paradigm นี้ได้ดี และแนวทางที่เกือบเป็น pure functional ต่อ compile graph ก็ช่วยได้เหมือนกัน การทำ machine learning ใน Haskell น่าจะสนุก
สงสัยว่าสามารถใช้ KV quantization แบบแยกต่างกัน (เช่น K8V4) กับโมเดลที่แปลงเป็นรูปแบบ
.ggufแล้วได้ไหม หรือต้อง build โมเดลใหม่พร้อมใส่การรองรับพิเศษ?ถ้าเข้ากันได้กับไฟล์
.ggufใด ๆ ก็อยากรู้ด้วยว่ามีข้อจำกัดเรื่องชนิดของโมเดล (Mistral, Phi-3 ฯลฯ) หรือการตั้งค่า tokenizer ไหม.ggufที่มีอยู่เดิมได้เลย โดยไม่ต้องจัดโครงสร้างใหม่หรือแปลงแบบพิเศษ quantization เกิดขึ้นที่ KV cache ณ เวลา runtime ไม่ใช่ระหว่างโหลดหรือแปลงโมเดลทำได้เพราะ KV cache ถูกสร้างขึ้นระหว่าง inference ขณะประมวลผล token และแยกจาก weight ของโมเดลโดยสิ้นเชิง flag
--kvq-keyและ--kvq-valแค่บอก llama.cpp ว่าจะเก็บ tensor กลางเหล่านี้ไว้ในหน่วยความจำอย่างไรทดสอบสำเร็จกับ Llama-3, Mistral, Phi-2/Phi-3, TinyLlama และ variant ของ Qwen แล้ว
ข้อจำกัดเดียวคือจำเป็นต้องใช้ Metal backend ของ llama.cpp และตอนนี้ implementation ของ Flash Attention ใน llama.cpp จะข้ามรูปแบบ KV cache แบบ custom ดังนั้นต้องปิด Flash Attention ด้วย
-fa 0ตัวเทคนิคเองน่าจะใช้ได้กับสถาปัตยกรรม transformer ใด ๆ ที่ใช้กลไก attention มาตรฐานได้มีเวลาอ่านโค้ดแล้ว ถ้าผมเข้าใจ PR นี้ถูกต้อง ฟีเจอร์นี้มีอยู่ใน llama.cpp มาตั้งแต่ปี 2023 แล้ว ดังนั้นแพตช์จึงไม่จำเป็น: https://github.com/ggml-org/llama.cpp/pull/4312
แทนที่จะให้ fork ของ llama.cpp ที่นำการเปลี่ยนแปลงไป commit ไว้แล้ว repository กลับให้รันสคริปต์
install.shสคริปต์นี้ checkout branch master ของ llama.cpp โดยไม่ระบุ revision แล้วค่อยนำแพตช์สั้น ๆ ไปใช้ แค่นี้ก็เป็น สัญญาณเตือน ว่ามีอะไรแปลก ๆ แล้วใน repository มีไฟล์แพตช์ต่างกัน 4 ไฟล์ และยังมีเวอร์ชันแพตช์เพิ่มเติมอีกหนึ่งอันที่ฝังเป็น Heredoc อยู่ในสคริปต์ติดตั้ง ในสคริปต์ยังมีโค้ดสำหรับ clone repository และลอง apply patch อยู่สองเวอร์ชันด้วย
install.shเขียนทับไฟล์แพตช์ไฟล์หนึ่งด้วยอีกไฟล์หนึ่งผ่านบรรทัดcp patch/split_kv_quant.diff patch/fixed_kv_patch.diffดังนั้นfixed_kv_patch.diffที่ check in อยู่ใน repository จะถูกเขียนทับก่อนนำไปใช้ในมุมมองของผม เดิมทีน่าจะตั้งใจใช้แพตช์นี้: https://github.com/dipampaul17/KVSplit/blob/main/patch/split... (แก้ไข: ดูจากคอมเมนต์ท้าย ๆ แล้ว จริง ๆ น่าจะเป็นอันนี้: https://github.com/dipampaul17/KVSplit/blob/main/patch/fixed... )
สิ่งที่แพตช์นี้เพิ่มเข้ามามีแค่อาร์กิวเมนต์
--kvqซึ่งตั้งค่า quantization ของ K และ V พร้อมกัน แต่ด้านบนของมันก็มีอาร์กิวเมนต์ในตัวสำหรับตั้งค่า quantization ของ K และ V แยกกันอยู่แล้ว เป็นไปได้หรือที่ระหว่างย้ายแพตช์พวกนี้ไปมา ผู้เขียนจะไม่ทันสังเกตว่าฟังก์ชันนี้มีอยู่แล้ว?ขอแนะนำอย่างยิ่งว่าอย่ารัน shell script จาก repository ใหม่ ๆ แบบนี้ โดยเฉพาะถ้าสคริปต์ซับซ้อนแบบนี้ยิ่งไม่ควร
โพสต์บน HN ได้ upvote มากกว่า 200 ครั้ง และ GitHub repository ก็มี star มากกว่า 200 ดวงและยังเพิ่มขึ้นเรื่อย ๆ แต่เนื้อหาดูเหมือนจะทำให้เข้าใจผิด คอมเมนต์ที่ชี้ปัญหาใน thread นี้แล้วโดน flag ไปเยอะ ๆ นั้นจริง ๆ แล้วถูกต้อง อีกเรื่องที่น่ากังวลคือผู้เขียนยังตอบใน thread นี้เรื่อย ๆ แต่หลบคำถามที่ว่าฟีเจอร์นี้มีอยู่แล้ว
แก้ไข: ผมอ่าน shell script ผิดไป จริง ๆ ดูเหมือนว่าจะ apply แพตช์นี้: https://github.com/dipampaul17/KVSplit/blob/main/patch/fixed... หลัง apply แพตช์แล้ว แปลกตรงที่มันเขียนทับ
fixed_kv_patch.diffด้วยsplit_kv_quant.diffแต่หลังจากนั้นก็ไม่ได้ทำอะไรอีก ไม่รู้ว่านี่เป็นผลจาก vibe coding หรือแค่การแก้โค้ดอย่างสะเพร่า แต่ขอย้ำอีกครั้งว่าไม่ควรรัน shell script แบบนี้จาก repository ที่ไม่รู้จักแก้ไข 2: ยิ่งสับสนเข้าไปอีก สคริปต์
install.shอ้างถึง URL เก่าของ repository llama.cpp (https://github.com/ggerganov/llama.cpp) ซึ่งเปลี่ยนไปตั้งนานแล้วและตอนนี้เป็น redirect แพตช์พยายามแก้การ parse อาร์กิวเมนต์ในcommon.cppแต่โค้ดนั้นถูกย้ายไปarg.cppเมื่อ 8 เดือนก่อนแล้ว (https://github.com/ggml-org/llama.cpp/commit/bfe76d4a17228bf...) ถ้าอย่างนั้นสคริปต์ติดตั้งและ repository นี้ก็อิงจากโค้ดราวปี 2024 แต่ใช้ option ที่เพิ่มเข้าไปใน llama.cpp ราวปี 2023 นี่มันเกิดอะไรขึ้นกันแน่?มี สัญญาณเตือน เยอะมาก มองในแง่ดีก็ยังดูเหมือนคนที่พยายามปั่นโปรไฟล์ GitHub ด้วยโค้ดที่ LLM สร้างขึ้น แค่ดู activity วันที่ 12 พฤษภาคมในโปรไฟล์นั้นก็พอ
แต่ activity ทั้งหมดบน GitHub ของผู้เขียนโพสต์ต้นทางก็น่าสงสัย วันที่ 12 พฤษภาคม เขาส่ง PR ขยะปนเปจาก LLM ไปยังโปรเจกต์ยอดนิยมหลายโปรเจกต์ และมีแค่ฝั่ง JAX ที่ปฏิเสธ ถึงอย่างนั้น เขาก็ยังใช้สิ่งนี้ปักหมุดโปรเจกต์ยอดนิยมบนโปรไฟล์ราวกับว่าเป็น contributor ได้
ยากจะบรรยายเป็นคำพูดได้ว่าสิ่งนี้น่ารังเกียจแค่ไหน ใครก็ตามที่ทำงานในวงการ AI ล้วนมีส่วนร่วมในการปนเปื้อนข้อมูล และผลลัพธ์ของมันยังแทบคาดเดาไม่ได้ด้วยซ้ำ อินเทอร์เน็ตที่ตายแล้วกับกระแส AI ขยะปนเปเป็นเพียงจุดเริ่มต้นเท่านั้น
บน Apple Silicon รุ่น 64GB หรือ 128GB สิ่งเหล่านี้เร็วขึ้นหรือดีกว่ารุ่น 36GB หรือ 48GB อย่างมีนัยสำคัญไหม?
เคยอ่านมาว่า context ขนาดใหญ่และโมเดลขนาดใหญ่นั้นช้าจนเจ็บปวด แม้บน Apple Silicon ที่เร็วที่สุดและใหญ่ที่สุดเท่าที่เงินจะซื้อได้
เลยสงสัยว่าสิ่งนี้ช่วยให้ใช้หน่วยความจำที่ใหญ่ขึ้นได้ดีขึ้นหรือไม่ หรือในทางปฏิบัติแล้ว บน Apple Silicon คำตอบก็ยังเป็นโมเดลที่ค่อนข้างเล็กอยู่ดี
อย่างไรก็ตาม KVSplit ไม่ได้เปลี่ยนความเร็วในการคำนวณโดยพื้นฐาน แต่เปลี่ยนแค่ ประสิทธิภาพการใช้หน่วยความจำ เท่านั้น ใน benchmark K8V4 ทำให้ throughput ดีขึ้น 14.5% แต่สาเหตุไม่ใช่เพราะปริมาณการคำนวณลดลง หากเป็นเพราะ locality ของหน่วยความจำดีขึ้น
เหตุผลหลักที่โมเดลขนาดใหญ่บน Apple Silicon “ช้าจนเจ็บปวด” ไม่ใช่ข้อจำกัดด้านหน่วยความจำ แต่เป็นขีดจำกัดด้านประสิทธิภาพการคำนวณ โมเดลพารามิเตอร์ 70B จะทำงานด้วยความเร็วสร้างโทเคนใกล้เคียงเดิม ไม่ว่า RAM ที่ใช้ได้หรือการปรับ KV cache ให้เหมาะสมจะเป็นอย่างไร
KVSplit ช่วยให้ใช้หน่วยความจำที่มีอยู่ได้ดีขึ้น โดยมีคุณค่าเป็นพิเศษเมื่อคอขวดอยู่ที่ความยาวของ context มากกว่าขนาดของโมเดล
สำหรับการใช้งาน Apple Silicon ในทางปฏิบัติ จุดที่เหมาะสมยังคงเป็นการใช้โมเดลที่เล็กกว่า (7B~13B) พร้อมหน้าต่าง context ที่ขยายขึ้น วิธีนี้ทำให้ประมวลผลข้อความได้มากขึ้นมาก ในขณะที่ยังรักษาความเร็วในการสร้างผลลัพธ์ไว้ในระดับสมเหตุสมผล
ถ้า workflow ต้องการทั้ง context มหึมาและโมเดลขนาดใหญ่ ก็ยังควรพิจารณา GPU ระดับเซิร์ฟเวอร์อยู่ดี แต่ KVSplit ช่วยดันขอบเขตที่เป็นไปได้บนฮาร์ดแวร์ Apple ออกไปอีกเล็กน้อย
เป็นงานที่ยอดเยี่ยมและดูน่าสนใจมาก แต่เพื่อให้เข้าใจได้ คงต้องมี คำอธิบายในระดับสูงกว่า อีกหน่อย
ตัวอย่างเช่น มันทำให้โมเดลที่มีหน้าต่าง context 2048 โทเคน รันด้วยหน้าต่าง context 4~6K ได้หรือเปล่า? หรือทำให้โมเดล 128K อย่าง gemma3 รันด้วยหน้าต่าง context 256K ขึ้นไปได้?
use case ในอุดมคติของโมเดล local คืออะไร?
ในทางปฏิบัติ หมายความว่าสามารถประมวลผลหนังสือทั้งเล่มในครั้งเดียวบน MacBook, วิเคราะห์ codebase ขนาดใหญ่โดยไม่ต้องแบ่งไฟล์ หรือเก็บประวัติการสนทนายาว ๆ ในแอปแชตได้
การประหยัดหน่วยความจำแปรผันเชิงเส้นตามความยาวของ context ยิ่งหน้าต่าง context ยาว หน่วยความจำที่ประหยัดได้แบบสัมบูรณ์ก็ยิ่งมาก บน M4 MacBook ของผม เมื่อ context 8K นั้น KV cache ลดจาก 176MB เหลือ 72MB ถ้าเป็น context 128K การประหยัดในอัตราเดียวกันจะปล่อยหน่วยความจำระดับกิกะไบต์ให้ว่าง
การปรับให้เหมาะสมนี้มีคุณค่ามากที่สุดเมื่อชนขีดจำกัดของหน้าต่าง context มากกว่าขีดจำกัดของพารามิเตอร์โมเดล ถ้าเกิดข้อผิดพลาดหน่วยความจำไม่พอเพราะ input ยาว ไม่ใช่เพราะน้ำหนักโมเดลขนาดใหญ่ KVSplit ก็แก้คอขวดโดยตรง
การเพิ่มหน้าต่าง context หลังการฝึกไม่ใช่เรื่องง่าย ดังนั้นถ้าไม่รู้แน่ชัดว่ากำลังทำอะไรอยู่ การหาโมเดลที่ถูกฝึกมาด้วยหน้าต่าง context ที่ใหญ่กว่าจะดีกว่า
การใช้งานโมเดล local มีได้หลากหลาย เช่น งานออฟไลน์ ความเป็นส่วนตัว/ความปลอดภัย เป็นต้น แต่ส่วนใหญ่ก็มักใช้เพื่อทดลองปรับแต่งโมเดล
มีเรื่องแปลก ๆ เกิดขึ้น จึงคิดว่าไม่ควรติดตั้งสิ่งนี้หรือรันสคริปต์นั้น
โพสต์ที่ส่งมานี้ผม flag แล้ว
เป็นไอเดียและความพยายามที่ยอดเยี่ยม สิ่งนี้ใช้กับ GPU ได้ด้วยไหม? และดูเหมือนน่าจะเข้ากันได้กับเทคนิค quantization อื่น ๆ ด้วย แบบนี้ควรมองว่าต้องมีแพตช์แยกสำหรับแต่ละแบบหรือเปล่า?
backend CUDA ของ llama.cpp รองรับการตั้งค่าประเภท cache แยกกันอยู่แล้วผ่าน flag
--cache-type-kและ--cache-type-vแพตช์เฉพาะนี้เน้นการปรับให้เหมาะสมสำหรับ Metal โดยเฉพาะ แต่เทคนิคหลักย้ายไปใช้ได้ตรง ๆยังเข้ากันได้กับวิธี quantization อื่น ๆ ด้วย การปรับ KV cache ให้เหมาะสม นี้เสริมกันกับการ quantize น้ำหนักโมเดล (Q4_K_M, GPTQ, AWQ ฯลฯ) สามารถใช้ความแม่นยำ KV cache แบบไม่สมมาตรร่วมกับรูปแบบน้ำหนักโมเดลใด ๆ ก็ได้
การ quantize KV cache เกิดขึ้นขณะรันระหว่างการประมวลผลโทเคน และแยกจากน้ำหนักโมเดล จึงไม่ขัดแย้งกับวิธีที่ตัวโมเดลถูก quantize มันทำงานคนละส่วนใน inference pipeline
ส่วนที่ต้องทำงานเพิ่มเติมคือการผสานรวมกับ inference engine เฉพาะทางที่มีการจัดการ KV cache แบบ custom เช่น vLLM หรือ TensorRT-LLM แต่ละตัวต้อง implement ความแม่นยำ KV แบบไม่สมมาตรแยกกัน
ประโยชน์ที่เห็นได้ทันทีที่สุดบน GPU อาจมาจากการนำ insight นี้ไปรวมเข้ากับการ implement FlashAttention โดยตรง บนฮาร์ดแวร์ CUDA การลดแบนด์วิดท์หน่วยความจำอาจนำไปสู่ความเร็วที่เพิ่มขึ้นมากกว่า
ที่ context ขนาดเล็ก perplexity +0.86% ถือว่าค่อนข้างมากไม่ใช่หรือ? แล้วในขนาด context ที่สมจริงกว่าอย่าง 64~128K เป็นอย่างไร?
หรือจะเอาหน่วยความจำที่เหลือไปใช้กับอย่างอื่น เช่น IDE ก็ได้