מאיץ אחורי

זה די פשוט לתאר חישוב Tensor , אבל מתי ואיך החישוב הזה יתבצע תלוי באיזה קצה אחורי משמש עבור Tensor s ומתי יש צורך בתוצאות במעבד המארח.

מאחורי הקלעים, פעולות על מכשירי Tensor נשלחות למאיצים כמו GPUs או TPUs , או פועלות על ה-CPU כאשר אין מאיץ זמין. זה קורה אוטומטית עבורך, ומקל על ביצוע חישובים מקבילים מורכבים באמצעות ממשק ברמה גבוהה. עם זאת, זה יכול להיות שימושי כדי להבין כיצד ההפצה הזו מתרחשת ולהיות מסוגל להתאים אותה לביצועים מיטביים.

ל- Swift for TensorFlow יש שני קצה אחוריים לביצוע חישוב מואץ: TensorFlow eager mode ו-X10. ברירת המחדל האחורית היא מצב להוט של TensorFlow, אך ניתן לעקוף זאת. מדריך אינטראקטיבי זמין שידריך אותך דרך השימוש בקצה האחורי השונים הללו.

מצב להוט של TensorFlow

הקצה האחורי של מצב TensorFlow להוט ממנף את ה-API של TensorFlow C כדי לשלוח כל פעולת Tensor ל-GPU או ל-CPU כאשר היא נתקלת. לאחר מכן מאחזרים את התוצאה של אותה פעולה ומועברת לפעולה הבאה.

שליחת פעולה לפי פעולה זו היא פשוטה להבנה ואינה דורשת תצורה מפורשת בתוך הקוד שלך. עם זאת, במקרים רבים זה לא מביא לביצועים אופטימליים בגלל התקורה של שליחת פעולות קטנות רבות, בשילוב עם היעדר היתוך ואופטימיזציה של פעולות שיכולים להתרחש כאשר קיימים גרפים של פעולות. לבסוף, מצב TensorFlow eager אינו תואם ל-TPUs, וניתן להשתמש בו רק עם CPUs ו-GPUs.

X10 (מעקב מבוסס XLA)

X10 הוא שמו של הקצה האחורי של Swift for TensorFlow שמשתמש במעקב אחר טנסור עצלן ובקומפיילר האופטימיזציה של XLA כדי במקרים רבים לשפר משמעותית את הביצועים על פני שיגור פעולה אחר פעולה. בנוסף, הוא מוסיף תאימות ל- TPUs , מאיצים שהותאמו במיוחד לסוגי החישובים שנמצאים במודלים של למידת מכונה.

השימוש ב-X10 עבור חישובי Tensor אינו ברירת המחדל, לכן עליך להצטרף ל-backend זה. זה נעשה על ידי ציון כי 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 s בין מכשירים, כך שאם שני Tensor s בהתקנים שונים משמשים בפעולה יחד, תתרחש שגיאת זמן ריצה. כדי להעתיק ידנית את התוכן של Tensor למכשיר חדש, אתה יכול להשתמש באתחול Tensor(copying:to:) . לכמה מבנים בקנה מידה גדול יותר המכילים Tensor בתוכם, כמו דגמים ואופטימיזרים, יש פונקציות מסייעות להעברת כל Tensor הפנימי שלהם למכשיר חדש בשלב אחד.

בניגוד למצב להוט של TensorFlow, פעולות המשתמשות ב-X10 backend אינן נשלחות בנפרד כאשר הן נתקלות בהן. במקום זאת, שיגור למאיץ מופעל רק על ידי קריאת ערכים מחושבים בחזרה למארח או על ידי הצבת מחסום מפורש. הדרך שבה זה עובד היא שזמן הריצה מתחיל מהערך שנקרא למארח (או החישוב האחרון לפני מחסום ידני) ועוקב אחר גרף החישובים שמביאים לערך זה.

לאחר מכן, גרף זה מומר לייצוג הביניים XLA HLO ומועבר למהדר XLA כדי להיות אופטימלי והידור לביצוע על המאיץ. משם, כל החישוב נשלח למאיץ ומתקבלת התוצאה הסופית.

חישוב הוא תהליך שלוקח זמן, לכן מומלץ להשתמש ב-X10 עם חישובים מקבילים מסיביים שמתבטאים באמצעות גרף ומבוצעים פעמים רבות. ערכי Hash ו-Caching משמשים כך שגרפים זהים מורכבים רק פעם אחת עבור כל תצורה ייחודית.

עבור מודלים של למידת מכונה, תהליך האימון כולל לרוב לולאה שבה המודל נתון לאותה סדרה של חישובים שוב ושוב. תרצה שכל אחד מהמעברים הללו ייראה כחזרה על אותה עקבה, במקום גרף אחד ארוך עם יחידות חוזרות בתוכו. זה מתאפשר על ידי הכנסה ידנית של קריאה לפונקציית LazyTensorBarrier() במיקומים בקוד שלך שבהם אתה רוצה שהמעקב יסתיים.

תמיכה ברמת דיוק מעורבת ב-X10

אימון עם דיוק מעורב באמצעות X10 נתמך ומספקים גם API ברמה נמוכה וגם ברמה גבוהה כדי לשלוט בו. ה- API ברמה נמוכה מציע שני מאפיינים מחושבים: toReducedPrecision ו- toFullPrecision הממירים בין דיוק מלא לדיוק מופחת, יחד עם isReducedPrecision לשאילתות על הדיוק. מלבד Tensor s, ניתן להמיר מודלים ואופטימיזציה בין דיוק מלא לדיוק מופחת באמצעות API זה.

שימו לב שהמרה לדיוק מופחת לא משנה את הסוג הלוגי של Tensor . אם t הוא Tensor<Float> , t.toReducedPrecision הוא גם Tensor<Float> עם ייצוג בסיס מופחת.

כמו במכשירים, אסור לבצע פעולות בין טנסורים בעלי דיוק שונה. זה ימנע קידום שקט ולא רצוי לציפים של 32 סיביות, שקשה יהיה לזהות על ידי המשתמש.