แบ็กเอนด์คันเร่ง

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

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

Swift สำหรับ TensorFlow มีแบ็กเอนด์สองรายการสำหรับดำเนินการคำนวณแบบเร่งความเร็ว: โหมดกระตือรือร้น TensorFlow และ X10 แบ็กเอนด์เริ่มต้นคือโหมดกระตือรือร้นของ TensorFlow แต่สามารถแทนที่ได้ มี บทช่วยสอนแบบโต้ตอบ ที่จะแนะนำคุณเกี่ยวกับการใช้แบ็กเอนด์ที่แตกต่างกันเหล่านี้

โหมดกระตือรือร้น TensorFlow

แบ็คเอนด์โหมดกระตือรือร้นของ TensorFlow ใช้ประโยชน์จาก TensorFlow C API เพื่อส่งการทำงาน Tensor แต่ละรายการไปยัง GPU หรือ CPU ทันทีที่พบ ผลลัพธ์ของการดำเนินการนั้นจะถูกดึงข้อมูลและส่งต่อไปยังการดำเนินการถัดไป

การจัดส่งแบบดำเนินการทีละขั้นตอนนี้เข้าใจได้ง่ายและไม่ต้องมีการกำหนดค่าที่ชัดเจนภายในโค้ดของคุณ อย่างไรก็ตาม ในหลายกรณี ไม่ได้ส่งผลให้ได้ประสิทธิภาพที่ดีที่สุด เนื่องจากมีค่าใช้จ่ายจากการส่งการดำเนินการเล็กๆ จำนวนมากออกไป รวมกับการขาดการผสมผสานการดำเนินการและการเพิ่มประสิทธิภาพที่อาจเกิดขึ้นได้เมื่อมีกราฟของการดำเนินการอยู่ สุดท้ายนี้ โหมดกระตือรือร้นของ TensorFlow เข้ากันไม่ได้กับ TPU และใช้ได้กับ CPU และ GPU เท่านั้น

X10 (การติดตามแบบ XLA)

X10 เป็นชื่อของแบ็กเอนด์ Swift สำหรับ TensorFlow ที่ใช้การติดตามเทนเซอร์แบบขี้เกียจและ คอมไพเลอร์ที่ปรับให้เหมาะสม XLA ในหลาย ๆ กรณีปรับปรุงประสิทธิภาพที่เหนือกว่าการจัดส่งแบบปฏิบัติการต่อการปฏิบัติงานอย่างมีนัยสำคัญ นอกจากนี้ยังเพิ่มความเข้ากันได้สำหรับ TPU ซึ่งเป็นตัวเร่งความเร็วที่ได้รับการปรับให้เหมาะสมโดยเฉพาะสำหรับประเภทการคำนวณที่พบในโมเดลการเรียนรู้ของเครื่อง

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

let tensor1 = Tensor<Float>([0.0, 1.0, 2.0], on: Device.defaultXLA)
let tensor2 = Tensor<Float>([1.5, 2.5, 3.5], on: Device.defaultXLA)

หลังจากนั้น การอธิบายการคำนวณจะเหมือนกับโหมดกระตือรือร้นของ TensorFlow ทุกประการ:

let tensor3 = tensor1 + tensor2

สามารถให้รายละเอียดเพิ่มเติมได้เมื่อสร้าง Tensor เช่น ตัวเร่งความเร็วชนิดใดที่จะใช้ และแม้แต่ตัวเร่งความเร็วใด หากมีหลายตัว ตัวอย่างเช่น คุณสามารถสร้าง Tensor บนอุปกรณ์ TPU ตัวที่สองได้ (สมมติว่าโฮสต์ที่โปรแกรมกำลังทำงานอยู่มองเห็นได้) โดยใช้สิ่งต่อไปนี้:

let tpuTensor = Tensor<Float>([0.0, 1.0, 2.0], on: Device(kind: .TPU, ordinal: 1, backend: .XLA))

ไม่มีการเคลื่อนไหวโดยปริยายของ Tensor ระหว่างอุปกรณ์ ดังนั้นหากใช้ Tensor สองตัวบนอุปกรณ์ที่แตกต่างกันในการดำเนินการร่วมกัน ข้อผิดพลาดรันไทม์จะเกิดขึ้น หากต้องการคัดลอกเนื้อหาของ Tensor ไปยังอุปกรณ์ใหม่ด้วยตนเอง คุณสามารถใช้เครื่องมือเริ่มต้น Tensor(copying:to:) ได้ โครงสร้างขนาดใหญ่บางแห่งที่มีเทน Tensor อยู่ภายใน เช่น โมเดลและเครื่องมือเพิ่มประสิทธิภาพ มีฟังก์ชันตัวช่วยสำหรับการย้าย Tensor ภายในทั้งหมดไปยังอุปกรณ์ใหม่ในขั้นตอนเดียว

ต่างจากโหมดกระตือรือร้นของ TensorFlow ตรงที่การดำเนินการที่ใช้แบ็กเอนด์ X10 จะไม่ถูกจัดส่งแยกกันเมื่อพบ แต่การสั่งงานไปยังเครื่องเร่งความเร็วจะถูกกระตุ้นโดยการอ่านค่าที่คำนวณกลับไปยังโฮสต์หรือโดยการวางสิ่งกีดขวางที่ชัดเจนเท่านั้น วิธีการทำงานคือรันไทม์เริ่มต้นจากค่าที่อ่านไปยังโฮสต์ (หรือการคำนวณครั้งสุดท้ายก่อนอุปสรรคแบบแมนนวล) และติดตามกราฟของการคำนวณที่ส่งผลให้เกิดค่านั้น

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

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

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

การรองรับแบบผสมความแม่นยำใน X10

รองรับการฝึกอบรมที่มีความแม่นยำแบบผสมผ่าน X10 และมีทั้ง API ระดับต่ำและระดับสูงเพื่อควบคุม API ระดับต่ำ มีคุณสมบัติในการคำนวณสองประการ: toReducedPrecision และ toFullPrecision ซึ่งจะแปลงระหว่างความแม่นยำเต็มและความแม่นยำที่ลดลง พร้อมด้วย isReducedPrecision เพื่อสืบค้นความแม่นยำ นอกจาก Tensor แล้ว โมเดลและเครื่องมือเพิ่มประสิทธิภาพยังสามารถแปลงระหว่างความแม่นยำเต็มและความแม่นยำลดลงได้โดยใช้ API นี้

โปรดทราบว่าการแปลงเป็นความแม่นยำที่ลดลงจะไม่เปลี่ยนประเภทลอจิคัลของ Tensor หาก t เป็น Tensor<Float> t.toReducedPrecision ก็เป็น Tensor<Float> ด้วยการนำเสนอพื้นฐานที่มีความแม่นยำลดลง

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