เนื้อหาในเว็บไซต์นี้ได้รับการแปลโดยใช้ปัญญาประดิษฐ์ (AI) หรือเทคโนโลยีการแปลด้วยเครื่อง และอาจมีข้อผิดพลาด

Skip to content

วิธีที่เราขยายระบบ Bert ให้รองรับคำขอมากกว่า 1 พันล้านครั้งต่อวันบน CPU

นี่คือปัญหาคลาสสิกแบบไก่กับไข่สำหรับนักวิทยาศาสตร์ข้อมูลและวิศวกรแมชชีนเลิร์นนิง: เมื่อคุณกำลังพัฒนาโมเดลแมชชีนเลิร์นนิงใหม่สำหรับธุรกิจของคุณ คุณควรทำให้มันแม่นยำก่อน แล้วค่อยกังวลเรื่องความเร็วในการนำไปใช้งานจริง? หรือคุณควรทำให้มันสามารถทำงานได้เร็วตั้งแต่แรก แล้วค่อยทำให้แม่นยำ? ลองตั้งคำถามนี้ดู แล้วคุณจะเห็นการถกเถียงอย่างคึกคักตามมาเสมอ!

ในช่วงต้นปี 2019 เราต้องเผชิญกับสถานการณ์เดียวกันนี้เมื่อเราเริ่มพัฒนาตัวจำแนกข้อความรุ่นถัดไปสำหรับ Roblox โดยใช้โมเดลการเรียนรู้เชิงลึก Bert ที่ล้ำสมัย (ในขณะนั้น) เราไม่แน่ใจว่าโมเดล Bert ซึ่งเป็นเครือข่ายประสาทเทียมขนาดใหญ่ที่มีโมเดลพื้นฐาน "ระดับเริ่มต้น" เริ่มต้นที่ 110 ล้านพารามิเตอร์ จะสามารถตอบสนองความต้องการด้านความเร็วและปริมาณงานที่เข้มงวดในการผลิตได้หรือไม่ เราได้เดินหน้าพัฒนาตัวจำแนกข้อความที่ใช้ Bert เป็นฐานให้มีความแม่นยำสูง และในช่วงหลายเดือนที่ตึงเครียดที่ตามมา โชคดีที่เราสามารถทำให้มันเร็วพอสำหรับการผลิตได้ ดังนั้นสำหรับเรา "ไก่" (ความแม่นยำ) มาเป็นอันดับแรก แล้ว "ไข่" (ความเร็ว) มาเป็นอันดับหลัง แม้ว่านี่จะเป็นประสบการณ์ที่เครียดสำหรับเรา แต่มันไม่จำเป็นต้องเป็นเช่นนั้นสำหรับคุณ เพราะในบทความนี้เราจะมาแบ่งปันการปรับปรุงที่ทำให้การอนุมานของ Bert เร็วขึ้นสำหรับเรา ดังนั้นคุณสามารถเริ่มต้นด้วยไข่ (ซึ่งเป็นแนวทางที่รู้จักกันดีในการทำให้โมเดล Bert บางตัวทำงานได้เร็วในสภาพแวดล้อมการผลิต) จากนั้นจึงมุ่งเน้นไปที่ไก่ (ทำให้โมเดล Bert ของคุณมีความแม่นยำ)

การเดินทางของเราในการทำให้ Bert รวดเร็วในการผลิต

เวทมนตร์ของ Bert สำหรับการประมวลผลภาษาธรรมชาติ (NLP) ได้รับการบันทึกไว้อย่างดีในปี 2019 และที่ Roblox เราได้เห็นเวทมนตร์นั้นทำงานอย่างใกล้ชิดกับข้อมูลของเราเอง ด้วยการปรับแต่งโมเดลการเรียนรู้เชิงลึกของ Bert อย่างละเอียด เราได้เปลี่ยนแปลงแอปพลิเคชันการจำแนกประเภทข้อความและการจดจำนิติบุคคลที่มีชื่อ (NER) ของเราอย่างมาก โดยมักจะปรับปรุงประสิทธิภาพของโมเดล (คะแนน F1) ได้ดีขึ้นถึง 10 เปอร์เซ็นต์หรือมากกว่าเมื่อเทียบกับโมเดลก่อนหน้า

