Show HN: ความเป็นไปได้ในการเพิ่มความเร็วการอนุมานของ LLM ได้ 2 เท่า (Speeding up LLM inference 2x times (possibly))
(asciinema.org)Effort คืออะไร?
- Effort หมายถึงพลังงาน เวลา และทรัพยากรที่投入เพื่อทำบางสิ่งให้สำเร็จหรือบรรลุเป้าหมาย
- หมายถึงความพยายามและความอุตสาหะที่จำเป็นต่อการเอาชนะอุปสรรคหรือให้ได้ผลลัพธ์ตามต้องการ
- Effort อาจเป็นได้ทั้งทางกายและทางใจ
- ความพยายามทางกาย เช่น พลังงานที่ใช้ในการยกของหนักหรือวิ่งมาราธอน
- ความพยายามทางใจ เช่น สมาธิและพลังใจที่จำเป็นในการแก้ปัญหาที่ซับซ้อนหรือเรียนรู้ทักษะใหม่
- เป็นองค์ประกอบสำคัญในการประสบความสำเร็จในหลายด้านของชีวิต เช่น การเติบโตส่วนบุคคล การทำงาน และการศึกษา
ความเห็นของ GN⁺
- Effort ไม่ได้หมายถึงเพียงปริมาณเวลา หรือพลังงานที่ทุ่มลงไปเท่านั้น แต่เป็นแนวคิดที่เกิดจากหลายปัจจัยร่วมกัน เช่น ความต่อเนื่อง สมาธิ และแรงจูงใจ ดังนั้น ต่อให้พยายามอย่างหนัก ก็ไม่ได้หมายความว่าจะประสบความสำเร็จเสมอไป
- โดยเฉพาะงานที่ต้องใช้ Effort ทางใจ ควรระวังไม่ให้เกิดภาวะหมดไฟ การหาวิธีรักษาระดับ Effort ให้ต่อเนื่องด้วยการพักผ่อนและให้รางวัลที่เหมาะสมจึงเป็นเรื่องสำคัญ
- แต่ละคนอาจมีวิธีลง Effort ที่ต่างกัน บางคนเหมาะกับการทุ่มเทอย่างเข้มข้นในระยะสั้น ขณะที่บางคนเหมาะกับการค่อย ๆ ลง Effort อย่างสม่ำเสมอในระยะยาว การหาวิธีที่เหมาะกับตัวเองจะมีประสิทธิภาพมากกว่า
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
แก่นของอัลกอริทึมดูเหมือนจะเป็นการตัดแต่งพารามิเตอร์ระหว่างรัน แล้วกำหนด น้ำหนักที่สำคัญน้อยกว่า จากลำดับค่าสัมบูรณ์ของน้ำหนักที่จะถูกตัดแต่งภายในแต่ละกลุ่ม ทำให้เป็น 0 เพื่อทำให้ เมทริกซ์น้ำหนักมีความเบาบาง
ถ้าค้นหาเรื่องการตัดแต่งโมเดลจะเจอผลลัพธ์จำนวนมาก และ https://arxiv.org/abs/2305.11627 ก็ใช้ “การตัดแต่งตามขนาด” เป็น baseline พร้อมอ้างถึง https://arxiv.org/pdf/2301.00774.pdf
ไม่ค่อยชอบวิธีที่งานวิจัยบางชิ้นนำ baseline ไป implement แบบลวก ๆ แล้วอวดวิธีของตัวเอง หรือใช้ศัพท์คณิตศาสตร์เยอะ ๆ มาห่อหุ้มเอาไว้ บล็อกโพสต์ต้นทางอธิบายวิธีนี้ให้คนที่แทบไม่มีพื้นความรู้เข้าใจได้ง่ายกว่ามาก
แนวทางในเปเปอร์ที่กล่าวถึงดูเหมือนจะเป็นแบบ static และดูเหมือนจะไม่ได้เสนออัลกอริทึมที่ทำให้การคำนวณจริงเร็วขึ้นด้วยผลลัพธ์ 20~50% นั่นเป็นส่วนใหญ่ของความยากเลย สักวันหนึ่งผมตั้งใจจะหาเวลาอ่านวรรณกรรมที่เกี่ยวข้องอย่างจริงจัง
ท้ายที่สุดผมอยากเพิ่ม หน้าอ้างอิง พร้อมกับเปเปอร์ที่คนอื่น ๆ โพสต์ไว้ในคอมเมนต์ คิดว่าอีกไม่นานอาจมีใครสักคนเจอบทความที่สรุปอัลกอริทึมนี้ไว้แล้วก็ได้
ระหว่างพัฒนาผมถาม gpt-4 และลองกูเกิลแล้ว แต่สิ่งที่พบส่วนใหญ่เป็นแบบ static หรือเป็นแนวตัดมิติ/เลเยอร์ทั้งก้อนออกโดยพลการแล้วฝึกใหม่ ไม่พบอะไรที่ตรงกับไอเดียนี้พอดี
ยิ่งบทความแน่นและเข้าใจยากเท่าไร ก็ยิ่งมีโอกาสมากขึ้นที่จะซ่อนวิทยาศาสตร์ที่บกพร่องไว้ข้างใน
ชอบประโยคนี้ในส่วน implementation บน GPU
“ผู้อ่านที่เพิ่งเริ่มเขียนโปรแกรม GPU อาจถามตอนนี้ว่า - มันทำงานได้อย่างไร?
ผู้อ่านที่มีประสบการณ์เขียนโปรแกรม GPU อาจถามว่า - แล้วมันทำงานได้อย่างไรกันแน่?”
คงดีถ้ามีคนที่รู้ Metal จริง ๆ มาช่วยรีวิวโค้ดนี้ นี่เป็นความพยายามเขียนโปรแกรม GPU ครั้งแรกของผม
“ลองพลิกเมทริกซ์ จัดเรียงองค์ประกอบตามแถว แล้วมองการคูณจากทิศทางนั้นใหม่ คนฉลาด ๆ เรียกสิ่งนี้ว่า รูปแบบ compressed sparse row (CSR) ตอนนี้ถ้าจะคูณ ก็เอา 1 ของเวกเตอร์มาคูณกับ 256 แล้วบวกเข้าไปที่แถวที่ 3 ของเวกเตอร์ผลลัพธ์ ประมาณนี้ ต่อไปมาดูกันว่าจะเกิดอะไรขึ้นถ้าตัดคอลัมน์สุดท้ายที่มีค่าต่ำสุดออก”
สงสัยว่ามันเชื่อมโยงกับวิธีที่ CSR ลดจำนวนการคูณอย่างไร
วิธีนี้ดูคล้าย ความเบาบางแบบกึ่งมีโครงสร้าง หรือที่เรียกว่า 2:4 sparsity จึงน่าจะเปรียบเทียบอย่างชัดเจนได้ จากที่อ่านผ่าน ๆ เทคนิคนี้ถูกปรับให้เหมาะกับ Apple Silicon, เร็วขึ้นประมาณ 2 เท่าที่ sparsity 75%, เป็นแบบ dynamic ที่ใช้ระหว่าง runtime ตามอินพุต และสามารถเลือกระดับ sparsity ได้
ในทางกลับกัน 2:4 semi-structured sparsity ถูกปรับให้เหมาะกับ GPU ที่มี sparse tensor core คือ Nvidia Ampere เป็นต้นไป, เร็วขึ้นประมาณ 2 เท่าที่ sparsity 50%, เป็นแบบ static ที่ใช้กับสถานะที่จัดเก็บของโมเดล และที่ sparsity 50% ก็มีโอกาสให้ผลลัพธ์แย่กว่าเทคนิคนี้
สิ่งที่อยากเห็นคือการเปรียบเทียบระหว่างผลลัพธ์ของ semi-structured sparsity ที่ sparsity 50% และเร็วขึ้น 2 เท่า กับผลลัพธ์ของเทคนิคนี้ที่ sparsity 75% และเร็วขึ้น 2 เท่า
ที่เลือก Apple Silicon เพราะพัฒนาได้ง่าย อัลกอริทึมนี้อาจทำผลงานได้ดีบนสถาปัตยกรรมอื่นด้วย
ในฐานะคนที่เคยใช้ CSR มาก่อน เรื่องนี้ไม่น่าแปลกใจ ในบรรดารูปแบบที่ใหม่กว่า อาจมีแบบที่เข้ากับคุณลักษณะของฮาร์ดแวร์ได้ดีกว่า เช่น block ELL
รูปแบบแบบนั้นหลีกเลี่ยงการอ่านที่ไม่ถูกรวมกันหรือ gather ได้ แต่โค้ดจะยุ่งยากขึ้น
bucketMul แทบไม่มีการอ่านที่ไม่ถูกรวมกัน และใช้โครงสร้างข้อมูลที่ต่างจาก CSR ทั่วไป อธิบายไว้ที่นี่: https://kolinko.github.io/effort/bucketmul.html
แบ่งแต่ละแถวของเมทริกซ์ออกเป็น 16 ส่วน แล้วเลือกว่าจะต้องอ่านส่วนไหน การเขียนเป็นแบบเชิงเส้นทั้งหมด
แต่ตอนนี้ผมก็ไม่แน่ใจว่าที่พูดอยู่สมเหตุสมผลไหม วันนี้ดึกแล้วและเป็นวันที่ยาวนาน
เป็นไอเดียและบทความที่ดี ผมเองก็ทำงานด้าน sparsity ในการอนุมานของโครงข่ายประสาทอยู่เหมือนกัน และนึกถึงบางประเด็นที่ควรรู้ไว้
เมื่อเทียบกับการทำ การคูณเมทริกซ์-เวกเตอร์ แบบหนาแน่น อัลกอริทึมนี้เพิ่มความซับซ้อนของอัลกอริทึม แต่ลดทราฟฟิกของหน่วยความจำ การคูณเมทริกซ์-เวกเตอร์มักติดคอขวดที่หน่วยความจำ ดังนั้นถ้าลดการเข้าถึงหน่วยความจำได้ throughput ก็จะสูงขึ้น อย่างไรก็ตาม หากขนาด batch มากกว่า 1 การเข้าถึงหน่วยความจำจะไม่ใช่คอขวดอีกต่อไป และมีความเป็นไปได้สูงที่ความเร็วที่เพิ่มขึ้นจะหายไปอย่างรวดเร็ว
สำหรับตัวเปรียบเทียบ ผมอยากเห็นไม่ใช่แค่โมเดลเดียวกัน แต่รวมถึงโมเดลอื่นที่มีสถาปัตยกรรมเร็วกว่า 2 เท่าด้วย เช่น ถ้าใช้วิธีนี้กับ LLM พารามิเตอร์ 13B ที่ sparsity 50% จะเทียบกับ LLM พารามิเตอร์ 7B เป็นอย่างไร และเทียบกับ LLM เดียวกันที่ quantize ให้เหลือครึ่งหนึ่งของ bitwidth อ้างอิงเป็นอย่างไร ถ้าภายในเวลาเท่ากันสามารถให้เอาต์พุตที่มี fidelity สูงกว่าเฟรมเวิร์กอนุมานเดิมได้ ก็น่าจะเป็นประเด็นสำหรับเปเปอร์ที่น่าสนใจ
เนื่องจากข้ามการคูณไป ข้อผิดพลาดจากการประมาณน่าจะเอนเอียงไปทางทำให้ค่าสัมบูรณ์เล็กกว่าผลลัพธ์จริงเสมอ ถ้าเพิ่ม เทอมชดเชย เพื่อแก้ systematic error นั้นได้ ประสิทธิภาพน่าจะดีขึ้นอีกเล็กน้อย
สัญกรณ์ Big-O อาจไม่เหมาะกับงานบน GPU มากนัก แต่ในกรณีนี้ก็ประมาณได้ถูกต้อง
ปัญหาหลักคือข้อจำกัดเชิงสถาปัตยกรรมของ GPU อัลกอริทึมนี้ต้องใช้รีจิสเตอร์/เธรดกรุ๊ป/แคชเมโมรี่มากกว่าวิธีดั้งเดิม และนั่นกลายเป็นคอขวดหลัก อีกทั้งการคูณทั้งหมดใช้ bucket ที่ต่างกัน จึงไม่ง่ายที่จะทำให้งานขนานกันเหมือนโมเดล MoE
สำหรับสถาปัตยกรรมที่ใหญ่ขึ้น ผมทดสอบค่อนข้างมากกับ Mixtral ซึ่งโดยพื้นฐานแล้วเป็นโมเดล 13B และจากความรู้สึกของผม มันรับมือได้ดีกว่ามาก ความเร็วอนุมานต่อ effort ยังคงอยู่ และคุณภาพต่อ effort ก็ยังให้ผลลัพธ์ที่อ่านได้ถึงระดับ 12~16% ไม่ใช่ 20~25% การทดสอบยังจำกัด และระหว่างใส่ implementation ของ Mistral ผมทำ implementation ของ Mixtral พัง เลยยังไม่มีข้อมูลที่แน่ชัด แต่ตั้งใจจะแก้เร็ว ๆ นี้
โดยสัญชาตญาณ ผมมองว่ายิ่งโมเดลใหญ่ ก็ยิ่งตัด effort ออกได้มากขึ้น
ตอนแรกผมก็เดาเหมือนกันว่าการข้ามการคูณจะทำให้เกิด bias แต่ผิดจากสัญชาตญาณ มันไม่ได้เป็นแบบนั้น มีกราฟอยู่บ้าง แต่ยังไม่พร้อมสำหรับเผยแพร่
เนื่องจากค่าต่าง ๆ ในเมทริกซ์กระจายอยู่ทั้งบวกและลบอย่างสม่ำเสมอ หลังผ่าน threshold หนึ่งไปแล้ว drift ของค่าผลลัพธ์จึงไม่มาก
ดูยอดเยี่ยมมาก แต่ latency 15ms นั้นใกล้กับ 16.7ms ของ vertical sync ที่ 60Hz
ถ้ากำลังอัปเดตหน้าจอทุกโทเคน อาจเป็นสาเหตุที่ทำให้เกิดการ sync ที่ไหนสักแห่งก็ได้
แต่ก็ขอบคุณสำหรับไอเดียครับ จะถือว่าเป็น contribution แรกจากชุมชน :D
เป็น contribution ที่ยอดเยี่ยมและเปิดกว้างจริง ๆ ผมจะจับตาดูว่า llama.cpp จะนำสิ่งนี้ไป implement หรือไม่
ผมกำลังหาวิธีทำให้การอนุมานบน CPU เร็วขึ้น และชอบไอเดียเรื่อง effort มาก
ชื่อนี้ chatgpt เป็นคนเสนอ พอมันบอกว่าไม่รู้จักแนวทางนี้ ก็เป็นไปได้ว่ามันอาจเป็นของใหม่จริง ๆ
ผมอยากติดต่อ llama.cpp และโปรเจกต์อื่น ๆ และหวังว่าจะมีการ implement เคยคิดว่าจะเขียนแพตช์ให้ llama เองด้วย แต่ C++ กับขนาดของโปรเจกต์นั้นหนักเกินไปสำหรับผม
การอนุมานบน CPU ก็น่าจะเร็วขึ้นเช่นกัน ยิ่งกว่านั้น เนื่องจากสามารถโหลดน้ำหนักเพียงบางส่วน เช่น ข้ามส่วนที่สำคัญน้อยกว่าแล้วโหลดแค่ 70% ได้ จึงน่าจะรันโมเดลด้วย VRAM น้อยกว่าเดิมได้ อย่างไรก็ตาม ยังจำเป็นต้องมี implementation แบบ Q8
ที่น่าสนใจคือ ตอนพยายามเทียบ benchmark กับ llama.cpp ผมหาความเร็ว 7B/FP16 บน MB Air 16GB ไม่เจอ เพราะโดยวิธีทั่วไปมันรันไม่ได้ แต่ด้วย Effort ทำได้
ในทำนองเดียวกัน ผมรัน Mixtral ความละเอียดเต็มแต่ถูกตัดทอนบน M2 96GB ได้ ปกติต้องใช้ RAM 114GB แต่เมื่อโหลดน้ำหนักแค่ 75% มันทำงานได้ลื่น ตอนนี้ implementation พังไปนิดจนให้เอาต์พุตขยะอยู่ เลยต้องแก้ก่อน
เป็นบทความที่ดี ผมสงสัยจริง ๆ ว่า ประสิทธิภาพต่อ VRAM เมื่อเทียบกับการ quantization แบบง่าย ๆ เป็นอย่างไร
และก็สงสัยด้วยว่ามีแผนจะ implement เวอร์ชันข้ามแพลตฟอร์มหรือไม่
แน่นอนว่าสามารถโหลดน้ำหนักให้น้อยลงได้ แต่ถ้าตัดน้ำหนักออกเกิน 20~30% คุณภาพดูเหมือนจะแย่ลงอย่างรวดเร็ว
กล่าวอีกอย่างคือ อัลกอริทึมนี้แยกเวลาอนุมานออกจากการใช้ VRAM
ถึงอย่างนั้น ผมเองก็สงสัยว่าเมื่อใช้ effort แล้ว Q8 ที่ตัดเหลือ 75% จะให้ผลลัพธ์ดีกว่า Q6 ได้หรือไม่
แต่คงต้องใช้เวลาอีกหลายสัปดาห์กว่าจะขัด implementation ให้พอทดสอบได้อย่างถูกต้อง
วิธีนี้ไม่ได้ retrain แต่ผมสงสัยว่าถ้าใช้แนวทางแบบนี้ร่วมกับ quantization แล้วค่อยฝึกเพิ่มเติมภายหลัง จะกู้คุณภาพที่เสียไปกลับมาได้บางส่วนหรือไม่
ดีที่ได้เห็นอะไรแบบนี้ และดีที่ช่วยให้จินตนาการได้ว่าต่อไปประสิทธิภาพและต้นทุนจะปรับปรุงได้แค่ไหน ขอบคุณที่พัฒนาเป็นโอเพนซอร์ส
แบบหนึ่งคือการฝึกหลังจากนั้น และอีกแบบคือ quantization-aware training ที่ทำ quantization ระหว่างการฝึก แต่ยังคง activation และ gradient ไว้ที่ความละเอียดเต็ม