- LLM ขนาดใหญ่ปรับใช้ได้ยากเนื่องจากข้อจำกัดของหน่วยความจำ GPU แต่ DFloat11 ลดขนาดน้ำหนัก BFloat16 ลงเหลือประมาณ 70% พร้อมคงเอาต์พุตให้ เหมือนต้นฉบับในระดับบิต
- หัวใจสำคัญคือ exponent 8 บิต ของ BFloat16 ในความเป็นจริงมีข้อมูลเพียงประมาณ 2.6 บิต โดยรักษา sign และ mantissa ไว้ และบีบอัดเฉพาะ exponent ด้วย Huffman coding
- การเข้ารหัสความยาวแบบไดนามิกมักกลายเป็นคอขวดบน GPU ดังนั้น DFloat11 จึงปรับให้เหมาะกับ inference แบบขนานด้วย LUT แบบลำดับชั้น, kernel 2 ขั้นตอน และการคลายการบีบอัดเป็นหน่วย transformer block
- ยืนยันได้กับ Llama 3.3, Qwen 3, Mistral 3, DeepSeek R1 Distilled, FLUX.1, Stable Diffusion 3.5 ฯลฯ ว่าลด ขนาดโมเดลได้ประมาณ 30% และคงเอาต์พุตเดิมไว้
- throughput ของการสร้าง token สูงกว่าทางเลือกแบบ CPU offload 2.3–46.2 เท่า และทำให้ inference แบบไม่สูญเสียของ Llama 3.1 405B ขนาด 810GB ทำได้บนโหนดเดียวที่มี GPU 8×80GB
คอขวดด้านหน่วยความจำที่ DFloat11 มุ่งแก้
- foundation model เช่น LLM และ Diffusion Model มีขนาดโตขึ้นอย่างรวดเร็ว ทำให้ปรับใช้อย่างมีประสิทธิภาพบนฮาร์ดแวร์ที่มีข้อจำกัดด้านหน่วยความจำได้ยาก
- Llama 3.1 405B เก็บพารามิเตอร์ 405 พันล้านตัวในรูปแบบ BFloat16 และต้องใช้หน่วยความจำประมาณ 810GB สำหรับ inference ทั้งหมด
- ซึ่งเกินความจุของเซิร์ฟเวอร์ GPU ระดับสูงทั่วไปที่มี GPU 8×80GB
- หากต้องใช้หลายโหนด ต้นทุนการปรับใช้และภาระด้านการเข้าถึงก็จะเพิ่มขึ้น
- DFloat11 มีเป้าหมายบีบอัดโมเดล BFloat16 ให้เหลือประมาณ 70% ของขนาดเดิม พร้อมรักษาความแม่นยำ 100% ในทุกงาน
แนวทางที่ต่างจากการ quantization แบบสูญเสีย
- Quantization เป็นวิธีบีบอัดแบบสูญเสียที่ลดหน่วยความจำด้วยการลด precision ของน้ำหนัก
- สามารถลดการใช้หน่วยความจำได้มากและเพิ่มความเร็ว inference ได้ แต่จะเกิดข้อผิดพลาดจากการประมาณค่า
- การสูญเสียความแม่นยำคาดการณ์ได้ยาก เพราะขึ้นอยู่กับโมเดลฐาน วิธี quantization, benchmark ประเมินผล และ bit-width เป้าหมาย
- ตัวอย่างเช่น เมื่อใช้ 8-bit SmoothQuant กับ DeepSeek-R1-Distill-Qwen-1.5B ความแม่นยำเฉลี่ยใน reasoning task ลดลง 9.09%
- แม้ตัวชี้วัดความแม่นยำโดยรวมจะใกล้เคียงกัน โมเดลที่ผ่าน quantization ก็อาจแสดงพฤติกรรมการตอบที่ต่างจากต้นฉบับ
- Dutta et al. สังเกตปรากฏการณ์ flips ที่คำตอบถูกเปลี่ยนเป็นผิด หรือคำตอบผิดเปลี่ยนเป็นถูก
- Qwen2-1.5B ที่ quantize ด้วย W8A16 GPTQ มีความแม่นยำ GSM8K 8-shot ลดลงเพียง 0.3% แต่สถานะถูก/ผิดของคำตอบเปลี่ยนไปใน 6.37% ของคำตอบ
- ในโดเมนอย่างการเงินและการแพทย์ การเปลี่ยนแปลงเอาต์พุตของโมเดลที่ผ่าน quantization อาจทำให้ยากต่อการตอบสนองข้อกำหนดด้าน กฎระเบียบและความน่าเชื่อถือ
- การบีบอัดโมเดลแบบไม่สูญเสียที่มีอยู่เดิมมักเน้นประสิทธิภาพการจัดเก็บ checkpoint, การลดเวลาดาวน์โหลดจาก model hub และฮาร์ดแวร์เฉพาะทางอย่าง FPGA จึงให้ประโยชน์น้อยสำหรับ inference บน GPU ทั่วไป
ช่องว่างสำหรับการบีบอัดที่ยังเหลืออยู่ใน exponent ของ BFloat16
- BFloat16 แบ่ง 16 บิตออกเป็น sign 1 บิต, exponent 8 บิต, mantissa 7 บิต
- จุดเริ่มต้นของ DFloat11 คือการวิเคราะห์ Shannon entropy ขององค์ประกอบ BFloat16 แต่ละส่วนในน้ำหนักของ LLM
- entropy ของ sign และ mantissa ใกล้เคียงกับ bit-width ของแต่ละส่วน จึงมีพื้นที่ให้บีบอัดจำกัด
- exponent ได้รับการจัดสรร 8 บิต แต่มี entropy เพียงประมาณ 2.6 บิต
- การกระจายของค่า exponent ไม่สมดุลอย่างมาก
- จากค่า 8 บิตที่เป็นไปได้ 256 ค่า มีการใช้เพียงประมาณ 40 ค่า
- ค่าที่เหลือไม่ปรากฏ
- ลำดับความถี่ก็ลดลงอย่างรวดเร็ว
- ด้วย entropy ที่ต่ำ exponent จึงเป็นเป้าหมายสำหรับการบีบอัดแบบไม่สูญเสีย และข้อมูล exponent ประมาณ 5.4 บิตสามารถบีบอัดได้
รูปแบบ DFloat11
- DFloat11 หรือ DF11 เป็นรูปแบบ floating-point ความยาวแบบไดนามิกที่บีบอัดเฉพาะ exponent ของน้ำหนัก BFloat16 ด้วย entropy coding
- Huffman tree ถูกสร้างจากการกระจายของ exponent ในน้ำหนักโมเดล
- ค่า exponent ที่พบได้บ่อยจะได้รับโค้ดสั้น
- ค่าที่พบได้น้อยจะได้รับโค้ดยาว
- sign และ mantissa ถูกเก็บไว้เหมือนต้นฉบับ
- exponent ถูกเก็บใน byte array
EncodedExponentในรูปแบบ bit-packed - sign และ mantissa ถูกเก็บแยกต่างหากใน byte array
PackedSignMantissa
- exponent ถูกเก็บใน byte array
- ผลคือ น้ำหนัก BFloat16 ลดลงเหลือเฉลี่ยประมาณ 11 บิต และสามารถกู้คืนกลับเป็นค่า BFloat16 เดิมได้โดยไม่สูญเสีย precision
การคลายการบีบอัดที่ปรับให้เหมาะกับ inference บน GPU
- น้ำหนักที่ผ่าน entropy coding ใช้การเข้ารหัสความยาวแบบไดนามิก จึงไม่สามารถนำเข้า matrix multiplication ได้โดยตรง
- ต้องคลายการบีบอัด weight matrix ที่ต้องใช้ให้กลับเป็น BFloat16 เดิมทันที
- เมื่อ matrix multiplication เสร็จแล้ว matrix BFloat16 จะถูกทิ้งทันทีเพื่อประหยัดหน่วยความจำ GPU
- Huffman decoding ทั่วไปต้องเดิน tree ทีละบิตตามลำดับ จึงไม่เข้ากับโครงสร้างขนานของ GPU
- หากให้ thread เดียวรับผิดชอบการคลายการบีบอัด การใช้งาน GPU จะต่ำและ latency จะสูง
การถอดรหัสด้วย LUT แบบลำดับชั้น
- DFloat11 ใช้ การถอดรหัสบน lookup table แทนการเดิน Huffman tree
- หากความยาวสูงสุดของ Huffman code คือ L, LUT เดี่ยวต้องมีขนาด
2^L- ใน LLM ค่า L มักอยู่ในช่วง 24–32
- LUT ระดับ
2^32รายการวางบน SRAM ของ GPU ได้ยาก
- เพื่อหลีกเลี่ยงปัญหานี้ Huffman tree จะถูกแบ่งเป็น subtree ที่ไม่ทับซ้อนกันความสูง 8 และแต่ละ subtree จะถูกทำเป็น compact LUT ขนาด 256-entry
- ใน LUT แบบลำดับชั้น บางรายการต้องทำหน้าที่เป็น reference ชี้ไปยัง LUT ชั้นล่าง
- ใช้ประโยชน์จากข้อเท็จจริงที่มีค่า exponent จำนวนมากที่ไม่ได้ใช้ใน LLM
- นำค่า exponent ที่ไม่ได้ใช้ในช่วง 240–255 กลับมาใช้เป็น pointer ภายใน
- ค่าเหล่านี้แทนขนาดที่ใหญ่มากระดับ ±2^113 ถึง ±2^128 และไม่ปรากฏในน้ำหนัก LLM
- ในการทดลอง จำนวน compact LUT k ของ Huffman tree สำหรับ exponent ของ BFloat16 อยู่ในช่วง 4–8 รายการ
- ใช้หน่วยความจำสูงสุด
(8 + 1) × 256 bytesรวมกับCodeLengths - ขนาดระดับนี้ใส่ใน SRAM ได้และทำ repeated lookup ได้รวดเร็ว
- ใช้หน่วยความจำสูงสุด
GPU kernel 2 ขั้นตอนและ metadata เสริม
- GPU thread แต่ละตัวรับผิดชอบช่วงต่อเนื่อง n byte ของ encoded exponent
- ในการทดลองใช้ n = 8
- thread จะถอดรหัส Huffman code ที่เริ่มอยู่ภายในช่วงของตนเอง
- โค้ดความยาวแบบไดนามิกทำให้เกิดปัญหา 2 อย่าง
- ตำแหน่งบิตเริ่มต้นที่ถูกต้องของแต่ละ thread ไม่ชัดเจน
- ยกเว้น thread แรกแล้ว เป็นเรื่องยากที่จะรู้ output index ของ decoded element
- ปัญหาแรกแก้ด้วย array Gaps
- Gaps มีหนึ่งรายการต่อ thread
- แต่ละรายการระบุ bit offset ของ Huffman code ที่ valid ตัวแรก โดยอ้างอิงจาก byte เริ่มต้นของ thread
- เมื่อความยาว code สูงสุดคือ 32 บิต offset จะอยู่ในช่วง
[0, 31]และเก็บด้วย 5 บิต
- ปัญหาตำแหน่งเอาต์พุตลด overhead หน่วยความจำด้วยการเก็บเฉพาะตำแหน่งระดับ thread block
- หากเก็บตำแหน่งเอาต์พุต 32 บิตต่อ thread overhead จะสูงสำหรับ thread หลายหมื่นตัวต่อ weight matrix
- DFloat11 เก็บเฉพาะตำแหน่งเอาต์พุตของ element แรกในแต่ละ thread block
- kernel ทำงานเป็น 2 ขั้นตอน
- ขั้นที่ 1 แต่ละ thread ถอดรหัสช่วงของตนเอง เพียงนับจำนวน element และไม่เขียนลง HBM
- thread ภายใน block ทำ prefix sum ด้วย Blelloch algorithm เพื่อคำนวณตำแหน่งเอาต์พุตของแต่ละ thread
- ขั้นที่ 2 ถอดรหัสช่วงเดิมอีกครั้ง และเขียนค่าที่ decoded แล้วลงใน SRAM write buffer ตามตำแหน่งที่คำนวณไว้
- encoded exponent ถูกโหลดเข้า SRAM ก่อน pass แรกเพื่อหลีกเลี่ยงการเข้าถึง global memory ซ้ำ
- หลังจาก decoded exponent ทั้งหมดถูกเขียนลง SRAM แล้ว จึงทำ coalesced write ไปยัง HBM หนึ่งครั้ง
การคลายการบีบอัดเป็นหน่วย Transformer block
- การคลายการบีบอัด weight matrix เดี่ยวอาจมีขนาดเล็กเกินกว่าจะใช้ทรัพยากร GPU ได้เต็มที่
- ยิ่ง matrix มีขนาดใหญ่ throughput การคลายการบีบอัดของ DFloat11 ก็ยิ่งดีขึ้น
- คลายการบีบอัดหลาย matrix พร้อมกันเพื่อเพิ่ม throughput และซ่อน latency
- weight matrix ของ DFloat11 ทั้งหมดใน transformer block จะถูกคลายการบีบอัดรวมกันเป็น batch เดียว
- batched decompression นี้ทำก่อน forward pass ของ transformer block นั้นทันที
- token embedding และ language modeling head ของ LLM ก็เป็นเป้าหมายการบีบอัดเช่นกัน
- matrix เหล่านี้ใหญ่พอที่จะทำให้ทรัพยากร GPU อิ่มตัว จึงไม่จำเป็นต้อง batching แยก
ผลการประเมินและผลจริง
- DFloat11 ถูกประเมินกับ LLM และ diffusion transformer รวมถึง Llama 3, Qwen 3, Mistral 3, DeepSeek R1 Distilled, FLUX.1, Stable Diffusion 3.5 เป็นต้น
- ผลลัพธ์แสดงให้เห็นว่า ลดขนาดโมเดลได้ประมาณ 30% และคงเอาต์พุตต้นฉบับไว้ได้อย่างสมบูรณ์
- เอาต์พุตเหมือนกับโมเดลต้นฉบับแบบ bit-for-bit
- เนื่องจากไม่ใช่การบีบอัดแบบสูญเสีย จึงรักษา precision ของน้ำหนัก BFloat16 ต้นฉบับไว้
- เมื่อเทียบกับทางเลือกที่ offload ส่วนหนึ่งของโมเดลที่ไม่บีบอัดไปยัง CPU เพื่อให้เข้ากับข้อจำกัดหน่วยความจำ DFloat11 ทำ throughput ในการสร้าง token ได้สูงกว่า 2.3–46.2 เท่า
- ภายใต้งบประมาณหน่วยความจำ GPU คงที่ ช่วยให้ generation length ยาวกว่าโมเดลที่ไม่บีบอัด 5.7–14.9 เท่า
- Llama 3.1 405B เป็นโมเดลขนาด 810GB แต่ DFloat11 ทำให้ inference แบบไม่สูญเสียทำได้บนโหนดเดียวที่มี GPU A100 8×80GB
- ผลลัพธ์นี้แสดงให้เห็นว่าสามารถลดข้อกำหนดฮาร์ดแวร์ที่ต้องใช้ในการรัน Llama-3.1-405B ลงครึ่งหนึ่ง พร้อมทำ inference ได้โดยไม่มี accuracy loss
ยังไม่มีความคิดเห็น