อย่างไรก็ตาม แม้ว่าการพัฒนาโมเดล Bert ของเราจะได้รับแรงสนับสนุนจากทรัพยากรที่น่าทึ่งและไลบรารีที่ยอดเยี่ยมมากมาย แต่เราพบว่ามีแหล่งข้อมูลเพียงไม่กี่แห่งที่แนะนำวิธีการปรับขนาด Bert บน PyTorch สำหรับการใช้งานในกรณีที่ต้องการความหน่วงต่ำและปริมาณงานสูงในระดับการผลิตจริง และสำหรับบริการ NLP หลายรายการของเรา เราจำเป็นต้องรองรับการอนุมานมากกว่า 25,000 ครั้งต่อวินาที (และมากกว่า 1 พันล้านครั้งต่อวัน) ที่ความหน่วงไม่เกิน 20 มิลลิวินาที

เป้าหมายของบทความนี้คือการแบ่งปันประสบการณ์ของเราในการเร่งความเร็วการอนุมานของ Bert มากกว่า 30 เท่าสำหรับการจำแนกประเภทข้อความที่ Roblox ผ่านการปรับปรุงต่างๆ (สรุปไว้ด้านล่าง) เราไม่เพียงแต่ทำให้ Bert ทำงานได้เร็วพอสำหรับผู้ใช้ของเราเท่านั้น แต่ยังประหยัดพอที่จะใช้งานในสภาพแวดล้อมการผลิตด้วยต้นทุนที่จัดการได้บน CPU

เกณฑ์มาตรฐาน

เพื่อวัตถุประสงค์ของบทความนี้ เราจะมุ่งเน้นไปที่การปรับปรุงความเร็วในการอนุมานของบริการจำแนกประเภทข้อความที่พัฒนาขึ้นโดยการปรับแต่ง Bert ที่นี่ "การอนุมาน" หมายถึงกระบวนการต่อเนื่องของการนำข้อความเป็นอินพุต แปลงเป็นโทเค็นเป็นเทนเซอร์อินพุต ป้อนเทนเซอร์นั้นเข้าสู่โมเดลที่ใช้ Bert ของเรา และส่งคืนป้ายกำกับจำแนกประเภทที่สร้างโดยโมเดล

ภาพของ "สะพานแก้ว" ในประเทศจีนด้านบนเป็นตัวอย่างที่ดีในการเปรียบเทียบกับความหน่วงเวลาและปริมาณงาน ความหน่วงเวลาเปรียบเสมือนเวลาที่ใช้ในการข้ามสะพานของคนหนึ่งคน ในขณะที่ปริมาณงานวัดจำนวนคนที่สามารถข้ามสะพานได้ในช่วงเวลาที่กำหนด

เมื่อใดก็ตามที่เราต้องการปรับปรุงความเร็วอย่างเป็นระบบ เราจำเป็นต้องเลือกเกณฑ์มาตรฐาน เกณฑ์มาตรฐานที่เลือกสำหรับตัวจำแนก Bert ของเรามีสองส่วนประกอบ:

  • ความล่าช้า: เวลาเฉลี่ยที่ใช้ในการให้บริการคำขอการอนุมานหนึ่งครั้ง (เรายังมีเกณฑ์มาตรฐานที่ 99 เปอร์เซ็นต์ไทล์ภายในองค์กรด้วย)
  • ปริมาณงาน: จำนวนการอนุมานที่เราสามารถให้บริการได้ในหนึ่งวินาที

เพื่อการเปรียบเทียบปริมาณงานที่สม่ำเสมอ โค้ดมาตรฐานของเราได้เริ่มต้นกระบวนการทำงานจำนวน 32 กระบวนการ ซึ่งแต่ละกระบวนการจะดำเนินการตามคำขอการอนุมานจำนวน 1,000 คำขออย่างต่อเนื่องตามลำดับ การทดสอบมาตรฐานนี้ได้ดำเนินการบนเซิร์ฟเวอร์เดียวที่มีหน่วยประมวลผล Xeon Scalable จำนวน 36 คอร์

การทดสอบมาตรฐานนี้จำลองสภาพแวดล้อมการผลิตจริงที่มีบริการจำแนกประเภทข้อความซึ่งได้รับการสนับสนุนโดยกลุ่มของกระบวนการทำงาน (worker processes) ภายใต้สภาวะที่มีโหลด แต่ละกระบวนการทำงานจะยุ่งอยู่กับการประมวลผลคำขอการอนุมานต่อเนื่องเป็นระยะเวลานาน ในสภาพแวดล้อมการผลิตจริงของเรา เรามีกระบวนการทำงานเหล่านี้หลายร้อยตัวกระจายอยู่บนเครื่องหลายเครื่อง เพื่อให้สามารถรองรับคำขอได้มากกว่า 25,000 คำขอต่อวินาที โดยมีค่าความล่าช้าเฉลี่ยต่ำกว่า 20 มิลลิวินาที

เครื่องมือ

โมเดล Bert ที่ปรับแต่งอย่างละเอียดของเราได้รับการพัฒนาโดยใช้ไลบรารี Huggingface Transformers v2.3.0 เราเลือกใช้ Huggingface โดยเฉพาะเนื่องจากรองรับ PyTorch และขณะนี้เรากำลังใช้งานจริงกับ PyTorch 1.4.0 Huggingface ช่วยให้เราทดลองใช้โมเดล transformer อื่นๆ อีกมากมายได้อย่างง่ายดาย รวมถึง Roberta, XLNet, DistilBert, Albert และอื่นๆ อีกมากมาย นี่คือวิธีที่เราค้นพบ DistilBert ซึ่งช่วยปรับปรุงความเร็วในการอนุมานของเราอย่างมาก (เราจะพูดถึงเรื่องนี้ในอีกสักครู่)

เริ่มต้น: CPU หรือ GPU สำหรับการอนุมาน?

การตัดสินใจครั้งใหญ่ครั้งแรกของเราคือการเลือกระหว่างการรันการอนุมานสำหรับตัวจำแนกข้อความที่ใช้ Bert บน CPU หรือ GPU

สำหรับการฝึกอบรมโมเดลของเรา GPU นั้นเร็วกว่า CPU อย่างไม่ต้องสงสัย การปรับแต่ง (ฝึกอบรม) โมเดล Bert สำหรับการจำแนกประเภทข้อความของเราใช้เวลานานกว่าบน CPU ถึง 10 เท่าเมื่อเทียบกับ GPU แม้จะเปรียบเทียบกับ Tesla V100 GPU กับเซิร์ฟเวอร์ที่ใช้ CPU Xeon Scalable 36 คอร์ที่มีราคาใกล้เคียงกันก็ตาม

สำหรับการอนุมาน การเลือกระหว่าง GPU และ CPU ขึ้นอยู่กับแอปพลิเคชัน นี่คือปัจจัยที่ส่งผลต่อการตัดสินใจของเราในการเลือกใช้ CPU สำหรับการอนุมาน:

  • ความเรียบง่าย. GPUs สามารถปรับขนาดได้ดีที่สุดเมื่อมีการจัดกลุ่มข้อมูลเข้า. อย่างไรก็ตาม ตัวจัดหมวดหมู่ข้อความของเราทำงานในสภาพแวดล้อมแบบเรียลไทม์แบบคำขอ-คำตอบ. ดังนั้น การจัดกลุ่มคำขอแบบเรียลไทม์เหล่านี้จะสร้างภาระและเพิ่มความซับซ้อนเท่านั้น. การทำอินเฟอร์เรนซ์บน CPU มอบเส้นทางที่ง่ายกว่าให้กับเรา เพราะบน CPU แบบจำลอง Bert ของเราทำงานได้ดีมากแม้เมื่อเราไม่ได้จัดกลุ่มข้อมูลเข้าของมัน.
  • เศรษฐศาสตร์ต้นทุนที่เอื้ออำนวย สำหรับตัวจำแนกข้อความของเรา เศรษฐศาสตร์ต้นทุนของการอนุมานบน CPU ดีกว่าบน GPU หลังจากนำการปรับปรุงการปรับขนาดทั้งหมดในบทความนี้ไปใช้แล้ว ปริมาณงานของบริการจำแนกประเภทข้อความของเราสูงขึ้น 6 เท่าต่อดอลลาร์เมื่อใช้ CPU เมื่อเทียบกับ GPU โดยเฉพาะอย่างยิ่ง เราสามารถปรับขนาดบริการที่ใช้ Bert ของเราให้รองรับการอนุมานได้มากกว่า 3,000 ครั้งต่อวินาทีบนเซิร์ฟเวอร์ Intel Xeon Scalable 36 คอร์ เทียบกับ 500 ครั้งต่อวินาทีบน GPU Tesla V100 ที่มีต้นทุนเทียบเท่ากัน

คำแนะนำเกี่ยวกับ CPU

สำหรับการเปรียบเทียบมาตรฐานของเรา เราพบว่าโปรเซสเซอร์ Intel Xeon Scalable รุ่นที่ 2 ทำงานได้ดีที่สุดเนื่องจากการรองรับการอนุมานการเรียนรู้เชิงลึกที่ยอดเยี่ยม ตัวอย่างเช่น การเร่งความเร็วที่มีผลกระทบมากที่สุดบางประการของเราใช้ประโยชน์จากคำสั่ง Vector Neural Network และแกน Fused-Multiply-Add (FMA) แบบคู่ที่มีอยู่ในโปรเซสเซอร์ Xeon Scalable รุ่นที่ 2 ผลลัพธ์ทั้งหมดที่เรารายงานในบทความนี้อยู่บนชิปซีรีส์ Intel Xeon Scalable โปรเซสเซอร์ประสิทธิภาพของคุณอาจแตกต่างกันไปในการกำหนดค่า CPU อื่นๆ เนื่องจากการทดสอบของเราบนชิปที่แตกต่างกันมีข้อจำกัด

การปรับแต่งเธรด

อุปสรรคแรกที่เราพบในการขยายการอนุมานของ Bert บน CPU คือ PyTorch ต้องได้รับการปรับแต่งให้เหมาะสมกับเธรดก่อนที่กระบวนการทำงานหลายตัวจะสามารถทำการอนุมานโมเดลพร้อมกันได้ นี่เป็นเพราะภายในแต่ละกระบวนการ โมเดล PyTorch พยายามใช้หลายคอร์ในการจัดการแม้แต่คำขอการอนุมานเพียงหนึ่งเดียว ดังนั้นแต่ละกระบวนการจึงแข่งขันกันเพื่อใช้ทรัพยากรที่มีจำกัดเดียวกัน (คอร์ทางกายภาพ) ส่งผลให้เกิดการชะงักเมื่อมีกระบวนการทำงานเหล่านี้มากเกินไปทำงานพร้อมกันในเครื่องเดียวกัน

โชคดีที่วิธีแก้ไขปัญหานี้เป็นเรื่องง่าย: ในแต่ละกระบวนการทำงานที่ทำการอนุมาน เราเพียงแค่ตั้งค่าจำนวนเธรดเป็น 1 ตามที่แสดงในบรรทัดที่ 9 ด้านล่าง

ผลกระทบจากการทำเช่นนี้คืออะไร? เมื่อมีกระบวนการทำงานของผู้ใช้จำนวนมากกำลังทำงานบนเครื่องเดียวกัน การปรับขนาดเป็นไปอย่างเป็นระเบียบมากขึ้น เนื่องจากแต่ละกระบวนการทำงานใช้เพียงหนึ่งคอร์เท่านั้น นี่คือสิ่งที่ทำให้เราสามารถปรับขนาดไปยังกระบวนการทำงานจำนวนมากบนเครื่องเดียวได้ ทำให้เราสามารถเพิ่มปริมาณงานต่อเครื่องในขณะที่ยังคงรักษาความหน่วงเวลาที่ยอมรับได้

สถานการณ์ที่ 1: เบิร์ต ฐานข้อมูล

เบสไลน์แรกคือโมเดล Bert แบบพื้นฐานสำหรับการจำแนกข้อความ หรือสถาปัตยกรรมที่อธิบายไว้ในเอกสารต้นฉบับของ Bert โมเดล Bert นี้ถูกสร้างขึ้นโดยใช้โมเดล BertForSequenceClassication Pytorch จากไลบรารี Huggingface Transformers 2.3.0 ในสถานการณ์นี้ เราได้ทำการออกแบบอย่างจงใจให้ดูไม่ซับซ้อน - เราเติมศูนย์ที่ด้านหน้าของอินพุตเทนเซอร์ทั้งหมดเพื่อให้มีความยาวคงที่ที่ 128 โทเค็น เหตุผลที่ทำเช่นนี้คือ การเติมศูนย์ที่ด้านหน้าเป็นสิ่งจำเป็นเมื่อทำการจัดกลุ่มอินพุต เพื่อให้อินพุตทั้งหมดมีขนาดเท่ากัน เนื่องจากง่ายที่จะเติมศูนย์ที่ด้านหน้าโดยไม่ได้ตั้งใจแม้เมื่อขนาดของกลุ่มเป็น 1 เราจึงต้องการวัดผลกระทบต่อประสิทธิภาพของการทำเช่นนี้ในสถานการณ์พื้นฐานนี้

ในกล่องสีชมพูด้านล่างนี้ เราแสดงผลลัพธ์การเปรียบเทียบมาตรฐานสำหรับสถานการณ์ "Bert Baseline" นี้ แม้ที่ 330 มิลลิวินาที ความหน่วงก็ยังคงสูงเกินไปสำหรับความต้องการในการผลิตของเรา และปริมาณงานที่ได้ก็แย่มากเมื่อเทียบกับจำนวนคอร์ที่ใช้ในการทดสอบ

สถานการณ์ที่ 2: โมเดลขนาดเล็ก (DistilBert)

หลังจากที่ Bert ได้รับการปล่อยออกมาในเดือนตุลาคม 2018 ก็มีโมเดลจำนวนมากที่พยายามผลักดันขีดความสามารถของเทคโนโลยีให้ก้าวหน้ายิ่งขึ้นด้วยการสร้างโมเดลที่มีพารามิเตอร์มากขึ้น ในขณะที่ Bert base มีพารามิเตอร์ 110 ล้านตัว โมเดลใหม่บางตัวกลับมีพารามิเตอร์มากกว่า 1 พันล้านตัว ซึ่งให้ประสิทธิภาพทางสถิติที่สูงกว่าเพียงเล็กน้อย แต่ประสิทธิภาพในการคำนวณกลับแย่ลงอย่างมาก อย่างไรก็ตาม สิ่งที่เราต้องการคือประสิทธิภาพในการคำนวณที่ดีกว่า และเราพร้อมที่จะยอมเสียสละประสิทธิภาพทางสถิติบางส่วนเพื่อให้ได้มาซึ่งสิ่งนั้น

โชคดีสำหรับพวกเรา ในเดือนตุลาคม 2019 Sanh และคณะได้แนะนำ DistilBert ซึ่งเป็นหนึ่งในหลาย ๆ โมเดลที่สวนกระแสของโมเดลขนาดใหญ่แบบ Bert โมเดลเหล่านี้ที่เรียกว่า "โมเดลขนาดเล็ก" มุ่งเน้นไปที่การลดขนาดของ Bert ซึ่งนำไปสู่การอนุมานและการฝึกฝนที่เร็วขึ้นมาก โดยมีการสูญเสียประสิทธิภาพทางสถิติเพียงเล็กน้อยเท่านั้น

เราเลือกใช้ DistilBert ด้วยเหตุผลหลักสองประการ ประการแรก DistilBert มีความเร็วประมาณสองเท่าของ Bert แต่ประสิทธิภาพทางสถิติ (ค่า F1 score) ในการจำแนกข้อความของเรานั้นอยู่ในระดับที่ใกล้เคียงกับ Bert ภายใน 1% ประการที่สอง ไลบรารี Huggingface Transformers ทำให้เราสามารถเปลี่ยนจาก DistilBert เป็น Bert ได้อย่างง่ายดาย ช่วยให้เราสามารถรักษาขั้นตอนการทำงานในการฝึกและอนุมานของเราไว้ได้โดยไม่เปลี่ยนแปลง

นี่คือผลลัพธ์ของเกณฑ์มาตรฐานเมื่อเราใช้โมเดล DistilBert ขนาดเล็กแทนที่จะใช้ Bert ขนาดใหญ่ ตามที่เราเห็น เราสามารถลดความล่าช้าลงเกือบครึ่งหนึ่ง ในขณะที่เพิ่มปริมาณงานเกือบสองเท่า

สถานการณ์ที่ 3: ข้อมูลขนาดเล็ก (รูปทรงแบบไดนามิก)

การปรับปรุงครั้งต่อไปของเราเกิดจากการทำให้สิ่งอื่นเล็กลง ในกรณีนี้คือการลดขนาดของอินพุตเทนเซอร์ที่ป้อนเข้าสู่โมเดล DistilBert

ก่อนอื่นขออธิบายพื้นฐานเล็กน้อยเกี่ยวกับการเติมศูนย์ (zero-padding) โมเดลการเรียนรู้เชิงลึกโดยทั่วไปจะคาดหวังให้ได้รับชุดข้อมูลของเทนเซอร์เป็นอินพุต สำหรับตัวจำแนกข้อความของเรา นั่นหมายถึงการแปลงข้อความอินพุตให้เป็นเทนเซอร์ผ่านการแบ่งโทเค็นด้วย Bert จากนั้นจึงนำเทนเซอร์เหล่านั้นมารวมเป็นชุด (batch) เนื่องจากข้อความมีความยาวไม่แน่นอน เราจึงต้องเติมศูนย์ที่ด้านหน้าของเทนเซอร์ที่มีความยาวไม่คงที่เหล่านี้เพื่อให้ได้ชุดข้อมูลที่มีขนาดคงที่ ดังตัวอย่างต่อไปนี้:

ตามที่ได้กล่าวไว้ก่อนหน้านี้ ตัวจัดหมวดหมู่ข้อความของเราทำงานในสภาพแวดล้อมแบบเรียลไทม์แบบคำขอ-คำตอบ ดังนั้น ขนาดของชุดข้อมูลที่เหมาะสมที่สุดคือ 1 ซึ่งหมายความว่าเราไม่จำเป็นต้องเติมศูนย์ที่ด้านหน้าของเทนเซอร์เลย เนื่องจากเทนเซอร์ทั้งหมดมีขนาดเท่ากันเมื่อขนาดของชุดข้อมูลคือ 1 นี่คือลักษณะของข้อความนำเข้าจากข้างต้นเมื่อเราส่งไปยังโมเดลในชุดข้อมูลแยกกันโดยไม่เติมศูนย์:

เราเรียกสิ่งนี้ว่า "รูปร่างแบบไดนามิก" เนื่องจากเราอนุญาตให้เทนเซอร์มีความยาวแปรผันในแต่ละแบทช์ การเปลี่ยนไปใช้อินพุตที่มีรูปร่างแบบไดนามิกช่วยปรับปรุงปริมาณงานและความหน่วงของเราได้อย่างมาก โดยสัญชาตญาณแล้ว สิ่งนี้สมเหตุสมผลเพราะเรากำลังทำให้อินพุตของโมเดลเล็กลงโดยไม่มีการเติมศูนย์ นอกจากนี้ ยังไม่มีผลกระทบเชิงลบต่อคะแนน F1 ของเรา เนื่องจากความน่าจะเป็นของผลลัพธ์โมเดลของเราเท่าเดิมไม่ว่าจะใช้รูปร่างแบบไดนามิกหรือการเติมศูนย์

นี่คือผลลัพธ์ของเกณฑ์มาตรฐานเมื่อเราใช้ DistilBert + Dynamic Shapes ดังที่เห็น เราสามารถลดความหน่วงลงได้มากกว่าครึ่งในขณะที่เพิ่มปริมาณงานเป็นสองเท่า

สถานการณ์ที่ 4: น้ำหนักที่น้อยลง (การควอนไทซ์)

เราเห็นการเพิ่มประสิทธิภาพที่ใหญ่ที่สุดจากน้ำหนักที่น้อยกว่า ซึ่งได้มาผ่านเทคนิคที่เรียกว่าการควอนไทซ์

การควอนไทซ์เกี่ยวข้องกับการปรับปรุงประสิทธิภาพของการคำนวณการเรียนรู้เชิงลึกผ่านการแทนค่าที่เล็กลงของน้ำหนักของโมเดล เช่น การแทนค่าน้ำหนักแบบจุดลอยตัว 32 บิตเป็นจำนวนเต็ม 8 บิต เทคนิคการควอนไทซ์เฉพาะที่เราใช้สำหรับโมเดล DistilBert ของเราคือ "การควอนไทซ์แบบไดนามิก" เทคนิคนี้เกี่ยวข้องกับการควอนไทซ์น้ำหนักหลังการฝึกฝน แทนที่จะทำการควอนไทซ์ระหว่างการฝึกฝน (ซึ่งเรียกว่าการฝึกฝนที่คำนึงถึงการควอนไทซ์)

การรองรับการควอนไทซ์แบบไดนามิกใน Pytorch 1.3+ นั้นง่ายมากในการนำไปใช้ และการเปลี่ยนแปลงเพียงบรรทัดเดียวในโค้ดของเราทำให้สามารถใช้การแทนค่าแบบจำนวนเต็ม 8 บิตสำหรับน้ำหนักในชั้นเชิงเส้นทั้งหมดของโมเดล DistilBert ได้:

นี่คือการแสดงภาพว่าควอนไทซ์นี้เปลี่ยนแปลงโมเดล DistilBert ดั้งเดิมอย่างไร ดังที่คุณเห็น PyTorch จะแทนที่เลเยอร์ "Linear" เช่น เลเยอร์ Query (Q), Key (K) และ Value (V) ของการประมวลผลแบบ Attention ด้วยเลเยอร์ "Dynamic Quantized Linear" ซึ่งจะใช้อินทิกรัล 8 บิตที่ผ่านการควอนไทซ์แล้วในการดำเนินการคูณ/บวกภายใน
นี่คือผลลัพธ์ของเกณฑ์มาตรฐานเมื่อเราใช้ Distilbert + Dynamic Shapes + Dynamic Quantization เราจะเห็นได้ว่าการปรับปรุงทั้งสามอย่างนี้ร่วมกันสร้างการปรับปรุงอย่างมากถึง 30 เท่าเมื่อเทียบกับค่าพื้นฐานของ Bert แบบธรรมดาของเรา ทั้งในด้านความล่าช้าและปริมาณงาน

เรายังสังเกตเห็นผลกระทบเชิงลบเล็กน้อยต่อ F1 (น้อยกว่า 1%) หลังจากการควอนไทซ์ แต่การปรับปรุงในแง่ของปริมาณงานและความหน่วงทำให้คุ้มค่ามาก

สถานการณ์ที่ 5: จำนวนคำขอที่น้อยลง (การแคช)

การปรับปรุงประสิทธิภาพขั้นสุดท้ายคือการลดจำนวนคำขอที่ส่งไปยังโมเดล DistilBert อย่างมีประสิทธิภาพ เราทำเช่นนี้โดยการแคชคำตอบสำหรับข้อความที่ป้อนซ้ำโดยใช้ token ids เป็นกุญแจ การทำงานนี้ได้ผลเนื่องจากคำตอบของโมเดลสำหรับข้อความที่ป้อนซ้ำจะเหมือนกันเสมอ นอกจากนี้ ประสิทธิภาพจะเพิ่มขึ้นเมื่อการกระจายข้อความพื้นฐานมีความไม่สม่ำเสมอมากขึ้น

สำหรับข้อมูลของเรา เราได้สังเกตการณ์เชิงประจักษ์ว่าอัตราการเข้าถึงแคช (cache hit rate) อยู่ที่ 40% ในระบบผลิตจริง เมื่อเราแคชข้อมูลจำนวน 1 ล้านรายการไว้ในหน่วยความจำกระบวนการ (process memory) เมื่อพิจารณาว่าการเข้าถึงแคชแต่ละครั้งหมายถึงต้นทุนที่แท้จริงเป็นศูนย์สำหรับการอนุมาน แคชของเราจึงช่วยเพิ่มประสิทธิภาพการประมวลผลเกือบสองเท่า

เราไม่ได้รวมผลกระทบของการแคชไว้ในเกณฑ์มาตรฐานของเรา เนื่องจากมันขึ้นอยู่กับข้อมูล แต่เราต้องการให้แน่ใจว่าเราได้กล่าวถึงผลกระทบที่สำคัญของการแคชอย่างง่าย

หมายเหตุเกี่ยวกับการขยายขนาดในแนวนอน

ผลลัพธ์การทดสอบประสิทธิภาพทั้งหมดที่คุณเห็นในบทความนี้มาจากการรันด้วยผู้ปฏิบัติงานพร้อมกัน 32 คนบนเซิร์ฟเวอร์เดียวกัน เพื่อรองรับการขยายตัวมากกว่า 1 พันล้านคำขอต่อวันในสภาพแวดล้อมการผลิต เราเพียงแค่ขยายจำนวนผู้ปฏิบัติงานในแนวนอนไปยังเซิร์ฟเวอร์หลายเครื่อง

ความเป็นไปได้ในอนาคต

แม้ว่าเราได้พูดคุยเกี่ยวกับการปรับแต่งรายละเอียดปลีกย่อยที่จำเป็นมากมายเพื่อให้โมเดล Bert PyTorch ของเราจากห้องปฏิบัติการไปสู่การผลิตแล้ว แต่เราตระหนักดีว่ามีการนวัตกรรมมากมายเกิดขึ้นในพื้นที่นี้ซึ่งอาจส่งผลกระทบต่อกลยุทธ์การขยายขนาดของเราในอนาคต ตัวอย่างเช่น เรากำลังจับตาดู Onnx Runtime อย่างใกล้ชิด เนื่องจากมันมีประสิทธิภาพที่โดดเด่นเมื่อเทียบกับเกณฑ์มาตรฐานที่ไม่ผ่านการควอนไทซ์ของเรา ณ เวลาที่เขียนบทความนี้ Microsoft กำลังเปิดเผยโค้ด Bert optimization สำหรับ Onnx Runtime แบบโอเพนซอร์ส และเรากำลังทำงานอย่างใกล้ชิดกับ Intel เช่นกันเพื่อสำรวจโอกาสในการปรับปรุงประสิทธิภาพร่วมกับ OpenVino

บทสรุปและข้อสรุป

ข้อสรุปหลักของเราคือ DistilBert/Bert มีความสามารถในการขยายตัวที่ดีบน CPU โดยเฉพาะสำหรับการจำแนกข้อความแบบเรียลไทม์ ในบทความนี้ เราจะอธิบายวิธีที่เราสามารถเพิ่มความเร็วในการประมวลผลและลดความหน่วงของการจำแนกข้อความด้วย Bert ได้อย่างน้อย 30 เท่า โดยทำให้ทุกอย่างเล็กลง - ทั้งโมเดลที่เล็กลง (DistilBert) ข้อมูลนำเข้าที่เล็กลง (Dynamic Shapes) และค่าน้ำหนักที่เล็กลง (Quantization) เรารู้สึกยินดีเป็นอย่างยิ่งที่สามารถให้บริการคำขอได้มากกว่า 1 พันล้านครั้งต่อวันด้วยตัวจำแนกประเภทด้วยระบบเรียนรู้เชิงลึกของเรา และเราสามารถทำสิ่งนี้ได้ในต้นทุนที่เหมาะสมบน CPU ด้วยค่าความหน่วงเฉลี่ยต่ำกว่า 20 มิลลิวินาที

ขอขอบคุณ Edin Mulalić และ Saša Anđelković เป็นอย่างยิ่งสำหรับความช่วยเหลือในการสืบค้นเทคนิคต่าง ๆ เหล่านี้

ทั้งบริษัท Roblox Corporation และบล็อกนี้ไม่ได้รับรองหรือสนับสนุนบริษัทหรือบริการใด ๆ ทั้งสิ้น นอกจากนี้ ไม่มีการรับประกันหรือคำมั่นสัญญาใด ๆ เกี่ยวกับความถูกต้อง ความน่าเชื่อถือ หรือความสมบูรณ์ของข้อมูลที่ปรากฏในบล็อกนี้

บทความบล็อกนี้ได้รับการเผยแพร่ครั้งแรกบนบล็อกเทคโนโลยีของ Roblox