בהמשך מתואר הסמנטיקה של הפעולות שמוגדרות בממשק XlaBuilder
. בדרך כלל, הפעולות האלה ממופות אחד לאחד לפעולות שמוגדרות בממשק ה-RPC ב-xla_data.proto
.
הערה לגבי מינוחים: סוג הנתונים הכלליים ש- XLA מטפל בו הוא מערך N-מימדי שמחזיק אלמנטים מסוג אחיד (כמו מספר ממשי (float) של 32 ביט). במסמכי העזרה, המונח array (מערך) משמש לציון מערך בעל מימדים שרירותיים. למקרי נוחות, למקרים מיוחדים יש שמות ספציפיים ומוכרים יותר. לדוגמה, וקטור הוא מערך חד-ממדי, ו-מטריצה היא מערך דו-ממדי.
AfterAll
מידע נוסף זמין במאמר XlaBuilder::AfterAll
.
הפונקציה AfterAll מקבלת מספר משתנה של אסימונים ומפיקה טוקן יחיד. אסימונים הם סוגים פרימיטיביים שאפשר לחבר בין פעולות שמשפיעות לצדדים כדי לאכוף את הסדר. אפשר להשתמש ב-AfterAll
כצירוף של אסימונים כדי להזמין פעולה אחרי פעולות של קבוצות.
AfterAll(operands)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
XlaOp |
מספר משתנה של אסימונים |
AllGather
מידע נוסף זמין במאמר XlaBuilder::AllGather
.
מבצעת שרשור בין רפליקציות.
AllGather(operand, all_gather_dim, shard_count, replica_group_ids,
channel_id)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand
|
XlaOp
|
מערך לשרשור בין עותקים |
all_gather_dim |
int64 |
מאפיין שרשור |
replica_groups
|
וקטור של וקטורים של
int64 |
הקבוצות שבין |
channel_id
|
אופציונלי int64
|
מזהה ערוץ אופציונלי לתקשורת בין מודולים |
replica_groups
הוא רשימה של קבוצות רפליקות שביניהן מתבצע שרשור (מזהה הרפליקציה של הרפליקה הנוכחית אפשר לאחזר באמצעותReplicaId
). סדר הרפליקות בכל קבוצה קובע את הסדר שבו ערכי הקלט שלהן נמצאים בתוצאה. השדהreplica_groups
חייב להיות ריק (במקרה כזה כל הרפליקות שייכות לקבוצה אחת, בסדר מ-0
עדN - 1
), או להכיל את אותו מספר רכיבים כמו מספר הרפליקות. לדוגמה,replica_groups = {0, 2}, {1, 3}
מבצע שרשור בין הרפליקות0
ל-2
, לבין1
ו-3
.shard_count
הוא הגודל של כל קבוצת עותקים. אנחנו זקוקים לזה במקרים שבהם הערכים שלreplica_groups
ריקים.- השדה
channel_id
משמש לתקשורת בין מודולים: רק פעולותall-gather
עם אותוchannel_id
יכולות לתקשר זו עם זו.
צורת הפלט היא צורת הקלט, כאשר all_gather_dim
גדולה פי shard_count
. לדוגמה, אם יש שתי רפליקות והאופרטנד הוא [1.0, 2.5]
ו-[3.0, 5.25]
, בהתאמה, בשתי הרפליקות, ערך הפלט של הפעולה הזו, כאשר all_gather_dim
הוא 0
, יהיה [1.0, 2.5, 3.0,
5.25]
בשתי הרפליקות.
AllReduce
למידע נוסף, ראו XlaBuilder::AllReduce
.
ביצוע חישוב מותאם אישית בין הרפליקות.
AllReduce(operand, computation, replica_group_ids, channel_id)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand
|
XlaOp
|
מערך או קבוצת מערכי tupliות לא ריקה שרוצים לצמצם בין הרפליקות |
computation |
XlaComputation |
חישוב ההנחה |
replica_groups
|
וקטור של וקטורים של int64 |
הקבוצות שביניהן מתבצעות ההנחות |
channel_id
|
אופציונלי int64
|
מזהה ערוץ אופציונלי לתקשורת בין מודולים |
- כש-
operand
הוא קבוצה של מערכי משנה, הפעולה all-reduce מתבצעת בכל אלמנט של הקבוצה. replica_groups
היא רשימה של קבוצות של רפליקות שביניהן מתבצעת ההפחתה (אפשר לאחזר את מזהה הרפליקה של הרפליקה הנוכחית באמצעותReplicaId
).replica_groups
חייבת להיות ריקה (במקרה כזה, כל הרפליקות שייכות לקבוצה אחת) או להכיל את אותו מספר רכיבים כמו מספר הרפליקות. לדוגמה,replica_groups = {0, 2}, {1, 3}
מבצע צמצום בין הרפליקות0
ל-2
, לבין1
ו-3
.channel_id
משמש לתקשורת בין מודולים: רק פעולותall-reduce
עם אותוchannel_id
יכולות לתקשר זו עם זו.
צורת הפלט זהה לצורת הקלט. לדוגמה, אם יש שתי רפליקות והאופרנד מכיל את הערכים [1.0, 2.5]
ו-[3.0, 5.25]
בשתי הרפליקות, ערך הפלט מהפעולה הזו ומחישוב הסיכום יהיה [4.0, 7.75]
בשתי הרפליקות. אם הקלט הוא tuple, גם הפלט הוא tuple.
כדי לחשב את התוצאה של AllReduce
צריך קלט אחד מכל רפליקה, לכן אם רפליקציה אחת מפעילה צומת AllReduce
יותר פעמים מאשר רפליקציה אחרת, הרפליקציה הקודמת תמתין כמו שצריך. מכיוון שכל הרפליקות מריצות את אותה תוכנית, יש מעט מאוד דרכים שבהן זה יכול לקרות, אבל זה אפשרי כשהתנאי של לולאת while תלוי בנתונים מה-infeed, והנתונים שמוזנים ל-infeed גורמים ללולאת while לבצע איטרציות יותר פעמים ברפליקת אחת מאשר ברפליקת אחרת.
AllToAll
למידע נוסף, ראו XlaBuilder::AllToAll
.
AllToAll היא פעולה קולקטיבית ששולחת נתונים מכל הליבות לכל הליבות. הוא מורכב משני שלבים:
- שלב הפיזור. בכל ליבה, המשתנה מפוצל למספר הבלוקים
split_count
לאורךsplit_dimensions
, והבלוקים מפוזרים לכל הליבות, למשל, הבלוק ה-i נשלח לליבה ה-i. - שלב האיסוף. כל ליבה מקשרת את הבלוקים שהתקבלו לאורך הערך של
concat_dimension
.
אפשר להגדיר את הליבות המשתתפות באופן הבא:
replica_groups
: כל ReplicaGroup מכילה רשימה של מזהי רפליקציות שמשתתפים בחישוב (אפשר לאחזר את מזהה הרפליקציה של הרפליקציה הנוכחית באמצעותReplicaId
). המערכת תחיל את AllToAll בתוך תת-קבוצות לפי הסדר שצוין. לדוגמה, הערךreplica_groups = { {1,2,3}, {4,5,0} }
מציין ש-AllToAll יחול על הרפליקות{1, 2, 3}
, ובשלב האיסוף, והבלוקים שהתקבלו יקושרו באותו הסדר של 1, 2, 3. לאחר מכן, המערכת תחיל את AllToAll אחר ברפליקות 4, 5 ו-0, וסדר השרשור הוא גם 4, 5, 0. אם השדהreplica_groups
ריק, כל הרפליקות שייכות לקבוצה אחת, לפי סדר הקונקטנוציה שלהן.
דרישות מוקדמות:
- גודל המאפיין של האופרנד ב-
split_dimension
מתחלק ב-split_count
. - צורת האופרנד היא לא חצאית.
AllToAll(operand, split_dimension, concat_dimension, split_count,
replica_groups)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך קלט ממדי של n |
split_dimension
|
int64
|
ערך במרווח [0,
n) שמציין את המאפיין שבו מתבצע הפיצול של הפועל |
concat_dimension
|
int64
|
ערך במרווח [0,
n) שמציין את המאפיין שלצידו מחוברים הבלוקים של הפיצול |
split_count
|
int64
|
מספר הליבות שמשתתפות בפעולה הזו. אם השדה replica_groups ריק, הערך צריך להיות מספר הרפליקות. אחרת, הערך צריך להיות שווה למספר הרפליקות בכל קבוצה. |
replica_groups
|
וקטור ReplicaGroup
|
כל קבוצה מכילה רשימה של מזהי רפליקות. |
למטה מוצגת דוגמה של Alltoall.
XlaBuilder b("alltoall");
auto x = Parameter(&b, 0, ShapeUtil::MakeShape(F32, {4, 16}), "x");
AllToAll(x, /*split_dimension=*/1, /*concat_dimension=*/0, /*split_count=*/4);
בדוגמה הזו, יש 4 ליבות שמשתתפות ב-Alltoall. בכל ליבה, המשתנה מחולק ל-4 חלקים לפי המאפיין 0, כך שכל חלק הוא מסוג f32[4,4]. 4 החלקים מפוזרים לכל הליבות. לאחר מכן, כל ליבה מקשרת את החלקים שהתקבלו לפי המאפיין 1, בסדר של הליבה 0-4. לכן הפלט בכל ליבה הוא בפורמט f32[16,4].
BatchNormGrad
תיאור מפורט של האלגוריתם מופיע גם ב-XlaBuilder::BatchNormGrad
ובמאמר המקורי על נירמול בכמות גדולה.
מחשבת הדרגתיות של נורמה באצווה.
BatchNormGrad(operand, scale, mean, variance, grad_output, epsilon, feature_index)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך ממדי של n לנרמול (x) |
scale |
XlaOp |
מערך מימדי אחד (\(\gamma\)) |
mean |
XlaOp |
מערך חד-מימדי (\(\mu\)) |
variance |
XlaOp |
מערך מימדי אחד (\(\sigma^2\)) |
grad_output |
XlaOp |
שלבים שהועברו אל BatchNormTraining (\(\nabla y\)) |
epsilon |
float |
ערך אפסילון (\(\epsilon\)) |
feature_index |
int64 |
אינדקס למאפיין נבחר ב-operand |
לכל מאפיין במאפיין התכונה (feature_index
הוא האינדקס של המאפיין במאפיין ב-operand
), הפעולה מחשבת את ההדרגתיות ביחס ל-operand
, ל-offset
ול-scale
בכל שאר המאפיינים. feature_index
חייב להיות אינדקס חוקי למאפיין התכונה ב-operand
.
שלושת הגרדיאנטים מוגדרים לפי הנוסחאות הבאות (בהנחה שמשתמשים במערך 4-מימדי בתור operand
, עם אינדקס של מאפיין המאפיין l
, גודל האצווה m
וגדלים מרחביים w
ו-h
):
\[ \begin{split} c_l&= \frac{1}{mwh}\sum_{i=1}^m\sum_{j=1}^w\sum_{k=1}^h \left( \nabla y_{ijkl} \frac{x_{ijkl} - \mu_l}{\sigma^2_l+\epsilon} \right) \\\\ d_l&= \frac{1}{mwh}\sum_{i=1}^m\sum_{j=1}^w\sum_{k=1}^h \nabla y_{ijkl} \\\\ \nabla x_{ijkl} &= \frac{\gamma_{l} }{\sqrt{\sigma^2_{l}+\epsilon} } \left( \nabla y_{ijkl} - d_l - c_l (x_{ijkl} - \mu_{l}) \right) \\\\ \nabla \gamma_l &= \sum_{i=1}^m\sum_{j=1}^w\sum_{k=1}^h \left( \nabla y_{ijkl} \frac{x_{ijkl} - \mu_l}{\sqrt{\sigma^2_{l}+\epsilon} } \right) \\\\\ \nabla \beta_l &= \sum_{i=1}^m\sum_{j=1}^w\sum_{k=1}^h \nabla y_{ijkl} \end{split} \]
ערכי הקלט mean
ו-variance
מייצגים ערכי רגעים לפי אצווה וממדים מרחביים.
סוג הפלט הוא קבוצה של שלושה כינויים:
פלט | סוג | סמנטיקה |
---|---|---|
grad_operand
|
XlaOp
|
שיפוע ביחס לקלט operand ($\nabla
x$) |
grad_scale
|
XlaOp
|
שיפוע ביחס לקלט scale ($\nabla
\gamma$) |
grad_offset
|
XlaOp
|
שיפוע ביחס לקלט offset ($\nabla
\beta$) |
BatchNormInference
תיאור מפורט של האלגוריתם זמין גם במאמרים XlaBuilder::BatchNormInference
והמאמר המקורי בנושא נורמליזציה באצווה.
נורמליזציה של מערך במאפיינים של קבוצות עיבוד נתונים ומאפיינים מרחביים.
BatchNormInference(operand, scale, offset, mean, variance, epsilon, feature_index)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך ממדי של n לנרמול |
scale |
XlaOp |
מערך מימדי אחד |
offset |
XlaOp |
מערך מימדי אחד |
mean |
XlaOp |
מערך מימדי אחד |
variance |
XlaOp |
מערך מימדי אחד |
epsilon |
float |
ערך אפסילון |
feature_index |
int64 |
אינדקס למאפיין נבחר ב-operand |
לכל מאפיין במאפיין המאפיין (feature_index
הוא האינדקס של מאפיין המאפיין ב-operand
), הפעולה מחשבת את הממוצע והשונות בכל המאפיינים האחרים, ומשתמשת בממוצע ובשונות כדי לנרמל כל רכיב ב-operand
. הערך של feature_index
חייב להיות אינדקס חוקי למאפיין המאפיין ב-operand
.
BatchNormInference
היא שוות-ערך לקריאה ל-BatchNormTraining
בלי לחשב את mean
ו-variance
לכל קבוצה. במקום זאת, המערכת משתמשת בערכים המשוערים mean
ו-variance
. המטרה של הפעולה הזו היא לצמצם את זמן האחזור בתהליך ההסקה, ולכן השם שלה הוא BatchNormInference
.
הפלט הוא מערך n-ממדי ומנורמל, שיש לו צורה זהה לזו של הקלט operand
.
BatchNormTraining
למידע מפורט של האלגוריתם, ראו XlaBuilder::BatchNormTraining
ו-the original batch normalization paper
.
נורמליזציה של מערך במאפיינים של קבוצות עיבוד נתונים ומאפיינים מרחביים.
BatchNormTraining(operand, scale, offset, epsilon, feature_index)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך n-מימדי לצורך נירמול (x) |
scale |
XlaOp |
מערך מימדי אחד (\(\gamma\)) |
offset |
XlaOp |
מערך חד-מימדי (\(\beta\)) |
epsilon |
float |
ערך אפסילון (\(\epsilon\)) |
feature_index |
int64 |
אינדקס למאפיין התכונה ב-operand |
לכל מאפיין במאפיין המאפיין (feature_index
הוא האינדקס של מאפיין המאפיין ב-operand
), הפעולה מחשבת את הממוצע והשונות בכל המאפיינים האחרים, ומשתמשת בממוצע ובשונות כדי לנרמל כל רכיב ב-operand
. הערך של feature_index
חייב להיות אינדקס חוקי למאפיין המאפיין ב-operand
.
האלגוריתם פועל באופן הבא לכל קבוצה ב-operand
\(x\) שמכילה m
רכיבי m
עם w
ו-h
כגודל של ממדים מרחביים (בהנחה ש-operand
הוא מערך דו-ממדי):
חישוב הממוצע של הקבוצה \(\mu_l\) לכל מאפיין
l
במאפיין המאפיין: \(\mu_l=\frac{1}{mwh}\sum_{i=1}^m\sum_{j=1}^w\sum_{k=1}^h x_{ijkl}\)חישוב השונות באצווה \(\sigma^2_l\): $\sigma^2l=\frac{1}{mwh}\sum{i=1}^m\sum{j=1}^w\sum{k=1}^h (x_{ijkl} - \mu_l)^2$
נרמול, שינוי גודל ושינויים: \(y_{ijkl}=\frac{\gamma_l(x_{ijkl}-\mu_l)}{\sqrt[2]{\sigma^2_l+\epsilon} }+\beta_l\)
המערכת מוסיפה את הערך אפסילון, בדרך כלל מספר קטן, כדי למנוע שגיאות של חילוק באפס.
סוג הפלט הוא קבוצה של שלושה ערכים מסוג XlaOp
:
פלט | סוג | סמנטיקה |
---|---|---|
output
|
XlaOp
|
מערך n-ממדי באותה צורה כמו הקלט operand (y) |
batch_mean |
XlaOp |
מערך מימדי אחד (\(\mu\)) |
batch_var |
XlaOp |
מערך מימדי אחד (\(\sigma^2\)) |
הערכים batch_mean
ו-batch_var
הם רגעים שמחושבים במאפיינים של האצווה והמרחב באמצעות הנוסחאות שלמעלה.
BitcastConvertType
מידע נוסף זמין במאמר XlaBuilder::BitcastConvertType
.
בדומה ל-tf.bitcast
ב-TensorFlow, מבצעת פעולת bitcast לפי רכיב מצורף (element-wise) מצורת נתונים לצורת יעד. גודל הקלט והפלט חייב להיות זהה: לדוגמה, רכיבי s32
הופכים לרכיבי f32
באמצעות תוכנית bitcast, ורכיב s32
אחד הופך לארבעה רכיבי s8
. ה-bitcast מיושם כ-cast ברמה נמוכה, כך שמכונות עם ייצוגים שונים של נקודות צפות יניבו תוצאות שונות.
BitcastConvertType(operand, new_element_type)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T עם מאפייני גודל D |
new_element_type |
PrimitiveType |
סוג U |
מידות האופרנד וצורת היעד צריכות להיות זהות, חוץ מהמאפיין האחרון, שישתנה ביחס בין הגודל הראשוני לפני ההמרה ואחריה.
סוגי הרכיבים של המקור והיעד לא יכולים להיות זוגות.
המרה של Bitcast לסוג פרימיטיבי ברוחב שונה
הוראה BitcastConvert
של HLO תומכת במקרה שבו הגודל של סוג רכיב הפלט T'
לא שווה לגודל של רכיב הקלט T
. מכיוון שהפעולה כולה היא בעצם bitcast ולא משתנה את הבייטים הבסיסיים, הצורה של רכיב הפלט צריכה להשתנות. עבור B = sizeof(T), B' =
sizeof(T')
, יש שני מקרים אפשריים.
קודם כול, כשהערך הוא B > B'
, צורת הפלט מקבלת ממד חדש, משני הצדדים, בגודל B/B'
. לדוגמה:
f16[10,2]{1,0} %output = f16[10,2]{1,0} bitcast-convert(f32[10]{0} %input)
הכלל נשאר זהה למשתנים סקלריים יעילים:
f16[2]{0} %output = f16[2]{0} bitcast-convert(f32[] %input)
לחלופין, עבור B' > B
ההוראה דורשת שהמאפיין הלוגי האחרון של צורת הקלט יהיה שווה ל-B'/B
, והמאפיין הזה מושמט במהלך ההמרה:
f32[10]{0} %output = f32[10]{0} bitcast-convert(f16[10,2]{1,0} %input)
חשוב לשים לב שהמרות בין רוחבי סיביות שונים לא מבוצעות באופן מהותי.
להודיע לכולם
מידע נוסף זמין במאמר XlaBuilder::Broadcast
.
הוספת מאפיינים למערך על ידי שכפול הנתונים במערך.
Broadcast(operand, broadcast_sizes)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
המערך לשכפול |
broadcast_sizes |
ArraySlice<int64> |
הגדלים של המאפיינים החדשים |
המימדים החדשים נוספו בצד שמאל, כלומר אם ל-broadcast_sizes
יש את הערכים {a0, ..., aN}
ולצורה האופרנדית יש מידות {b0, ..., bM}
, לצורת הפלט יש מידות של {a0, ..., aN, b0, ..., bM}
.
המאפיינים החדשים מוסיפים לאינדקס עותקים של האופרנד, כלומר
output[i0, ..., iN, j0, ..., jM] = operand[j0, ..., jM]
לדוגמה, אם operand
הוא f32
סקלר עם הערך 2.0f
, ו-broadcast_sizes
הוא {2, 3}
, התוצאה תהיה מערך עם צורה f32[2, 3]
וכל הערכים בתוצאה יהיו 2.0f
.
BroadcastInDim
למידע נוסף, ראו XlaBuilder::BroadcastInDim
.
הרחבת הגודל והדירוג של מערך על ידי שכפול הנתונים במערך.
BroadcastInDim(operand, out_dim_size, broadcast_dimensions)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
המערך שרוצים לשכפל |
out_dim_size |
ArraySlice<int64> |
הגדלים של המאפיינים של צורת היעד |
broadcast_dimensions |
ArraySlice<int64> |
המאפיין בצורת היעד שאליו מתאים כל מאפיין בצורת המשתנה |
דומה ל'שידור', אבל מאפשר הוספת מאפיינים בכל מקום ולהרחיב את המאפיינים הקיימים באמצעות גודל 1.
הערך של operand
מופץ לצורה שמתוארת על ידי out_dim_size
.
הפונקציה broadcast_dimensions
ממפה את המאפיינים של operand
למאפיינים של צורת היעד, כלומר המאפיין ה-i של המשתנה המבצע ממופה למאפיין ה-broadcast_dimension[i] של צורת הפלט. המידות של operand
צריכות להיות בגודל 1 או להיות באותו גודל כמו המאפיין בפלט שאליו הם ממופים. שאר המאפיינים יתמלאו במאפיינים בגודל 1. לאחר מכן, השידור של המאפיינים המנוונים מתבצע לאורך המאפיינים המנוונים האלה כדי להגיע לצורת הפלט. הסמנטיקה מתוארת בפירוט בדף השידור.
התקשרות
למידע נוסף, ראו XlaBuilder::Call
.
מפעילה חישוב עם הארגומנטים הנתונים.
Call(computation, args...)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
computation |
XlaComputation |
חישוב מסוג T_0, T_1, ..., T_{N-1} -> S עם N פרמטרים מסוג שרירותי |
args |
רצף של N XlaOp s |
N ארגומנטים מסוג שרירותי |
ה-arity והסוגים של args
חייבים להתאים לפרמטרים של computation
. מותר שלא יהיה args
.
Cholesky
למידע נוסף, ראו XlaBuilder::Cholesky
.
הפונקציה מחשבת את הניתוח של Cholesky של קבוצה של מטריצות סימטריות (הרמיטיות) חיוביות מוגדרות.
Cholesky(a, lower)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
a |
XlaOp |
מערך מסוג מספר מרוכב או מספר צף, בעל דרגה גדולה מ-2. |
lower |
bool |
אם להשתמש במשולש העליון או התחתון של a . |
אם lower
הוא true
, הפונקציה מחשבת מטריצות משולש תחתון l
כך ש-$a = l .
l^T$. אם lower
הוא false
, מחשב מטריצות משושות עליונות u
כך ש\(a = u^T . u\).
ניתן לקרוא את נתוני הקלט רק מהמשולש התחתון/העליון של a
, בהתאם לערך של lower
. המערכת מתעלמת מהערכים מהמשולש השני. נתוני הפלט מוחזרים באותו משולש. הערכים במשולש השני מוגדרים בהטמעה ויכולים להיות כל ערך שהוא.
אם הדירוג של a
גדול מ-2, המערכת מתייחסת ל-a
כאצווה של מטריצות, כאשר כל המאפיינים המשניים חוץ מ-2 הם ממדים באצווה.
אם a
הוא לא ערך חיובי סימטרי (הרמיטיה), התוצאה מוגדרת מראש לפי תהליך ההטמעה.
קליפ
למידע נוסף, ראו XlaBuilder::Clamp
.
מצמיד אופרנד בתוך הטווח שבין ערך מינימלי לערך מקסימלי.
Clamp(min, operand, max)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
min |
XlaOp |
מערך מסוג T |
operand |
XlaOp |
מערך מסוג T |
max |
XlaOp |
מערך מסוג T |
הפונקציה מקבלת אופרנד וערכים מינימלי ומקסימלי, ומחזירה את האופרנד אם הוא נמצא בטווח שבין הערך המינימלי לערך המקסימלי. אחרת, הפונקציה מחזירה את הערך המינימלי אם האופרנד נמוך מהטווח הזה, או את הערך המקסימלי אם האופרנד גבוה מהטווח הזה. כלומר, clamp(a, x, b) = min(max(a, x), b)
.
כל שלוש המערכות צריכות להיות באותו צורה. לחלופין, כצורה מוגבלת של שידור, min
ו/או max
יכולים להיות סקלר מסוג T
.
דוגמה עם סקלר min
ו-max
:
let operand: s32[3] = {-1, 5, 9};
let min: s32 = 0;
let max: s32 = 6;
==>
Clamp(min, operand, max) = s32[3]{0, 5, 6};
כיווץ
אפשר לעיין גם במאמר XlaBuilder::Collapse
ובפעולה tf.reshape
.
צמצום המימדים של מערך למימד אחד.
Collapse(operand, dimensions)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T |
dimensions |
וקטור int64 |
קבוצת משנה עוקבת בסדר של המאפיינים של T. |
כיווץ מחליף את קבוצת המשנה הנתונה של מאפייני האופרנד במאפיין יחיד. ארגומנטים מסוג קלט הם מערך שרירותי מסוג T והוא וקטור קבוע-זמן הידור של אינדקסי מאפיינים. האינדקסים של המאפיינים חייבים להיות בסדר אלפביתי (מספרים מהנמוך לגבוה), קבוצת משנה רציפה של מאפייני T. לדוגמה, {0, 1, 2}, {0, 1} או {1, 2} הן קבוצות מאפיינים חוקיות, אבל {1, 0} או {0, 2} לא. הם מוחלפים במאפיין חדש יחיד, באותו מיקום ברצף המאפיינים שבו הם מוחלפים, כאשר גודל המאפיין החדש שווה למכפלה של גדלי המאפיינים המקוריים. מספר המאפיין הנמוך ביותר ב-dimensions
הוא המאפיין המשתנה האיטי ביותר (החשוב ביותר) בתוך ערימת הלולאות שמצמצמת את המאפיינים האלה, ומספר המאפיין הגבוה ביותר הוא המאפיין המשתנה המהיר ביותר (החשוב פחות). אם אתם צריכים סדר קריאה כללי יותר של התכונה 'קיבוץ', תוכלו לעיין באופרטור tf.reshape
.
לדוגמה, נניח ש-v הוא מערך של 24 רכיבים:
let v = f32[4x2x3] { { {10, 11, 12}, {15, 16, 17} },
{ {20, 21, 22}, {25, 26, 27} },
{ {30, 31, 32}, {35, 36, 37} },
{ {40, 41, 42}, {45, 46, 47} } };
// Collapse to a single dimension, leaving one dimension.
let v012 = Collapse(v, {0,1,2});
then v012 == f32[24] {10, 11, 12, 15, 16, 17,
20, 21, 22, 25, 26, 27,
30, 31, 32, 35, 36, 37,
40, 41, 42, 45, 46, 47};
// Collapse the two lower dimensions, leaving two dimensions.
let v01 = Collapse(v, {0,1});
then v01 == f32[4x6] { {10, 11, 12, 15, 16, 17},
{20, 21, 22, 25, 26, 27},
{30, 31, 32, 35, 36, 37},
{40, 41, 42, 45, 46, 47} };
// Collapse the two higher dimensions, leaving two dimensions.
let v12 = Collapse(v, {1,2});
then v12 == f32[8x3] { {10, 11, 12},
{15, 16, 17},
{20, 21, 22},
{25, 26, 27},
{30, 31, 32},
{35, 36, 37},
{40, 41, 42},
{45, 46, 47} };
CollectivePermute
מידע נוסף זמין במאמר XlaBuilder::CollectivePermute
.
CollectivePermute היא פעולה קולקטיבית ששולחת ומקבלים רפליקות בין נתונים.
CollectivePermute(operand, source_target_pairs)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך קלט n-ממדי |
source_target_pairs |
וקטור <int64, int64> |
רשימה של זוגות (source_replica_id, target_replica_id). לכל צמד, המשתנה נשלח מהרפליקה של המקור לרפליקה של היעד. |
חשוב לשים לב שההגבלות הבאות חלות על source_target_pair
:
- לכל שני זוגות לא יכול להיות אותו מזהה של רפליקת יעד, וגם לא אותו מזהה של רפליקת מקור.
- אם מזהה עותק הוא לא יעד באף צמד, הפלט ברפליקציות הזה הוא טנזור שכולל 0(ים) עם צורה זהה לקלט.
שרשור
מידע נוסף זמין במאמר XlaBuilder::ConcatInDim
.
הפונקציה משרשרים (במחרוזת) הרכבה מערך מאופרנדים מרובים של מערך. המערך הוא באותו דרגה של כל אחד מהאופרנדים של מערך הקלט (שחייבים להיות בדרגה זהה לזו של המערך), והוא מכיל את הארגומנטים לפי הסדר שבו צוינו.
Concatenate(operands..., dimension)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
רצף של N XlaOp |
N מערכי T עם המאפיינים [L0, L1, ...]. נדרש N >= 1. |
dimension |
int64 |
ערך במרווח [0, N) שמציין את המאפיין שיש לחבר בין operands . |
מלבד dimension
, כל המאפיינים חייבים להיות זהים. הסיבה לכך היא ש-XLA לא תומך במערכים 'לא סדירים'. חשוב גם לזכור שאי אפשר לשרשר ערכים של דירוג 0 (כי אי אפשר לתת שם למאפיין שבו מתבצעת השרשרת).
דוגמה במאפיין אחד:
Concat({ {2, 3}, {4, 5}, {6, 7} }, 0)
>>> {2, 3, 4, 5, 6, 7}
דוגמה לדו-ממדית:
let a = {
{1, 2},
{3, 4},
{5, 6},
};
let b = {
{7, 8},
};
Concat({a, b}, 0)
>>> {
{1, 2},
{3, 4},
{5, 6},
{7, 8},
}
דיאגרמה:
משפטי תנאי
מידע נוסף זמין במאמר XlaBuilder::Conditional
.
Conditional(pred, true_operand, true_computation, false_operand,
false_computation)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
pred |
XlaOp |
סקלר מסוג PRED |
true_operand |
XlaOp |
ארגומנט מסוג \(T_0\) |
true_computation |
XlaComputation |
XlaComputation מסוג \(T_0 \to S\) |
false_operand |
XlaOp |
ארגומנט מסוג \(T_1\) |
false_computation |
XlaComputation |
XlaComputation מסוג \(T_1 \to S\) |
הפונקציה מפעילה את true_computation
אם הערך של pred
הוא true
, את false_computation
אם הערך של pred
הוא false
ומחזירה את התוצאה.
הפונקציה true_computation
חייבת לקבל ארגומנט יחיד מסוג \(T_0\) , והיא תופעל באמצעות true_operand
, שגם הוא חייב להיות מאותו סוג. הפונקציה false_computation
חייבת לקבל ארגומנט יחיד מסוג \(T_1\) , והיא תופעל באמצעות false_operand
, שגם הוא חייב להיות מאותו סוג. הסוג של הערך המוחזר של true_computation
ושל false_computation
חייב להיות זהה.
הערה: רק אחד מהמאפיינים true_computation
ו-false_computation
יבוצע, בהתאם לערך של pred
.
Conditional(branch_index, branch_computations, branch_operands)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
branch_index |
XlaOp |
סקלר מסוג S32 |
branch_computations |
רצף של N XlaComputation |
Xlaחישובים מסוג \(T_0 \to S , T_1 \to S , ..., T_{N-1} \to S\) |
branch_operands |
רצף של N XlaOp |
ארגומנטים מסוג \(T_0 , T_1 , ..., T_{N-1}\) |
הפונקציה מפעילה את branch_computations[branch_index]
ומחזירה את התוצאה. אם branch_index
הוא S32
שהוא < 0 או >= N, אז branch_computations[N-1]
מופעל כענף ברירת המחדל.
כל branch_computations[b]
חייב לקבל ארגומנט יחיד מסוג \(T_b\) , והוא יופעל באמצעות branch_operands[b]
שחייב להיות מאותו סוג. סוג הערך המוחזר של כל branch_computations[b]
חייב להיות זהה.
שימו לב שרק אחד מהשדות branch_computations
יופעל, בהתאם לערך של branch_index
.
Conv (convolution)
מידע נוסף זמין במאמר XlaBuilder::Conv
.
כמו ConvWithGeneralPadding, אבל המילוי מצוין בקיצור כ-SAME או כ-VALID. בדילול SAME, הקלט (lhs
) מתמלא באפסים כדי שהפלט יהיה באותו צורה כמו הקלט, בלי להביא בחשבון את ה-striding. משמעות האפשרות VALID padding היא פשוט ללא תוספת.
ConvWithGeneralPadding (עיבוד נתונים באמצעות convolve)
למידע נוסף, ראו XlaBuilder::ConvWithGeneralPadding
.
חישוב עיבוד נתונים מסוג 'קונבולוציה', כפי שמשתמשים בו ברשתות נוירונים. כאן, אפשר לחשוב על convolve כחלון n-ממדי שנע על פני אזור בסיס n-ממדי, ומתבצעת חישוב לכל מיקום אפשרי של החלון.
ארגומנטים | סוג | סמנטיקה |
---|---|---|
lhs |
XlaOp |
דירוג n+2 מערך קלט |
rhs |
XlaOp |
מערך של משקלים של גרעיני חזקה n+2 |
window_strides |
ArraySlice<int64> |
מערך n-ממדי של צעדים של ליבה |
padding |
ArraySlice< pair<int64,int64>> |
מערך n-d של מרווח פנימי (נמוך, גבוה) |
lhs_dilation |
ArraySlice<int64> |
מערך של גורם הרחבה של n-d lhs |
rhs_dilation |
ArraySlice<int64> |
מערך גורם הרחבה n-d rhs |
feature_group_count |
int64 | מספר הקבוצות של התכונות |
batch_group_count |
int64 | מספר הקבוצות באצווה |
נניח ש-n הוא מספר המימדים המרחביים. הארגומנט lhs
הוא מערך דירוג n+2 שמתאר את אזור הבסיס. זה נקרא הקלט, למרות שגם ה-rhs הוא קלט. ברשת נוירונים, אלה הפעלות הקלט.
המאפיינים n+2 הם, לפי הסדר הזה:
batch
: כל קואורדינטה במאפיין הזה מייצגת קלט עצמאי שעבורו מתבצעת הקונבולוציה.z/depth/features
: לכל מיקום (x,y) באזור הבסיס משויך וקטור שמשויך למאפיין הזה.spatial_dims
: מתאר את המאפיינים המרחבייםn
שמגדירים את אזור הבסיס שבו החלון נע.
הארגומנט rhs
הוא מערך בעל דרגה n+2 שמתאר את המסנן/הגרעין/החלון הקוונטי. המאפיינים הם, לפי הסדר הזה:
output-z
: המאפייןz
של הפלט.input-z
: הגודל של המימד הזה כפולfeature_group_count
צריך להיות שווה לגודל של המימדz
ביחידות מידה שלמות.spatial_dims
: תיאור המימדים המרחביים שלn
שמגדירים את חלון ה-n-d שנע על פני אזור הבסיס.
הארגומנט window_strides
מציין את הצעדה של חלון הקוונטיל בממדים המרחביים. לדוגמה, אם הצעדה במאפיין המרחבי הראשון היא 3, אפשר למקם את החלון רק בקואורדינטות שבהן האינדקס המרחבי הראשון מתחלק ב-3.
הארגומנט padding
מציין את הכמות של אפס מרווחים שצריך להחיל על אזור הבסיס. כמות המרווח הפנימי יכולה להיות שלילית – הערך המוחלט של המרווח הפנימי מציין את מספר הרכיבים שיש להסיר מהמאפיין שצוין לפני ביצוע הקונבולוציה. padding[0]
מציין את המילוי למאפיין y
ו-padding[1]
מציין את המילוי למאפיין x
. לכל זוג יש את המרווח הפנימי הנמוך בתור הרכיב הראשון, ואת המרווח הפנימי הגבוה בתור הרכיב השני. המרווח הפנימי הנמוך מוחל בכיוון האינדקסים הנמוכים יותר, והמרווח הפנימי הגבוה מוחל בכיוון האינדקסים הגבוהים יותר. לדוגמה, אם הערך של padding[1]
הוא (2,3)
, תתבצע הוספת 2 אפסים בצד ימין ו-3 אפסים בצד שמאל במאפיין המרחבי השני. השימוש במילוי שווה ערך להוספת אותם ערכי אפס לקלט (lhs
) לפני ביצוע ההתמרה.
הארגומנטים lhs_dilation
ו-rhs_dilation
מציינים את גורם ההרחבה שיש להחיל על Hs ו-rhs, בהתאמה, בכל מימד מרחבי. אם גורם ההרחבה במימד מרחבי הוא d, אז חורי d-1 מוצבים במרומז בין כל אחד מהפריטים במימד הזה וכך מגדילים את המערך. החורים מלאים בערך מסוג no-op, שמשמעותו קונבולציה היא אפסים.
הרחבה של הצד הימני של המשוואה נקראת גם 'קונבולוציה אטרוסית'. למידע נוסף, ראו tf.nn.atrous_conv2d
. הרחבה של ה-lhs נקראת גם טרנספוזיציה של convolve. פרטים נוספים זמינים במאמר tf.nn.conv2d_transpose
.
אפשר להשתמש בארגומנט feature_group_count
(ערך ברירת המחדל הוא 1) כדי לבצע עיבוד נתונים קבוצתי. הערך feature_group_count
צריך להיות מחלק של מאפיין הקלט ושל מאפיין הפלט. אם הערך של feature_group_count
גדול מ-1, המשמעות היא שקונספטואלית, מאפייני הקלט והפלט ומאפייני הפלט של rhs
מחולקים באופן שווה למספר רב של קבוצות feature_group_count
, כאשר כל קבוצה מורכבת מרצף משנה רציף של מאפיינים. המאפיין של מאפיין הקלט rhs
צריך להיות שווה למאפיין של מאפיין הקלט lhs
חלקי feature_group_count
(כך שהוא כבר בגודל של קבוצה של מאפייני קלט). הקבוצות ה-i משמשות יחד כדי לחשב את feature_group_count
עבור עיבודים מרובים של convolve. התוצאות של ההתחברות הזו מחוברות זו לזו במאפיין הפלט.
עבור convolveion לעומק, הארגומנט feature_group_count
יוגדר למאפיין של מאפיין הקלט, והמסנן ישתנה מ-[filter_height, filter_width, in_channels, channel_multiplier]
ל-[filter_height, filter_width, 1, in_channels * channel_multiplier]
. לפרטים נוספים: tf.nn.depthwise_conv2d
.
אפשר להשתמש בארגומנט batch_group_count
(ערך ברירת המחדל 1) למסננים מקובצים במהלך ההפצה לאחור. batch_group_count
צריך להיות המחלק של הגודל של המימד lhs
(קלט). אם הערך של batch_group_count
גדול מ-1, המשמעות היא שהמאפיין batch של הפלט צריך להיות בגודל input batch
/ batch_group_count
. הערך של batch_group_count
חייב להיות מחלק של גודל המאפיינים של הפלט.
לצורת הפלט יש את המידות האלו, בסדר הזה:
batch
: הגודל של המימד הזה כפולbatch_group_count
צריך להיות שווה לגודל של המימדbatch
בשניות.z
: באותו גודל כמוoutput-z
בליבה (rhs
).spatial_dims
: ערך אחד לכל מיקום תקין של החלון הקוונטי.
באיור שלמעלה מוצג אופן הפעולה של השדה batch_group_count
. למעשה, אנחנו מחלקים כל קבוצה של lhs ל-batch_group_count
קבוצות, ועושים את אותו הדבר למאפייני הפלט. אחר כך, בכל אחת מהקבוצות האלה אנחנו מבצעים קונבולוציות צמדים ומשרשרים את הפלט לאורך מימד תכונת הפלט. הסמנטיקה התפעולית של כל שאר המאפיינים (תכונות ומרחב) נשארת ללא שינוי.
המיקומים החוקיים של החלון המתקפל נקבעים לפי הצעדים והגודל של אזור הבסיס אחרי המרווח הפנימי.
כדי לתאר את הפעולה של קונבולציה, נבחן קונבולציה דו-מימדית ונבחר כמה קואורדינטות קבועות batch
, z
, y
, x
בפלט. במקרה כזה, הערך של (y,x)
הוא המיקום של פינה של החלון בתוך אזור הבסיס (למשל, הפינה הימנית העליונה, בהתאם לאופן שבו מפרשים את המימדים המרחביים). עכשיו יש לנו חלון דו-מימדי, שנלקח מאזור הבסיס, שבו כל נקודה דו-מימדית משויכת לוקטור חד-מימדי, כך שאנחנו מקבלים תיבה תלת-מימדית. מהליבה המורכבת, מכיוון תיקנו את קואורדינטת הפלט z
, יש לנו גם תיבה תלת ממדית. לשתי התיבות יש אותם מאפיינים, כך שאפשר לחשב את הסכום של המכפלות של הרכיבים בין שתי התיבות (בדומה למכפלה סקלרית). זהו ערך הפלט.
הערה: אם הערך של output-z
הוא, למשל, 5, כל מיקום של החלון יוצר 5 ערכים בפלט במאפיין z
של הפלט. הערכים האלו שונים בחלק מהליבה המסוכנת שבה משתמשים – יש תיבת ערכים תלת-ממדית נפרדת לכל קואורדינטה output-z
. אפשר לחשוב על זה בתור 5 convolve נפרד עם מסנן שונה לכל אחד מהם.
הנה קוד מדומה לקונבולוציה דו-ממדית עם מרווח פנימי וצעדות:
for (b, oz, oy, ox) { // output coordinates
value = 0;
for (iz, ky, kx) { // kernel coordinates and input z
iy = oy*stride_y + ky - pad_low_y;
ix = ox*stride_x + kx - pad_low_x;
if ((iy, ix) inside the base area considered without padding) {
value += input(b, iz, iy, ix) * kernel(oz, iz, ky, kx);
}
}
output(b, oz, oy, ox) = value;
}
ConvertElementType
מידע נוסף זמין במאמר XlaBuilder::ConvertElementType
.
בדומה ל-static_cast
לפי רכיב ב-C++, מבצע פעולת המרה לפי רכיב מתבנית נתונים לתבנית יעד. המאפיינים חייבים להיות זהים, וההמרה צריכה להיות מורכבת מהרכיבים. למשל, רכיבי s32
הופכים לרכיבי f32
באמצעות שגרת המרה של s32
ל-f32
.
ConvertElementType(operand, new_element_type)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T עם מאפייני גודל D |
new_element_type |
PrimitiveType |
סוג U |
המאפיינים של המשתנה המבצע והצורה של היעד חייבים להיות זהים. הסוגים של רכיבי המקור והיעד לא יכולים להיות זוגות.
המרה כמו T=s32
ל-U=f32
תבצע נירמול של שגרת המרות מסוג 'מספר ציפה', למשל 'מסביב לשווי'.
let a: s32[3] = {0, 1, 2};
let b: f32[3] = convert(a, f32);
then b == f32[3]{0.0, 1.0, 2.0}
CrossReplicaSum
ביצוע AllReduce
עם חישוב סכום.
CustomCall
למידע נוסף, ראו XlaBuilder::CustomCall
.
קריאה לפונקציה שהמשתמש סיפק בתוך חישוב.
CustomCall(target_name, args..., shape)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
target_name |
string |
שם הפונקציה. תופעל הוראה של קריאה שמטרגטת את שם הסמל הזה. |
args |
רצף של N XlaOp שנ' |
N ארגומנטים מסוג שרירותי, שיועברו לפונקציה. |
shape |
Shape |
צורת הפלט של הפונקציה |
חתימה הפונקציה זהה, ללא קשר למספר הארגומנטים או לסוג שלהם:
extern "C" void target_name(void* out, void** in);
לדוגמה, אם משתמשים בהתאמה אישית לשיחה:
let x = f32[2] {1,2};
let y = f32[2x3] { {10, 20, 30}, {40, 50, 60} };
CustomCall("myfunc", {x, y}, f32[3x3])
דוגמה להטמעה של myfunc
:
extern "C" void myfunc(void* out, void** in) {
float (&x)[2] = *static_cast<float(*)[2]>(in[0]);
float (&y)[2][3] = *static_cast<float(*)[2][3]>(in[1]);
EXPECT_EQ(1, x[0]);
EXPECT_EQ(2, x[1]);
EXPECT_EQ(10, y[0][0]);
EXPECT_EQ(20, y[0][1]);
EXPECT_EQ(30, y[0][2]);
EXPECT_EQ(40, y[1][0]);
EXPECT_EQ(50, y[1][1]);
EXPECT_EQ(60, y[1][2]);
float (&z)[3][3] = *static_cast<float(*)[3][3]>(out);
z[0][0] = x[1] + y[1][0];
// ...
}
אסור שפונקציה שהמשתמש מספק תהיה עם תופעות לוואי, וההפעלה שלה צריכה להיות חזקה (idempotent).
נקודה
מידע נוסף זמין במאמר XlaBuilder::Dot
.
Dot(lhs, rhs)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
lhs |
XlaOp |
מערך מסוג T |
rhs |
XlaOp |
מערך מסוג T |
הסמנטיקה המדויקת של הפעולה הזו תלויה ברמות האופרנדים:
קלט | פלט | סמנטיקה |
---|---|---|
vector [n] dot vector [n] |
סקלר | מכפלה סקלרית של וקטורים |
מטריצה [m x k] dot וקטור [k] |
וקטור [m] | כפל של מטריצה וקטורית |
מטריצה [m x k] dot מטריצה [k x n] |
מטריצת [m x n] | כפל מטריצות |
הפעולה מבצעת סכום של מוצרים במאפיין השני של lhs
(או במאפיין הראשון אם הדירוג שלו הוא 1) ובמאפיין הראשון של rhs
. אלה המאפיינים 'בהסכם'. המאפיינים המקוצרים של lhs
ו-rhs
חייבים להיות באותו גודל. בפועל, אפשר להשתמש בו כדי לבצע מכפלות נקודה בין וקטורים, מכפלות של וקטורים/מטריצות או מכפלות של מטריצות/מטריצות.
DotGeneral
למידע נוסף, ראו XlaBuilder::DotGeneral
.
DotGeneral(lhs, rhs, dimension_numbers)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
lhs |
XlaOp |
מערך מסוג T |
rhs |
XlaOp |
מערך מסוג T |
dimension_numbers |
DotDimensionNumbers |
מספרי ממדים מקוצרים ובאצווה |
דומה ל-Dot, אבל מאפשר לציין מספרי מימדים חוזיים ובאצווה גם ל-lhs
וגם ל-rhs
.
שדות DotdimensionNumbers | סוג | סמנטיקה |
---|---|---|
lhs_contracting_dimensions
|
repeated int64 | lhs מספרי מאפיינים מתכווצים |
rhs_contracting_dimensions
|
repeated int64 | rhs מספרי המאפיינים של הקבלן |
lhs_batch_dimensions
|
repeated int64 | lhs מספרי מימדים באצווה |
rhs_batch_dimensions
|
repeated int64 | rhs מספרי מימדים באצווה |
הפונקציה DotGeneral מבצעת את סכום המכפלות של המאפיינים המצטמצמים שצוינו ב-dimension_numbers
.
מספרי המידות המשויכים מ-lhs
ומ-rhs
לא צריכים להיות זהים, אבל המידות שלהם צריכות להיות זהות.
דוגמה עם מספרי מאפיינים של צדדים חותמים:
lhs = { {1.0, 2.0, 3.0},
{4.0, 5.0, 6.0} }
rhs = { {1.0, 1.0, 1.0},
{2.0, 2.0, 2.0} }
DotDimensionNumbers dnums;
dnums.add_lhs_contracting_dimensions(1);
dnums.add_rhs_contracting_dimensions(1);
DotGeneral(lhs, rhs, dnums) -> { {6.0, 12.0},
{15.0, 30.0} }
למספרי המאפיינים המשויכים של האצווה מהשדות lhs
ו-rhs
צריכים להיות אותם גדלים של מאפיינים.
דוגמה למספרים באצווה (מטריצות בגודל אצווה 2, 2x2):
lhs = { { {1.0, 2.0},
{3.0, 4.0} },
{ {5.0, 6.0},
{7.0, 8.0} } }
rhs = { { {1.0, 0.0},
{0.0, 1.0} },
{ {1.0, 0.0},
{0.0, 1.0} } }
DotDimensionNumbers dnums;
dnums.add_lhs_contracting_dimensions(2);
dnums.add_rhs_contracting_dimensions(1);
dnums.add_lhs_batch_dimensions(0);
dnums.add_rhs_batch_dimensions(0);
DotGeneral(lhs, rhs, dnums) -> { { {1.0, 2.0},
{3.0, 4.0} },
{ {5.0, 6.0},
{7.0, 8.0} } }
קלט | פלט | סמנטיקה |
---|---|---|
[b0, m, k] dot [b0, k, n] |
[b0, m, n] | batch matmul |
[b0, b1, m, k] dot [b0, b1, k, n] |
[b0, b1, m, n] | batch matmul |
לכן, מספר המאפיין שנוצר מתחיל במאפיין האצווה, ואז במאפיין lhs
ללא חוזה/ללא אצווה, ולבסוף במאפיין rhs
ללא חוזה/ללא אצווה.
DynamicSlice
מידע נוסף זמין במאמר XlaBuilder::DynamicSlice
.
DynamicSlice מחלץ מערך משנה ממערך הקלט ב-start_indices
הדינמי. הגודל של הפלחים בכל מאפיין מועבר ב-size_indices
, שמציין את נקודת הסיום של מרווחים בלעדיים של פלחים בכל מאפיין: [start, start + size). הצורה של start_indices
חייבת להיות rank ==
1, עם גודל מאפיין שווה לדרגה של operand
.
DynamicSlice(operand, start_indices, size_indices)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך N-מימדי מסוג T |
start_indices |
רצף של N XlaOp |
רשימה של N מספרים שלמים סקלריים שמכילים את האינדקסים ההתחלתיים של הפלח לכל מאפיין. הערך חייב להיות גדול מ-0 או שווה ל-0. |
size_indices |
ArraySlice<int64> |
רשימה של N מספרים שלמים שמכילה את גודל הפרוסת לכל מאפיין. כל ערך חייב להיות גדול מאפס, והערך start + size חייב להיות קטן או שווה לגודל המאפיין כדי למנוע גלישת מודולים לפי גודל המאפיין. |
כדי לחשב את אינדקסי הפלחים היעילים, מחילים את הטרנספורמציה הבאה על כל אינדקס i
ב-[1, N)
לפני ביצוע הפלחים:
start_indices[i] = clamp(start_indices[i], 0, operand.dimension_size[i] - size_indices[i])
כך אפשר להבטיח שהפרוסה שחולץ תהיה תמיד בתוך תחומי הגבולות ביחס למערך האופרנד. אם הפלחים נמצאים בגבולות לפני החלת הטרנספורמציה, הטרנספורמציה לא משפיעה עליהם.
דוגמה חד-ממדית:
let a = {0.0, 1.0, 2.0, 3.0, 4.0}
let s = {2}
DynamicSlice(a, s, {2}) produces:
{2.0, 3.0}
דוגמה דו-ממדית:
let b =
{ {0.0, 1.0, 2.0},
{3.0, 4.0, 5.0},
{6.0, 7.0, 8.0},
{9.0, 10.0, 11.0} }
let s = {2, 1}
DynamicSlice(b, s, {2, 2}) produces:
{ { 7.0, 8.0},
{10.0, 11.0} }
DynamicUpdateSlice
למידע נוסף, ראו XlaBuilder::DynamicUpdateSlice
.
הפונקציה DynamicUpdateSlice יוצרת תוצאה שהיא הערך של מערך הקלט operand
, עם פלחים של update
שמחליפים את start_indices
.
הצורה של update
קובעת את הצורה של מערך המשנה של התוצאה שמתעדכן.
הצורה של start_indices
חייבת להיות בדירוג == 1, וגודל המימד שווה לדירוג של operand
.
DynamicUpdateSlice(operand, update, start_indices)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך ממדי של N מסוג T |
update |
XlaOp |
מערך ממדי של N מסוג T שמכיל את עדכון הפלח. כל מימד של צורת עדכון חייב להיות גדול מאפס, ו'התחלה + עדכון' חייב להיות קטן מגודל האופרנד של כל מימד או שווה לו, כדי להימנע מיצירת מדדי עדכון מחוץ לתחום. |
start_indices |
רצף של N XlaOp |
רשימה של N מספרים שלמים סקלריים שמכילים את האינדקסים ההתחלתיים של הפלח לכל מאפיין. הערך חייב להיות גדול מ-0 או שווה ל-0. |
מדדי הפרוסות האפקטיביים מחושבים על ידי החלת הטרנספורמציה הבאה על כל אינדקס i
ב-[1, N)
לפני ביצוע הפרוסות:
start_indices[i] = clamp(start_indices[i], 0, operand.dimension_size[i] - update.dimension_size[i])
כך אפשר להבטיח שהפלח המעודכן תמיד יהיה בתוך הגבולות של מערך המשתנה. אם הפלח נמצא בתוך התחום לפני החלת הטרנספורמציה, לטרנספורמציה אין השפעה.
דוגמה חד-ממדית:
let a = {0.0, 1.0, 2.0, 3.0, 4.0}
let u = {5.0, 6.0}
let s = {2}
DynamicUpdateSlice(a, u, s) produces:
{0.0, 1.0, 5.0, 6.0, 4.0}
דוגמה דו-ממדית:
let b =
{ {0.0, 1.0, 2.0},
{3.0, 4.0, 5.0},
{6.0, 7.0, 8.0},
{9.0, 10.0, 11.0} }
let u =
{ {12.0, 13.0},
{14.0, 15.0},
{16.0, 17.0} }
let s = {1, 1}
DynamicUpdateSlice(b, u, s) produces:
{ {0.0, 1.0, 2.0},
{3.0, 12.0, 13.0},
{6.0, 14.0, 15.0},
{9.0, 16.0, 17.0} }
פעולות אריתמטיות בינאריות לפי רכיבים
מידע נוסף זמין במאמר XlaBuilder::Add
.
יש תמיכה בקבוצה של פעולות חשבון בינאריות ברמת הרכיבים.
Op(lhs, rhs)
Op
Add
Sub
Mul
Div
Pow
Rem
Max
Min
And
Or
Xor
ShiftLeft
ShiftRightArithmetic
ShiftRightLogical
Atan2
Complex
ארגומנטים | סוג | סמנטיקה |
---|---|---|
lhs |
XlaOp |
אופרנד בצד ימין: מערך מסוג T |
rhs |
XlaOp |
אופרנד בצד שמאל: מערך מסוג T |
הצורות של הארגומנטים צריכות להיות דומות או תואמות. במסמכי העזרה בנושא שידור מוסבר מהי תאימות של צורות. לתוצאה של פעולה יש צורה שהיא התוצאה של שידור שני מערכי הקלט. בגרסה הזו, אין תמיכה בפעולות בין מערכי משנה של דרגות שונות, אלא אם אחד מהאופרטנדים הוא סקלר.
כש-Op
הוא Rem
, סימן התוצאה נלקח מהמחלק, והערך המוחלט של התוצאה תמיד קטן מהערך המוחלט של המחלק.
אם מתרחש חריגה ממכסת המספרים שלמים בחילוק (חילוק/שארית עם סימן או ללא סימן באפס, או חילוק/שארית עם סימן של INT_SMIN
ב--1
), מתקבל ערך שמוגדר על ידי ההטמעה.
לפעולות האלה יש וריאנט חלופי עם תמיכה בשידור ברמה שונה:
Op(lhs, rhs, broadcast_dimensions)
כאשר Op
זהה ל-Op
שלמעלה. צריך להשתמש בווריאנט הזה של הפעולה לביצוע פעולות אריתמטיות בין מערכי משנה (arrays) של דרגות שונות (למשל, הוספת מטריקס לוקטור).
המשתנה הנוסף broadcast_dimensions
הוא פרוסת מספרים שלמים שמשמשת להרחבת הדרגה של המשתנה בעל הדרגה הנמוכה יותר עד לדרגה של המשתנה בעל הדרגה הגבוהה יותר. הפונקציה broadcast_dimensions
ממפה את המאפיינים של הצורה ברמה הנמוכה יותר למאפיינים של הצורה ברמה הגבוהה יותר. המאפיינים שלא מותאמים של הצורה המורחבת מתמלאים במאפיינים בגודל אחד. שידור של מימד מנוטר לאחר מכן משדר את הצורות לאורך המימדים המנוונים האלה כדי להשוות את הצורה של שני האופרנדים. הסמנטיקה מתוארת בפירוט בדף השידור.
פעולות השוואה ברמת הרכיב
למידע נוסף, ראו XlaBuilder::Eq
.
יש תמיכה בקבוצה של פעולות השוואה בינאריות סטנדרטיות ברמת הרכיבים. חשוב לזכור שסמנטיקה רגילה של השוואה של נקודות צפות מסוג IEEE 754 חלה כשמשווים בין סוגי נקודות צפות.
Op(lhs, rhs)
כאשר Op
הוא אחד מהערכים Eq
(שווה ל-), Ne
(לא שווה ל-), Ge
(גדול או שווה ל-), Gt
(גדול מ-), Le
(פחות או שווה מ-), Lt
(פחות מ-). קבוצה נוספת של אופרטורים, EqTotalOrder, NeTotalOrder, GeTotalOrder, GtTotalOrder, LeTotalOrder ו-LtTotalOrder, מספקת את אותן פונקציות, מלבד תמיכה נוספת בסדר מלא על מספרי נקודה צפה, על ידי אכיפה של -NaN < -Inf < -Finite < -0 < +0 < +Finite < +Inf < +NaN.
ארגומנטים | סוג | סמנטיקה |
---|---|---|
lhs |
XlaOp |
אופרנד בצד ימין: מערך מסוג T |
rhs |
XlaOp |
אופרנד בצד שמאל: מערך מסוג T |
הצורות של הארגומנטים צריכות להיות דומות או תואמות. במסמכי העזרה בנושא שידור מוסבר מהי תאימות של צורות. התוצאה של פעולה היא בצורה שמתקבלת מהשידור של שני מערכי הקלט עם טיפוס הרכיב PRED
. בגרסה הזו, אין תמיכה בפעולות בין מערכי משנה של דרגות שונות, אלא אם אחד מהאופרטנדים הוא סקלר.
לפעולות האלה יש וריאנט חלופי עם תמיכה בשידור ברמה שונה:
Op(lhs, rhs, broadcast_dimensions)
כאשר הערך של Op
זהה לערך שלמעלה. צריך להשתמש בגרסה הזו של הפעולה לפעולות השוואה בין מערכי משנה (arrays) של דרגות שונות (למשל, הוספת מטריצה לוקטור).
המשתנה הנוסף broadcast_dimensions
הוא פלחי מספרים שלמים שמציינים את המאפיינים שבהם יש להשתמש לשידור של המשתנים. בדף השידור מוסבר בפירוט על הסמנטיקה.
פונקציות יוניאריות לפי רכיב
ב-XlaBuilder יש תמיכה בפונקציות אונריות ברמת הרכיבים הבאות:
Abs(operand)
אבסולוטיות x -> |x|
.
Cbrt(operand)
פעולת שורש מעוקבים ברמת הרכיב x -> cbrt(x)
.
Ceil(operand)
x -> ⌈x⌉
– חלוקת הסכום של כל הרכיבים ב-1.
Clz(operand)
ספירה של אפסים מובילים ברמת הרכיב.
Cos(operand)
קוסינוס של רכיבים x -> cos(x)
.
Erf(operand)
פונקציית שגיאה מבוססת-רכיב x -> erf(x)
כאשר
\(\text{erf}(x) = \frac{2}{\sqrt{\pi} }\int_0^x e^{-t^2} \, dt\).
Exp(operand)
מעריכי טבעי ברמת הרכיב x -> e^x
.
Expm1(operand)
מעריכיות טבעית ברמת הרכיב פחות x -> e^x - 1
אחת.
Floor(operand)
x -> ⌊x⌋
– רצפה של כל רכיב.
Imag(operand)
החלק המדומה של צורה מורכבת (או ממשית) לפי אלמנט. x -> imag(x)
. אם האופרנד הוא מסוג נקודה צפה, הפונקציה מחזירה 0.
IsFinite(operand)
הפונקציה בודקת אם כל אלמנט ב-operand
הוא סופי, כלומר, שהוא לא אינסוף חיובי או שלילי, והוא לא NaN
. הפונקציה מחזירה מערך של ערכים מסוג PRED
באותו צורה של הקלט, שבו כל רכיב הוא true
אם ורק אם רכיב הקלט התואם הוא סופי.
Log(operand)
לוגריתם טבעי מבחינת רכיבים x -> ln(x)
.
Log1p(operand)
לוגריתם טבעי שהוזז לפי רכיבים x -> ln(1+x)
.
Logistic(operand)
חישוב פונקציה לוגיסטית לפי רכיבים x ->
logistic(x)
.
Neg(operand)
ביטול של רכיבים x -> -x
.
Not(operand)
'לא' לוגית לפי רכיבים x -> !(x)
.
PopulationCount(operand)
חישוב מספר הסיביות שמוגדרים בכל רכיב של operand
.
Real(operand)
החלק האמיתי מבחינת הרכיבים מתוך צורה מורכבת (או אמיתית).
x -> real(x)
. אם האופרנד הוא סוג נקודה צפה (floating-point), מחזירה את אותו ערך.
Round(operand)
עיגול לפי אלמנט, במקרה של שוויון עיגול הרחק מהאפס.
RoundNearestEven(operand)
עיגול לפי רכיבים, בחיבור לזוגי הקרוב ביותר.
Rsqrt(operand)
הפונקציה ההדדית ברמת הרכיב של פעולת שורש ריבועית
x -> 1.0 / sqrt(x)
.
Sign(operand)
פעולת סימן ברמת הרכיב x -> sgn(x)
כאשר
\[\text{sgn}(x) = \begin{cases} -1 & x < 0\\ -0 & x = -0\\ NaN & x = NaN\\ +0 & x = +0\\ 1 & x > 0 \end{cases}\]
באמצעות אופרטור ההשוואה של סוג הרכיב operand
.
Sin(operand)
סינוס של רכיבים x -> sin(x)
.
Sqrt(operand)
פעולת שורש ריבועית מבחינת רכיב x -> sqrt(x)
.
Tan(operand)
טנגנס מבחינת רכיבים x -> tan(x)
.
Tanh(operand)
טנגנס היפרבולי לפי רכיבים x -> tanh(x)
.
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
האופרנד לפונקציה |
הפונקציה מוחלת על כל רכיב במערך operand
, וכתוצאה מכך נוצרת מערך עם אותה הצורה. מותר ל-operand
להיות סקלר (דרגה 0).
Fft
פעולת ה-FFT של XLA מיישמת את טרנספורמציית פורייה הישירה והפונקציה ההפוכה שלה לקלטים/פלטות אמיתיים ומרוכבים. יש תמיכה ב-FFTs מרובי-מימדים ב-3 צירים לכל היותר.
מידע נוסף זמין במאמר XlaBuilder::Fft
.
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
המערך שאנחנו ממירים בעזרת פורייה. |
fft_type |
FftType |
פרטים נוספים מופיעים בטבלה שבהמשך. |
fft_length |
ArraySlice<int64> |
אורכי דומיין הזמן של הצירים שעוברים טרנספורמציה. השינוי הזה נדרש במיוחד כדי ש-IRFFT יתאים את הגודל של הציר הפנימי ביותר, כי ל-RFFT(fft_length=[16]) יש צורת פלט זהה לזו של RFFT(fft_length=[17]) . |
FftType |
סמנטיקה |
---|---|
FFT |
FFT קדימה ממרוכב למרוכב. הצורה לא משתנה. |
IFFT |
המרה מרוכבת מרוכבת למורכבת (FFT), הצורה לא השתנתה. |
RFFT |
FFT קדימה ממרחב ממשי למרוכב. הצורה של הציר הפנימי ביותר מצטמצמת ל-fft_length[-1] // 2 + 1 אם fft_length[-1] הוא ערך שאינו אפס, והחלק המנוגד (conjugate) ההפוך של האות המומר מעבר לתדר Nyquist מושמט. |
IRFFT |
המרה מ-Real-to-complex ל-FFT (כלומר היא לוקחת מורכבת, מחזירה TRUE). הצורה של הציר הפנימי ביותר מורחבת ל-fft_length[-1] אם fft_length[-1] הוא ערך שאינו אפס, ומסקנת את החלק של האות המומר מעבר לתדר Nyquist מהערך הקונגרונטי ההפוך של הרשומות מ-1 ל-fft_length[-1] // 2 + 1 . |
FFT רב-מימדי
אם מציינים יותר מ-fft_length
אחד, הפעולה הזו זהה להפעלת מפל של פעולות FFT בכל אחד מהצירים הפנימיים ביותר. שימו לב שבמקרים של המרה מ-real ל-complex ומ-complex ל-real, טרנספורמציית הציר הפנימי ביותר מתבצעת (למעשה) קודם (RFFT, אחרונה ב-IRFFT), ולכן הציר הפנימי ביותר הוא זה שמשתנה. לאחר מכן, טרנספורמציות ציר אחרות יהיו מסוג complex->complex.
פרטי ההטמעה
ה-FFT במעבד (CPU) נתמך על ידי TensorFFT של Eigen. GPU FFT משתמש ב-cuFFT.
איסוף
פעולת האיסוף של XLA מחברת כמה פרוסות (כל פרוסה יכולה להיות במרווח זמן שונה בסביבת זמן הריצה) של מערך קלט.
סמנטיקה כללית
מידע נוסף זמין במאמר XlaBuilder::Gather
.
תיאור אינטואיטיבי יותר זמין בקטע 'תיאור לא רשמי' שבהמשך.
gather(operand, start_indices, offset_dims, collapsed_slice_dims, slice_sizes, start_index_map)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
המערך שממנו אנחנו אוספים. |
start_indices |
XlaOp |
מערך שמכיל את האינדקסים ההתחלתיים של הפרוסות שאנחנו אוספים. |
index_vector_dim |
int64 |
המאפיין ב-start_indices ש'מכיל' את המדדים ההתחלתיים. תיאור מפורט מופיע בהמשך. |
offset_dims |
ArraySlice<int64> |
קבוצת המאפיינים בפורמט הפלט שמשוייכים למערך שנחתך מהאופרטור. |
slice_sizes |
ArraySlice<int64> |
slice_sizes[i] הוא הגבולות של הפרוסה במאפיין i . |
collapsed_slice_dims |
ArraySlice<int64> |
קבוצת המאפיינים בכל פרוסה שמכווצת. המאפיינים האלה חייבים להיות בגודל 1. |
start_index_map |
ArraySlice<int64> |
מפה שמתארת איך למפות אינדקסים ב-start_indices לאינדקסים חוקיים באופרטנד. |
indices_are_sorted |
bool |
האם מובטח שהמדדים ימוינו על ידי מבצע הקריאה החוזרת. |
לנוחיותכם, אנחנו מוסיפים תוויות למאפיינים במערך הפלט שלא נמצאים ב-offset_dims
כ-batch_dims
.
הפלט הוא מערך בדרגה batch_dims.size
+ offset_dims.size
.
הערך operand.rank
חייב להיות שווה לסכום של offset_dims.size
ו-collapsed_slice_dims.size
. בנוסף, הערך של slice_sizes.size
חייב להיות שווה ל-operand.rank
.
אם index_vector_dim
שווה ל-start_indices.rank
, אנחנו מתייחסים ל-start_indices
כאל מאפיין עם מאפיין 1
בסופו (כלומר, אם start_indices
היה בצורה [6,7]
ו-index_vector_dim
הוא 2
, אנחנו מתייחסים ל-start_indices
כאל [6,7,1]
).
הגבולות של מערך הפלט לאורך המאפיין i
יחושבו כך:
אם הערך
i
נמצא ב-batch_dims
(כלומר, הוא שווה ל-batch_dims[k]
עבורk
מסוים), בוחרים את גבולות המאפיין התואמים מתוךstart_indices.shape
, ודילוגים עלindex_vector_dim
(כלומר, בוחרים ב-start_indices.shape.dims
[k
] אםk
<index_vector_dim
וב-start_indices.shape.dims
[k
+1
] במקרים אחרים).אם
i
נמצא ב-offset_dims
(כלומר שווה ל-offset_dims
[k
] בשביל חלק מ-k
), אנחנו בוחרים את הגבול המתאים מתוךslice_sizes
אחריcollapsed_slice_dims
(כלומר, אנחנו בוחרים אתadjusted_slice_sizes
[k
] כאשרadjusted_slice_sizes
הואslice_sizes
לאחר הסרת הגבולות באינדקסיםcollapsed_slice_dims
).
באופן רשמי, אינדקס המשתנה In
שתואם לאינדקס פלט Out
נתון מחושב באופן הבא:
מגדירים את
G
= {Out
[k
] fork
inbatch_dims
}. משתמשים ב-G
כדי לחתוך את הווקטורS
כך ש-S
[i
] =start_indices
[Combine(G
,i
)] כאשר Combine(A, b) מוסיף את b למיקוםindex_vector_dim
ב-A. שימו לב שזה מוגדר היטב גם אםG
ריק: אםG
ריק,S
=start_indices
.יוצרים אינדקס התחלה,
S
in
, ב-operand
באמצעותS
, על ידי פיזורS
באמצעותstart_index_map
. באופן מדויק יותר:S
in
[start_index_map
[k
]] =S
[k
] אםk
<start_index_map.size
.S
in
[_
] =0
אחרת.
יוצרים אינדקס
O
in
ב-operand
על ידי פיזור האינדקסים במאפייני ה-offset ב-Out
בהתאם לקבוצהcollapsed_slice_dims
. באופן מדויק יותר:O
in
[remapped_offset_dims
(k
)] =Out
[offset_dims
[k
]] אםk
<offset_dims.size
(remapped_offset_dims
מוגדר בהמשך).O
in
[_
] =0
אחרת.
In
הואO
in
+S
in
, כאשר + הוא חיבור של רכיבים.
remapped_offset_dims
היא פונקציה מונוטונית עם הדומיין [0
,
offset_dims.size
) והטווח [0
, operand.rank
) \ collapsed_slice_dims
. למשל, אם offset_dims.size
הוא 4
, operand.rank
הוא 6
ו-collapsed_slice_dims
הוא {0
, 2
}, אז remapped_offset_dims
הוא {0
→1
, 1
→3
, 2
→4
, 3
→5
}.
אם המדיניות indices_are_sorted
מוגדרת כ-True, XLA יכול להניח שהערכים start_indices
ממוינים (בסדר עולה, אחרי פיזור הערכים לפי start_index_map
) לפי המשתמש. אם זה לא המצב, הסמנטיקה מוגדרת בהטמעה.
תיאור לא רשמי ודוגמאות
באופן לא רשמי, כל אינדקס Out
במערך הפלט תואם לרכיב E
במערך האופרנד, המחושב באופן הבא:
אנחנו משתמשים במאפייני האצווה ב-
Out
כדי לחפש אינדקס התחלה מ-start_indices
.אנחנו משתמשים ב-
start_index_map
כדי למפות ל-operand
את האינדקס ההתחלתי (שגודלו עשוי להיות קטן מ-operand.rank) לאינדקס התחלתי 'מלא'.אנחנו יוצרים פרוסה דינמית בגודל
slice_sizes
באמצעות אינדקס ההתחלה המלא.אנחנו משנים את הצורה של הפלחים על ידי צמצום המאפיינים
collapsed_slice_dims
. מכיוון שכל המאפיינים של הפרוסה המקוצרת חייבים להיות מוגבלים ל-1, צורת ה-reshape הזו תמיד חוקית.אנחנו משתמשים במידות ההיסט ב-
Out
כדי להוסיף לאינדקס את הפלח הזה כדי לקבל את רכיב הקלט,E
, שתואם לאינדקס הפלטOut
.
בכל הדוגמאות הבאות מגדירים את index_vector_dim
כ-start_indices.rank
עד 1
. ערכים מעניינים יותר של index_vector_dim
לא משנים את הפעולה באופן מהותי, אבל הם הופכים את הייצוג החזותי למסורבל יותר.
כדי להבין איך כל הרכיבים האלה משתלבים, נבחן דוגמה לאיסוף של 5 פרוסות בצורת [8,6]
ממערך [16,11]
. המיקום של פרוסה במערך [16,11]
יכול להיות מיוצג בווקטור אינדקס בצורה S64[2]
, כך שאפשר לייצג את הקבוצה של 5 המיקומים כמערך S64[5,2]
.
לאחר מכן, אפשר לתאר את ההתנהגות של פעולת ה-gather כטרנספורמציה של אינדקס שמקבלת את הערכים [G
,O
0
,O
1
], אינדקס בפורמט הפלט, וממפה אותו לרכיב במערך הקלט באופן הבא:
קודם אנחנו בוחרים וקטור (X
,Y
) ממערך האינדקסים שאוספים באמצעות G
.
הרכיב במערך הפלט באינדקס [G
,O
0
,O
1
] הוא הרכיב במערך הקלט באינדקס [X
+O
0
,Y
+O
1
].
הערך של slice_sizes
הוא [8,6]
, שמחליט על הטווח של O0
ושל O1
, והם קובעים את גבולות הפרוסה.
פעולת האיסוף הזו משמשת כפרוסה דינמית באצווה עם G
בתור המאפיין באצווה.
אינדקסי האיסוף יכולים להיות מרובת-ממדיים. לדוגמה, גרסה כללית יותר של הדוגמה שלמעלה, שמשתמשת במערך 'gather indices' בפורמט [4,5,2]
, תתרגם את המדדים כך:
שוב, הפונקציה הזו פועלת כפלח דינמי של קבוצה G
0
ו-G
1
כמאפייני הקבוצה. גודל הפלח הוא עדיין [8,6]
.
פעולת ה-gather ב-XLA מבצעת הכללה של הסמנטיקה הבלתי רשמית שמפורטת למעלה בדרכים הבאות:
אנחנו יכולים להגדיר אילו מאפיינים בצורת הפלט הם מאפייני ההיסט (מאפיינים שמכילים את
O
0
,O
1
בדוגמה האחרונה). מאפייני האצווה של הפלט (מאפיינים שמכילים את הערכיםG
0
ו-G
1
בדוגמה האחרונה) מוגדרים כמאפייני הפלט שאינם מאפייני הזזה.מספר מאפייני ההיסט של הפלט שנמצאים באופן מפורש בצורת הפלט עשוי להיות קטן מרמת הקלט. המאפיינים ה'חסרים' האלה, שרשומים במפורש כ-
collapsed_slice_dims
, חייבים להיות בגודל פרוסה של1
. מכיוון שגודל הפלחים שלהם הוא1
, האינדקס היחיד שתקף עבורם הוא0
, והשמטת הפלחים לא יוצרת אי-בהירות.לפרוסת הנתונים שחולצה מהמערך Gather Indices ((
X
,Y
) בדוגמה האחרונה) יכולים להיות פחות רכיבים מאשר הדירוג של מערך הקלט, ומיפוי מפורש קובע איך צריך להרחיב את האינדקס כדי שיהיה לו אותו דירוג כמו הקלט.
בדוגמה האחרונה, אנחנו משתמשים ב-(2) וב-(3) כדי להטמיע את tf.gather_nd
:
G
0
ו-G
1
משמשים לחיתוך אינדקס התחלה ממערך אינדקסי האיסוף, כמו תמיד, אלא שהאינדקס ההתחלתי מכיל רק רכיב אחד, X
. באופן דומה, יש רק אינדקס אחד של היסט פלט עם הערך O
0
. כך כבר משתמשים כאינדקסים למערך הקלט של מערך הקלט. השדות האלה הורחבו כך קיימים כאינדקס כאינדקס למערך הקלט של מערך הקלט. הם מורחבים. כך ההוראות המורחבות כאי ההוראות שאינדקס למערך הקלט במערך הקלט. הן מפורטות בהתאם ל- [O
[שניהם { Gather_מא הזו מיפוי מיפוי (start_index_map
(start_index_map
ב
ב תיאור רשמי) ו-[[
ב-(start_index_map
ב[הפי תיאור מפורט) ו- ל- [במיפוי ל- [ מתוך [ב
[ מתוך [. [ מתוך ה
היא תיאור ה
היא [אינדקס במערך הקלט של מערך הקלט?] הם הורחבו בהתאם ל- [ O
0
{ מא ה- { מא ה- המטרה לremapped_offset_dims
X
X
0
0
0
0
0
0
0
0
O
O
G
G
G
G
1
1
GatherIndices
tf.gather_nd
slice_sizes
במקרה הזה הוא [1,11]
. באופן אינטואיטיבי, פירוש הדבר הוא שכל אינדקס אינדקס X
במערך האיסוף של האינדקסים בוחר שורה שלמה, והתוצאה היא שרשור כל השורות האלה.
GetDimensionSize
למידע נוסף, ראו XlaBuilder::GetDimensionSize
.
הפונקציה מחזירה את הגודל של המאפיין הנתון של המשתנה. האופרנד חייב להיות בצורת מערך.
GetDimensionSize(operand, dimension)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך קלט ממדי של n |
dimension |
int64 |
ערך בטווח [0, n) שקובע את המאפיין |
SetDimensionSize
מידע נוסף זמין במאמר XlaBuilder::SetDimensionSize
.
מגדיר את הגודל הדינמי של המאפיין הנתון של XlaOp. האופרנד חייב להיות בצורת מערך.
SetDimensionSize(operand, size, dimension)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך קלט ממדי של n. |
size |
XlaOp |
int32 שמייצג את הגודל הדינמי בסביבת זמן הריצה. |
dimension |
int64 |
ערך בטווח [0, n) שמציין את המאפיין. |
כתוצאה מכך, מעבירים את האופרנד, כשהמהדר (compiler) עוקב אחרי מאפיין דינמי.
פעולות של הפחתת מידע ב-downstream יתעלמו מערכים מרווחים.
let v: f32[10] = f32[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
let five: s32 = 5;
let six: s32 = 6;
// Setting dynamic dimension size doesn't change the upper bound of the static
// shape.
let padded_v_five: f32[10] = set_dimension_size(v, five, /*dimension=*/0);
let padded_v_six: f32[10] = set_dimension_size(v, six, /*dimension=*/0);
// sum == 1 + 2 + 3 + 4 + 5
let sum:f32[] = reduce_sum(padded_v_five);
// product == 1 * 2 * 3 * 4 * 5
let product:f32[] = reduce_product(padded_v_five);
// Changing padding size will yield different result.
// sum == 1 + 2 + 3 + 4 + 5 + 6
let sum:f32[] = reduce_sum(padded_v_six);
GetTupleElement
למידע נוסף, ראו XlaBuilder::GetTupleElement
.
הוספת אינדקסים לקבוצת ערכים (tuple) עם ערך קבוע בזמן הידור.
הערך חייב להיות קבוע-זמן הידור כדי שההסקת הצורה תוכל לקבוע את סוג הערך שמתקבל.
זה דומה ל-std::get<int N>(t)
ב-C++. באופן קונספטואלי:
let v: f32[10] = f32[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
let s: s32 = 5;
let t: (f32[10], s32) = tuple(v, s);
let element_1: s32 = gettupleelement(t, 1); // Inferred shape matches s32.
מידע נוסף זמין במאמר tf.tuple
.
Infeed
מידע נוסף זמין במאמר XlaBuilder::Infeed
.
Infeed(shape)
ארגומנט | סוג | סמנטיקה |
---|---|---|
shape |
Shape |
צורת הנתונים שנקראו מהממשק של המודעות שבתוכן. שדה הפריסה של הצורה צריך להיות מוגדר כך שיתאים לפריסה של הנתונים שנשלחים למכשיר. אחרת, ההתנהגות שלו לא מוגדרת. |
הפונקציה קוראת פריט נתונים יחיד מממשק הסטרימינג המשתמעים של ה-Infeed במכשיר, מפענחת את הנתונים לפי הצורה נתונה והפריסה שלה ומחזירה XlaOp
של הנתונים. מותר להשתמש בכמה פעולות Infeed במהלך חישוב, אבל חייב להיות סדר מוחלט בין פעולות ה-Infeed. לדוגמה, לשני מודעות In-feed בקוד שבהמשך יש סדר כולל כי יש תלות בין לולאות ה-while.
result1 = while (condition, init = init_value) {
Infeed(shape)
}
result2 = while (condition, init = result1) {
Infeed(shape)
}
אין תמיכה בצורות של צמדי ערכים (tuple) בתצוגת עץ. אם תבצעו פעולה על טופל (tuple) ריק, פעולת ה-Infeed תהיה למעשה פעולה ללא תוצאה (no-op) והיא תמשיך בלי לקרוא נתונים מה-Infeed של המכשיר.
Iota
למידע נוסף, ראו XlaBuilder::Iota
.
Iota(shape, iota_dimension)
יצירת ליטרל קבוע במכשיר במקום העברה גדולה של מארח. יצירת מערך בצורה שצוינה, שמכיל ערכים שמתחילים באפס וממשיכים בעלייה של אחד לאורך המאפיין שצוין. לסוגים של נקודה צפה, המערך שנוצר שווה ל-ConvertElementType(Iota(...))
, כאשר Iota
הוא מסוג שלם וההמרה היא לסוג של נקודה צפה.
ארגומנטים | סוג | סמנטיקה |
---|---|---|
shape |
Shape |
צורת המערך שנוצרה על ידי Iota() |
iota_dimension |
int64 |
המאפיין שבו רוצים להגדיל את הערך. |
לדוגמה, הפקודה Iota(s32[4, 8], 0)
תחזיר
[[0, 0, 0, 0, 0, 0, 0, 0 ],
[1, 1, 1, 1, 1, 1, 1, 1 ],
[2, 2, 2, 2, 2, 2, 2, 2 ],
[3, 3, 3, 3, 3, 3, 3, 3 ]]
אפשרות החזרה במחיר Iota(s32[4, 8], 1)
[[0, 1, 2, 3, 4, 5, 6, 7 ],
[0, 1, 2, 3, 4, 5, 6, 7 ],
[0, 1, 2, 3, 4, 5, 6, 7 ],
[0, 1, 2, 3, 4, 5, 6, 7 ]]
מפה
למידע נוסף, ראו XlaBuilder::Map
.
Map(operands..., computation)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
רצף של N XlaOp s |
N מערכים מסוגים T0..T{N-1} |
computation |
XlaComputation |
חישוב מסוג T_0, T_1, .., T_{N + M -1} -> S עם N פרמטרים מסוג T ו-M מסוג שרירותי |
dimensions |
מערך int64 |
מערך של מאפייני מפה |
מחילה פונקציה סקלרית על המערכים הנתונים של operands
, וכך מפיקה מערך של אותם מימדים, כאשר כל רכיב הוא תוצאה של הפונקציה הממופה שמוחלת על הרכיבים התואמים במערכי הקלט.
הפונקציה הממופתת היא חישוב שרירותי עם ההגבלה שיש לה N מקורות קלט מסוג סקלר T
ופלט יחיד מסוג S
. לפלט יש את אותן מאפיינים כמו לאופרטורים, מלבד העובדה שסוג הרכיב T מוחלף ב-S.
לדוגמה: Map(op1, op2, op3, computation, par1)
ממפה את elem_out <-
computation(elem1, elem2, elem3, par1)
בכל אינדקס (רב-מימדי) במערכי הקלט כדי ליצור את מערך הפלט.
OptimizationBarrier
מונעת מכל שלב אופטימיזציה להעביר חישובים מעבר למחסום.
מוודא שכל הקלט מוערך לפני אופרטורים שתלויים בפלט של המחסום.
משטח מונע החלקה
מידע נוסף זמין במאמר XlaBuilder::Pad
.
Pad(operand, padding_value, padding_config)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T |
padding_value |
XlaOp |
סקלר מסוג T כדי למלא את המילוי שנוסף |
padding_config |
PaddingConfig |
כמות המרווח הפנימי בשני הקצוות (נמוך, גבוה) ובין הרכיבים של כל מאפיין |
הרחבה של מערך operand
הנתון על ידי הוספת padding_value
נתונים מסביב למערך וגם בין הרכיבים של המערך. padding_config
מציין את כמות המילוי בקצוות ואת כמות המילוי הפנימי לכל מאפיין.
PaddingConfig
הוא שדה חוזר של PaddingConfigDimension
, שמכיל שלושה שדות לכל מאפיין: edge_padding_low
, edge_padding_high
ו-interior_padding
.
edge_padding_low
ו-edge_padding_high
מציינים את כמות המילוי שנוספה בקצוות הנמוכים (לצד המדד 0) ובקצוות הגבוהים (לצד המדד הגבוה ביותר) של כל מאפיין, בהתאמה. כמות המרווח הפנימי בקצוות יכולה להיות שלילית – הערך המוחלט של המרווח הפנימי השלילי מציין את מספר הרכיבים שצריך להסיר מהמאפיין שצוין.
interior_padding
מציין את המרווח הפנימי שנוסף בין שני רכיבים בכל מאפיין. הוא לא יכול להיות שלילי. הוספת מרווח פנימי מתרחשת מבחינה לוגית לפני הוספת מרווח קצה, כך שבמקרה של הוספת מרווח קצה שלילי, הרכיבים יוסרו מהאופרטנד עם הוספת מרווח פנימי.
הפעולה הזו היא פעולה ללא תוצאה (no-op) אם כל זוגות המרווחים בקצוות הם (0, 0) וכל ערכי המרווחים הפנימיים הם 0. האיור הבא מציג דוגמאות לערכי edge_padding
ו-interior_padding
שונים עבור מערך דו-ממדי.
Recv
מידע נוסף זמין במאמר XlaBuilder::Recv
.
Recv(shape, channel_handle)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
shape |
Shape |
את הצורה של הנתונים לקבל |
channel_handle |
ChannelHandle |
מזהה ייחודי לכל צמד שליחה/אישור |
מקבל נתונים בפורמט הנתונים הנתון מהוראה Send
במחשוב אחר שמשתמש באותו כינוי ערוץ. הפונקציה מחזירה XlaOp עבור הנתונים שהתקבלו.
ה-API של הלקוח בפעולה Recv
מייצג תקשורת סנכרונית.
עם זאת, ההוראה מחולקת באופן פנימי ל-2 הוראות HLO (Recv
ו-RecvDone
) כדי לאפשר העברות נתונים אסינכרוניות. מידע נוסף זמין במאמרים HloInstruction::CreateRecv
ו-HloInstruction::CreateRecvDone
.
Recv(const Shape& shape, int64 channel_id)
הקצאת המשאבים הנדרשים לקבלת נתונים מהוראה Send
עם אותו channel_id. הפונקציה מחזירה הקשר של המשאבים שהוקצו, שמשמש את ההוראה הבאה של RecvDone
כדי להמתין להשלמת העברת הנתונים. ההקשר הוא שלל {receive buffer (shape), requestidentifier
(U32)}, ואפשר להשתמש בו רק בהוראה RecvDone
.
RecvDone(HloInstruction context)
בהתאם להקשר שנוצר על ידי הוראה Recv
, הפונקציה ממתינה להשלמת העברת הנתונים ומחזירה את הנתונים שהתקבלו.
הקטנה
מידע נוסף זמין במאמר XlaBuilder::Reduce
.
החלת פונקציית הפחתה על מערך אחד או יותר במקביל.
Reduce(operands..., init_values..., computation, dimensions)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
רצף של N XlaOp |
N מערכים מסוגים T_0, ..., T_{N-1} . |
init_values |
רצף של N XlaOp |
N סקלריים מסוגים T_0, ..., T_{N-1} . |
computation |
XlaComputation |
חישוב מסוג T_0, ..., T_{N-1}, T_0, ..., T_{N-1} -> Collate(T_0, ..., T_{N-1}) . |
dimensions |
מערך int64 |
מערך לא מסודר של מימדים להקטנה. |
כאשר:
- הערך של N חייב להיות גדול מ-1 או שווה לו.
- החישוב צריך להיות אסוציאטיבי "בערך" (ראו בהמשך).
- לכל מערכי הקלט צריכים להיות אותם מאפיינים.
- כל הערכים הראשוניים צריכים ליצור זהות ב-
computation
. - אם הערך שלו הוא
N = 1
, הערך שלCollate(T)
הואT
. - אם הערך שלו הוא
N > 1
,Collate(T_0, ..., T_{N-1})
הוא שילוב שלN
רכיבים מסוגT
.
הפעולה הזו מפחיתה מימד אחד או יותר של כל מערך קלט למשתני סקלאר.
הדירוג של כל מערך מוחזר הוא rank(operand) - len(dimensions)
. הפלט של הפעולה הוא Collate(Q_0, ..., Q_N)
, כאשר Q_i
הוא מערך מסוג T_i
, שהמאפיינים שלו מתוארים בהמשך.
לקצוות עורפיים שונים מותר לשייך מחדש את חישוב הפחתת הנתונים. זה יכול להוביל להבדלים מספריים, כי חלק מפונקציות ההפחתה כמו חיבור לא שיוכים לצפים. עם זאת, אם טווח הנתונים מוגבל, הוספה של נקודה צפה קרובה מספיק להיות אסוציטיבית לרוב השימושים המעשיים.
דוגמאות
כשמפחיתים מימד אחד במערך חד-ממדי יחיד עם הערכים [10, 11,
12, 13]
, עם פונקציית ההפחתה f
(ה היא computation
), ניתן לחשב את הערך הזה באופן הבא:
f(10, f(11, f(12, f(init_value, 13)))
אבל יש גם עוד הרבה אפשרויות אחרות, למשל
f(init_value, f(f(10, f(init_value, 11)), f(f(init_value, 12), f(init_value, 13))))
בדוגמה הבאה מופיע פסאודו-קוד שמראה איך אפשר להטמיע הפחתה, באמצעות סיכום כחישוב ההפחתה עם ערך ראשוני של 0.
result_shape <- remove all dims in dimensions from operand_shape
# Iterate over all elements in result_shape. The number of r's here is equal
# to the rank of the result
for r0 in range(result_shape[0]), r1 in range(result_shape[1]), ...:
# Initialize this result element
result[r0, r1...] <- 0
# Iterate over all the reduction dimensions
for d0 in range(dimensions[0]), d1 in range(dimensions[1]), ...:
# Increment the result element with the value of the operand's element.
# The index of the operand's element is constructed from all ri's and di's
# in the right order (by construction ri's and di's together index over the
# whole operand shape).
result[r0, r1...] += operand[ri... di]
דוגמה לצמצום מערך דו-מימדי (מטריצה). לדוגמה, למבנה יש דרגה 2, מאפיין 0 בגודל 2 ומאפיין 1 בגודל 3:
תוצאות של צמצום מימדים 0 או 1 עם פונקציית "add":
חשוב לזכור ששתי תוצאות הפחתת המידע הן מערכי 1D. בדיאגרמה רואים אחת כעמודה והשנייה כשורה, מטעמי נוחות.
דוגמה מורכבת יותר היא מערך תלת-ממדי. הדירוג שלו הוא 3, המאפיין 0 הוא בגודל 4, המאפיין 1 הוא בגודל 2 והמאפיין 2 הוא בגודל 3. כדי לפשט את הדברים, הערכים 1 עד 6 מוכפלים במאפיין 0.
בדומה לדוגמה הדו-ממדית, ניתן לצמצם רק מאפיין אחד. לדוגמה, אם מפחיתים את המאפיין 0, מקבלים מערך מסדר 2 שבו כל הערכים במאפיין 0 מקופלים למערך סקלר:
| 4 8 12 |
| 16 20 24 |
אם מפחיתים את מאפיין 2, אנחנו מקבלים גם מערך עם דירוג 2 שבו כל הערכים שבמאפיין 2 מקפלים לצורת סקלר:
| 6 15 |
| 6 15 |
| 6 15 |
| 6 15 |
שימו לב שהסדר היחסי בין שאר המאפיינים בקלט נשמר בפלט, אבל יכול להיות שלחלק מהמאפיינים יוקצו מספרים חדשים (בגלל שהדירוג משתנה).
אפשר גם לצמצם כמה מאפיינים. על ידי חיסור המאפיינים 0 ו-1 נוצר המערך הדו-ממדי [20, 28, 36]
.
הקטנת המערך התלת-ממדי על כל המאפיינים שלו יוצרת את הערך 84
הסקלר.
פונקציית Reduce עם מספר פרמטרים
כש-N > 1
, החלת הפונקציה reduce מורכבת יותר, כי היא חלה בו-זמנית על כל הקלט. אופרטנדים מסופקים לחישוב לפי הסדר הבא:
- הרצת ערך מוקטן של האופרנד הראשון
- ...
- הרצת ערך מוקטן של אופרנד N'
- ערך קלט לאופרנד הראשון
- ...
- ערך הקלט של האופרנד ה-N
לדוגמה, נבחן את פונקציית הפחתת הערך הבאה, שבה אפשר להשתמש כדי לחשב את הערך המקסימלי ואת הערך argmax של מערך דו-מימדי במקביל:
f: (Float, Int, Float, Int) -> Float, Int
f(max, argmax, value, index):
if value >= max:
return (value, index)
else:
return (max, argmax)
למערכי קלט מסוג 1-D V = Float[N], K = Int[N]
ולערכי אתחול I_V = Float, I_K = Int
, התוצאה של f_(N-1)
של צמצום במאפיין הקלט היחיד מקבילה לאפליקציה הרקורסיבית הבאה:
f_0 = f(I_V, I_K, V_0, K_0)
f_1 = f(f_0.first, f_0.second, V_1, K_1)
...
f_(N-1) = f(f_(N-2).first, f_(N-2).second, V_(N-1), K_(N-1))
החלת הפחתה זו על מערך של ערכים ועל מערך של אינדקסים רציפים (כלומר iota) תבצע איטרציה משותפת על המערכים ותחזיר קבוצת ערכים (tuple) שמכילה את הערך המקסימלי ואת האינדקס התואם.
ReducePrecision
למידע נוסף, ראו XlaBuilder::ReducePrecision
.
ממחישה את ההשפעה של המרת ערכים בנקודה צפה לפורמט ברמת דיוק נמוכה יותר (למשל IEEE-FP16) ובחזרה לפורמט המקורי. מספר הביטים של המעריך והמנטסה בפורמט עם רמת דיוק נמוכה יותר אפשר לציין באופן שרירותי, אבל יכול להיות שלא כל גדלי הביטים נתמכים בכל הטמעות החומרה.
ReducePrecision(operand, mantissa_bits, exponent_bits)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך של נקודה צפה מסוג T . |
exponent_bits |
int32 |
מספר הביטים של המעריך בפורמט עם דיוק נמוך יותר |
mantissa_bits |
int32 |
מספר ביטים של מנטיסה בפורמט ברמת דיוק נמוכה |
התוצאה היא מערך מסוג T
. ערכי הקלט מעוגלים לערך הקרוב ביותר שאפשר לייצג במספר הביטים של המנטיסה (באמצעות סמנטיקה של 'קישור לזוגי'), וכל הערכים שחורגים מהטווח שצוין במספר הביטים של המעריך מוצמדים לאינסוף חיובי או שלילי. ערכי NaN
נשמרים, אבל יכול להיות שהם יומרו לערכי NaN
קנוניקליים.
בפורמט עם רמת דיוק נמוכה יותר חייב להיות לפחות ביט אחד של מעריך (כדי להבדיל בין ערך אפס לבין אינסוף, כי לשניהם יש חזקה אפס), וגם מספר לא שלילי של ביטים של חזקה. מספר הביטים של המעריך או של המנטיסה עשוי לחרוג מהערך התואם לסוג T
. במקרה כזה, החלק המתאים של ההמרה הוא פשוט פעולה ללא תוצאה (no-op).
ReduceScatter
למידע נוסף, ראו XlaBuilder::ReduceScatter
.
ReduceScatter היא פעולה קולקטיבית שמבצעת ביעילות AllReduce, ואז מפזרת את התוצאה על ידי פיצולה ל-shard_count
בלוקים לאורך scatter_dimension
, והרפליקה i
בקבוצת הרפליקות מקבלת את הפלח ith
.
ReduceScatter(operand, computation, scatter_dim, shard_count,
replica_group_ids, channel_id)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך או קבוצת מערכי tupliות לא ריקה שצריך לצמצם בין הרפליקות. |
computation |
XlaComputation |
חישוב ההנחה |
scatter_dimension |
int64 |
מאפיין לפיזור. |
shard_count |
int64 |
מספר הבלוקטים לפצל scatter_dimension |
replica_groups |
וקטור של וקטורים של int64 |
הקבוצות שביניהן מתבצעות הפחתות |
channel_id |
int64 אופציונלי |
מזהה ערוץ אופציונלי לתקשורת בין מודולים |
- כש-
operand
הוא המספר של מערכים, הפיזור של ההפחתה מתבצע על כל רכיב של הצירוף. replica_groups
הוא רשימה של קבוצות רפליקות שביניהן מבוצע ההפחתה (מזהה הרפליקציה של הרפליקה הנוכחית אפשר לאחזר באמצעותReplicaId
). סדר הרפליקות בכל קבוצה קובע את הסדר שבו תפוצל התוצאה של ההפחתה הכוללת. השדהreplica_groups
חייב להיות ריק (במקרה כזה כל הרפליקות שייכות לקבוצה אחת) או להכיל את אותו מספר רכיבים כמו מספר הרפליקות. אם יש יותר מקבוצת רפליקות אחת, כולן חייבות להיות באותו גודל. לדוגמה,replica_groups = {0, 2}, {1, 3}
מבצע צמצום בין הרפליקות0
ו-2
, לבין1
ו-3
, ואז מפזר את התוצאה.shard_count
הוא הגודל של כל קבוצת רפליקות. אנחנו זקוקים לזה במקרים שבהם הערכים שלreplica_groups
ריקים. אם השדהreplica_groups
לא ריק, הערך שלshard_count
חייב להיות שווה לגודל של כל קבוצת רפליקות.- השדה
channel_id
משמש לתקשורת בין מודולים: רק פעולותreduce-scatter
עם אותוchannel_id
יכולות לתקשר זו עם זו.
צורת הפלט היא צורת הקלט, כאשר scatter_dimension
קטנה פי shard_count
. לדוגמה, אם יש שתי רפליקות והאופרנד הוא [1.0, 2.25]
ו-[3.0, 5.25]
, בהתאמה, בשתי הרפליקות, ערך הפלט של הפעולה הזו, כאשר scatter_dim
הוא 0
, יהיה [4.0]
ברפליקה הראשונה ו-[7.5]
ברפליקה השנייה.
ReduceWindow
למידע נוסף, ראו XlaBuilder::ReduceWindow
.
הפונקציה מחילה פונקציית הפחתה על כל הרכיבים בכל חלון של רצף של N מערכים מרובי-ממדים, ויוצרת מערך יחיד או קבוצה של N מערכים מרובי-ממדים כפלט. לכל מערך פלט יש אותו מספר רכיבים כמו מספר המיקומים החוקיים של החלון. אפשר לבטא שכבת צבירה בתור ReduceWindow
. בדומה ל-Reduce
, הערך של computation
שהוחל תמיד מועבר ל-init_values
בצד ימין.
ReduceWindow(operands..., init_values..., computation, window_dimensions,
window_strides, padding)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
N XlaOps |
רצף של N מערכים רב-ממדיים מהסוגים T_0,..., T_{N-1} , שכל אחד מהם מייצג את אזור הבסיס שעליו החלון ממוקם. |
init_values |
N XlaOps |
N הערכים ההתחלתיים של הפחתה, אחד לכל אחד מ-N המשתנים. אפשר לקרוא פרטים נוספים בקטע צמצום. |
computation |
XlaComputation |
פונקציית הפחתה מסוג T_0, ..., T_{N-1}, T_0, ..., T_{N-1} -> Collate(T_0, ..., T_{N-1}) , שמיושמת על רכיבים בכל חלון של כל האופרנדים של הקלט. |
window_dimensions |
ArraySlice<int64> |
מערך של מספרים שלמים לערכי מאפייני החלון |
window_strides |
ArraySlice<int64> |
מערך מספרים שלמים לערכים של צעדים בחלון הזמן |
base_dilations |
ArraySlice<int64> |
מערך מספרים שלמים לערכים של הרחבה בסיסית |
window_dilations |
ArraySlice<int64> |
מערך של מספרים שלמים לערכים של הרחבת חלון |
padding |
Padding |
סוג המילוי של החלון (Padding::kSame, שמוסיף מילוי כדי שהצורה של הפלט תהיה זהה לזו של הקלט אם הצעידה היא 1, או Padding::kValid, שלא משתמש במילוי ו"עוצר" את החלון ברגע שהוא כבר לא מתאים) |
כאשר:
- N צריך להיות שווה ל-1 או גדול מ-1.
- לכל מערכי הקלט צריכים להיות אותם מאפיינים.
- אם הערך שלו הוא
N = 1
, הערך שלCollate(T)
הואT
. - אם הערך שלו הוא
N > 1
,Collate(T_0, ..., T_{N-1})
הוא שילוב שלN
רכיבים מסוג(T0,...T{N-1})
.
בקוד ובאיור שבהמשך מוצגת דוגמה לשימוש ב-ReduceWindow
. הקלט הוא מטריקס בגודל [4x6], וגם window_dimensions וגם window_stride_dimensions הם בגודל [2x3].
// Create a computation for the reduction (maximum).
XlaComputation max;
{
XlaBuilder builder(client_, "max");
auto y = builder.Parameter(0, ShapeUtil::MakeShape(F32, {}), "y");
auto x = builder.Parameter(1, ShapeUtil::MakeShape(F32, {}), "x");
builder.Max(y, x);
max = builder.Build().value();
}
// Create a ReduceWindow computation with the max reduction computation.
XlaBuilder builder(client_, "reduce_window_2x3");
auto shape = ShapeUtil::MakeShape(F32, {4, 6});
auto input = builder.Parameter(0, shape, "input");
builder.ReduceWindow(
input,
/*init_val=*/builder.ConstantLiteral(LiteralUtil::MinValue(F32)),
*max,
/*window_dimensions=*/{2, 3},
/*window_stride_dimensions=*/{2, 3},
Padding::kValid);
צעד של 1 במאפיין מציין שהמיקום של חלון במאפיין הוא רכיב אחד מהחלון הסמוך. כדי לציין שאין חפיפה בין חלונות, הערך של window_stride_dimensions צריך להיות שווה לערך של window_dimensions. באיור הבא מוצג השימוש בשני ערכים שונים של stride. הוספת הפסקה מתבצעת לכל מאפיין של הקלט, והחישובים זהים כאילו הקלט הגיע עם המאפיינים שלו אחרי הוספת הפסקה.
בדוגמה של מרווח פנימי לא טריווי, אפשר להשתמש במחשוב למינימום חלונות (הערך הראשוני הוא MAX_FLOAT
) עם המאפיין 3
ולצעוד 2
מעל מערך הקלט [10000, 1000, 100, 10, 1]
. הפונקציה kValid
מחשבת מינימומים בשני חלונות תקפים: [10000, 1000, 100]
ו-[100, 10, 1]
, וכתוצאה מכך מתקבל הפלט [100, 1]
. הפונקציה kSame
מוסיפה תחילה לאלמנטים של המערך כדי שהצורה אחרי reduce-window תהיה זהה לכניסה של צעד אחד, על ידי הוספת רכיבים ראשוניים משני הצדדים, ומקבלת את [MAX_VALUE, 10000, 1000, 100, 10, 1,
MAX_VALUE]
. הפעלת reduce-window על המערך המרופד פועלת על שלושה חלונות: [MAX_VALUE, 10000, 1000]
, [1000, 100, 10]
ו-[10, 1, MAX_VALUE]
, ומניבה את הערך [1000, 10, 1]
.
סדר ההערכה של פונקציית ההפחתה שרירותי ועשוי להיות לא קבוע. לכן, פונקציית ההפחתה לא צריכה להיות רגישה מדי לשיוך מחדש. פרטים נוספים זמינים בחלק של הדיון על שיוכים בהקשר של Reduce
.
ReplicaId
למידע נוסף, ראו XlaBuilder::ReplicaId
.
מחזירה את המזהה הייחודי (סקלר U32) של הרפליקה.
ReplicaId()
המזהה הייחודי של כל עותק הוא מספר שלם ללא סימן במרווח [0, N)
,
כאשר N
הוא מספר העותקים. מאחר שכל הרפליקות מריצות את אותה תוכנית, קריאה ל-ReplicaId()
בתוכנית תחזיר ערך שונה בכל רפליקה.
שינוי העיצוב
אפשר לעיין גם במאמר XlaBuilder::Reshape
ובפעולה Collapse
.
שינוי הצורה של המאפיינים של מערך להגדרה חדשה.
Reshape(operand, new_sizes)
Reshape(operand, dimensions, new_sizes)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T |
dimensions |
וקטור int64 |
הסדר שבו המאפיינים מקופלים |
new_sizes |
וקטור int64 |
הווקטור של מימדים חדשים |
באופן קונספטואלי, הפונקציה reshape קודם משטחת מערך וקובעת לו וקטור דו-מימדי של ערכים של נתונים, ולאחר מכן משייפת את הווקטור הזה לצורה חדשה. ארגומנטים של קלט הם מערך שרירותי מסוג T, וקטור קבוע-זמן הידור של אינדקסי מאפיינים וכן וקטור קבוע-זמן הידור של גודלי מימדים לתוצאה.
אם מציינים ערכים בעורך dimension
, הם חייבים להיות תמורה של כל המאפיינים של T. ערך ברירת המחדל אם לא מציינים ערכים הוא {0, ..., rank - 1}
. הסדר של המאפיינים ב-dimensions
הוא מהמאפיין שמשתנה לאט ביותר (החשוב ביותר) למאפיין שמשתנה במהירות הרבה ביותר (החשוב פחות) בתוך ערימת הלולאות שמצמצמת את מערך הקלט למאפיין יחיד. הווקטור new_sizes
קובע את הגודל של מערך הפלט. הערך באינדקס 0 ב-new_sizes
הוא הגודל של המאפיין 0, הערך באינדקס 1 הוא הגודל של המאפיין 1 וכן הלאה. המכפלה של המאפיינים של new_size
חייבת להיות שווה למכפלה של גדלי המאפיינים של המשתנה. כשמפיחים את המערך המכווץ למערך רב-מימדי שמוגדר על ידי new_sizes
, המאפיינים ב-new_sizes
ממוינים מהשינויים האיטיים ביותר (העיקריים ביותר) ועד לשינויים המהירים ביותר (המשניים ביותר).
לדוגמה, נניח ש-v הוא מערך של 24 רכיבים:
let v = f32[4x2x3] { { {10, 11, 12}, {15, 16, 17} },
{ {20, 21, 22}, {25, 26, 27} },
{ {30, 31, 32}, {35, 36, 37} },
{ {40, 41, 42}, {45, 46, 47} } };
In-order collapse:
let v012_24 = Reshape(v, {0,1,2}, {24});
then v012_24 == f32[24] {10, 11, 12, 15, 16, 17, 20, 21, 22, 25, 26, 27,
30, 31, 32, 35, 36, 37, 40, 41, 42, 45, 46, 47};
let v012_83 = Reshape(v, {0,1,2}, {8,3});
then v012_83 == f32[8x3] { {10, 11, 12}, {15, 16, 17},
{20, 21, 22}, {25, 26, 27},
{30, 31, 32}, {35, 36, 37},
{40, 41, 42}, {45, 46, 47} };
Out-of-order collapse:
let v021_24 = Reshape(v, {1,2,0}, {24});
then v012_24 == f32[24] {10, 20, 30, 40, 11, 21, 31, 41, 12, 22, 32, 42,
15, 25, 35, 45, 16, 26, 36, 46, 17, 27, 37, 47};
let v021_83 = Reshape(v, {1,2,0}, {8,3});
then v021_83 == f32[8x3] { {10, 20, 30}, {40, 11, 21},
{31, 41, 12}, {22, 32, 42},
{15, 25, 35}, {45, 16, 26},
{36, 46, 17}, {27, 37, 47} };
let v021_262 = Reshape(v, {1,2,0}, {2,6,2});
then v021_262 == f32[2x6x2] { { {10, 20}, {30, 40},
{11, 21}, {31, 41},
{12, 22}, {32, 42} },
{ {15, 25}, {35, 45},
{16, 26}, {36, 46},
{17, 27}, {37, 47} } };
במקרה מיוחד, באמצעות שינוי דרך העיצוב הוא יכול להפוך מערך של רכיב יחיד לסקלר ולהפך. לדוגמה,
Reshape(f32[1x1] { {5} }, {0,1}, {}) == 5;
Reshape(5, {}, {1,1}) == f32[1x1] { {5} };
Rev (הפוך)
למידע נוסף, ראו XlaBuilder::Rev
.
Rev(operand, dimensions)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T |
dimensions |
ArraySlice<int64> |
המאפיינים שרוצים להפוך |
הפונקציה הופכת את הסדר של הרכיבים במערך operand
לפי הערך שצוין ב-dimensions
, ויוצרת מערך פלט באותו צורה. כל רכיב במערך האופרנד באינדקס רב-מימדי מאוחסן במערך הפלט באינדקס שהומר. כדי לבצע את הטרנספורמציה של המדד המימדי, הופכים את המדד בכל מאפיין שרוצים להפוך (כלומר, אם מאפיין בגודל N הוא אחד מהמאפיינים שרוצים להפוך, המדד שלו i הופך ל-N - 1 - i).
שימוש אחד בפעולה Rev
הוא להפוך את מערך משקל הקונבולוציה לאורך שני מאפייני החלונות במהלך חישוב ההדרגתי ברשתות נוירונים.
RngNormal
למידע נוסף, ראו XlaBuilder::RngNormal
.
יצירת פלט בצורה נתונה באמצעות מספרים אקראיים שנוצרו בהתאם להתפלגות הנורמלית \(N(\mu, \sigma)\) . לפרמטרים \(\mu\) ו- \(\sigma\)ולצורה של הפלט צריכים להיות סוג אלמנט של נקודה צפה (floating-point). בנוסף, הערכים של הפרמטרים צריכים להיות סקלריים.
RngNormal(mu, sigma, shape)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
mu |
XlaOp |
סקלר מסוג T שקובע את הממוצע של המספרים שנוצרו |
sigma |
XlaOp |
סקלר מסוג T שקובע את סטיית התקן של ה-generated |
shape |
Shape |
צורת הפלט מסוג T |
RngUniform
מידע נוסף זמין במאמר XlaBuilder::RngUniform
.
הפונקציה בונה פלט של צורה נתונה עם מספרים אקראיים שנוצרים בעקבות ההתפלגות האחידה על פני הרווח \([a,b)\). הסוגים של הפרמטרים ורכיבי הפלט צריכים להיות בוליאניים, שלמים או של נקודות צפות, והם צריכים להיות עקביים. בשלב הזה, הקצוות העורפיים של המעבד (CPU) וה-GPU תומכים רק ב-F64, ב-F32, ב-F16, ב-BF16, ב-S64, ב-U64, ב-S32 וב-U32. בנוסף, הערכים של הפרמטרים צריכים להיות סקלריים. אם \(b <= a\) התוצאה מוגדרת על ידי ההטמעה.
RngUniform(a, b, shape)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
a |
XlaOp |
סקלר מסוג T שקובע את הגבול התחתון של מרווח |
b |
XlaOp |
סקלר מסוג T שקובע את הגבול העליון של מרווח |
shape |
Shape |
צורת פלט מסוג T |
RngBitGenerator
הפונקציה יוצרת פלט בצורה נתונה שמלאה בביטים אקראיים אחידים באמצעות האלגוריתם שצוין (או ברירת המחדל לקצה העורפי), ומחזירה מצב מעודכן (באותה צורה כמו המצב הראשוני) ואת הנתונים האקראיים שנוצרו.
המצב הראשוני הוא המצב הראשוני של יצירת המספר האקראי הנוכחי. הוא והצורה הנדרשת והערכים התקפים תלויים באלגוריתם שבו נעשה שימוש.
מובטח שהפלט יהיה פונקציה גורמית (deterministic) של המצב הראשוני, אבל לא מובטח שהוא יהיה גורמי בין הקצוות העורפיים וגרסאות שונות של המהדר.
RngBitGenerator(algorithm, key, shape)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
algorithm |
RandomAlgorithm |
אלגוריתם ה-PRNG שבו יש להשתמש. |
initial_state |
XlaOp |
המצב הראשוני של אלגוריתם ה-PRNG. |
shape |
Shape |
צורת הפלט של הנתונים שנוצרו. |
הערכים הזמינים עבור algorithm
:
rng_default
: אלגוריתם ספציפי לקצה העורפי עם דרישות ספציפיות לקצה העורפי.rng_three_fry
: אלגוריתם PRNG מבוסס ThreeFry שמבוסס על מונה. הצורה שלinitial_state
היאu64[2]
עם ערכים שרירותיים. Salmon et al. SC 2011. מספרים אקראיים במקביל: קל כמו 1, 2, 3.rng_philox
: אלגוריתם Philox ליצירת מספרים אקראיים במקביל. הצורהinitial_state
היאu64[3]
עם ערכים שרירותיים. Salmon et al. SC 2011. מספרים אקראיים מקבילים: החל מ-1, 2, 3.
פיזור
פעולת הפיזור של XLA יוצרת רצף של תוצאות שהן הערכים של מערך הקלט operands
, עם כמה פרוסות (באינדקסים שצוינו על ידי scatter_indices
) שמתעדכנות ברצף הערכים ב-updates
באמצעות update_computation
.
מידע נוסף זמין במאמר XlaBuilder::Scatter
.
scatter(operands..., scatter_indices, updates..., update_computation,
index_vector_dim, update_window_dims, inserted_window_dims,
scatter_dims_to_operand_dims)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
רצף של N XlaOp |
N מערכי מסוגים T_0, ..., T_N שלפיהם יתבצע פיזור. |
scatter_indices |
XlaOp |
המערך שמכיל את האינדקסים ההתחלתיים של הפרוסות שצריך לפזר אליהן. |
updates |
רצף של N XlaOp |
N מערכים מסוגים T_0, ..., T_N . updates[i] מכיל את הערכים שצריך להשתמש בהם כדי לבצע פיזור operands[i] . |
update_computation |
XlaComputation |
החישוב שישמש לשילוב הערכים הקיימים במערך הקלט והעדכונים במהלך הפיצול. החישוב הזה צריך להיות מסוג T_0, ..., T_N, T_0, ..., T_N -> Collate(T_0, ..., T_N) . |
index_vector_dim |
int64 |
המאפיין ב-scatter_indices שמכיל את האינדקסים ההתחלתיים. |
update_window_dims |
ArraySlice<int64> |
קבוצת המאפיינים בצורה updates שהם מאפייני חלון. |
inserted_window_dims |
ArraySlice<int64> |
קבוצת מאפייני החלון שיש להוסיף לצורה updates . |
scatter_dims_to_operand_dims |
ArraySlice<int64> |
מפת מאפיינים מהאינדקסים של הפיזור למרחב של אינדקס המשתנים. המערך הזה מפורש כמיפוי של i אל scatter_dims_to_operand_dims[i] . הוא צריך להיות אחד לאחד ושלם. |
indices_are_sorted |
bool |
האם יש ערובה שהאינדקסים ימוינו על ידי מבצע הקריאה החוזרת. |
unique_indices |
bool |
האם המפנה מבטיח שהאינדקסים יהיו ייחודיים. |
כאשר:
- N צריך להיות שווה ל-1 או גדול מ-1.
operands
[0
], …,operands
[N-1
] חייבים להיות בעלי אותם מאפיינים.- המאפיינים
updates
[0
], ...,updates
[N-1
] צריכים להיות זהים. - אם הערך שלו הוא
N = 1
, הערך שלCollate(T)
הואT
. - אם הערך שלו הוא
N > 1
,Collate(T_0, ..., T_N)
הוא צירוף שלN
רכיבים מסוגT
.
אם הערך של index_vector_dim
שווה ל-scatter_indices.rank
, אנחנו מתייחסים ל-scatter_indices
כאל מאפיין 1
עם תו אחריו.
אנחנו מגדירים את update_scatter_dims
מסוג ArraySlice<int64>
כקבוצת המאפיינים בצורה updates
שלא נמצאת ב-update_window_dims
, בסדר עולה.
הארגומנטים של scatter צריכים לעמוד באילוצים הבאים:
כל מערך
updates
חייב להיות בעל דרגהupdate_window_dims.size + scatter_indices.rank - 1
.גבולות המאפיין
i
בכל מערךupdates
חייבים להתאים לתנאים הבאים:- אם
i
נמצא בפונקציהupdate_window_dims
(כלומר שווה ל-update_window_dims
[k
] בשביל חלק מהשדותk
), הגבול של המאפייןi
ב-updates
לא יחרוג מהגבול התואם שלoperand
אחרי חישוב הערךinserted_window_dims
(כלומרadjusted_window_bounds
[k
], כאשרadjusted_window_bounds
מכיל את הגבולות שלoperand
עם גבולות באינדקסיםinserted_window_dims
). - אם הערך
i
נמצא ב-update_scatter_dims
(כלומר, הוא שווה ל-update_scatter_dims
[k
] עבור ערך כלשהו שלk
), אז הגבול של המאפייןi
ב-updates
חייב להיות שווה לגבול התואם שלscatter_indices
, תוך דילוג עלindex_vector_dim
(כלומר,scatter_indices.shape.dims
[k
], אםk
<index_vector_dim
, ו-scatter_indices.shape.dims
[k+1
] במקרים אחרים).
- אם
update_window_dims
חייב להיות בסדר עולה, ללא מספרי מאפיינים חוזרים ולהיות בטווח[0, updates.rank)
.inserted_window_dims
חייב להיות בסדר עולה, בלי מספרי מאפיינים חוזרים, ובטווח[0, operand.rank)
.operand.rank
חייב להיות שווה לסכום שלupdate_window_dims.size
ו-inserted_window_dims.size
.scatter_dims_to_operand_dims.size
חייב להיות שווה ל-scatter_indices.shape.dims
[index_vector_dim
] והערכים שלו חייבים להיות בטווח[0, operand.rank)
.
לכל אינדקס נתון U
בכל מערך updates
, האינדקס המתאים I
במערך operands
המתאים שעליו צריך להחיל את העדכון הזה:
- מגדירים את
G
= {U
[k
] fork
inupdate_scatter_dims
}. משתמשים ב-G
כדי לחפש וקטור אינדקסS
במערךscatter_indices
כך ש-S
[i
] =scatter_indices
[Combine(G
,i
)] כאשר Combine(A, b) מוסיף את b למיקומיםindex_vector_dim
ב-A. - יוצרים אינדקס
S
in
ב-operand
באמצעותS
, על ידי פיזור שלS
באמצעות המפהscatter_dims_to_operand_dims
. באופן רשמי יותר:S
in
[scatter_dims_to_operand_dims
[k
]] =S
[k
] אםk
<scatter_dims_to_operand_dims.size
.S
in
[_
] =0
אחרת.
- יוצרים אינדקס
W
in
לכל מערךoperands
על ידי פיזור המדדים ב-update_window_dims
ב-U
לפיinserted_window_dims
. באופן רשמי יותר:W
in
[window_dims_to_operand_dims
(k
)] =U
[k
] אםk
הוא ב-update_window_dims
, כאשרwindow_dims_to_operand_dims
היא הפונקציה המונוטונית עם הדומיין [0
,update_window_dims.size
) והטווח [0
,operand.rank
) \inserted_window_dims
. (לדוגמה, אםupdate_window_dims.size
הוא4
,operand.rank
הוא6
ו-inserted_window_dims
הוא {0
,2
}, אזwindow_dims_to_operand_dims
הוא {0
→1
,1
→3
,2
→4
,3
→5
}).W
in
[_
] =0
אחרת.
I
הואW
in
+S
in
, כאשר + הוא חיבור של רכיבים.
לסיכום, אפשר להגדיר את פעולת הפזורה באופן הבא.
- מאתחלים את
output
עםoperands
, כלומר לכל האינדקסיםJ
, לכל האינדקסיםO
במערךoperands
[J
]:
output
[J
][O
] =operands
[J
][O
] - לכל אינדקס
U
במערךupdates
[J
] ולאינדקס התואםO
במערךoperand
[J
], אםO
הוא אינדקס חוקי עבורoutput
:
(output
[0
][O
], ...,output
[N-1
][O
]) =update_computation
(output
[0
][O
], ..., ,output
[N-1
][O
],updates
[0
][U
], ...,updates
[N-1
][U
])
הסדר שבו מתבצעים העדכונים לא קובע. לכן, כשמספר אינדקסים ב-updates
מפנים לאותו אינדקס ב-operands
, הערך התואם ב-output
יהיה לא דטרמיניסטי.
שימו לב שהפרמטר הראשון שמוענק ל-update_computation
יהיה תמיד הערך הנוכחי מהמערך output
, והפרמטר השני יהיה תמיד הערך מהמערך updates
. הדבר חשוב במיוחד במקרים שבהם הפונקציה update_computation
לא מחליפת מקום.
אם הערך של indices_are_sorted
מוגדר כ-True, המערכת של XLA יכולה להניח שהמשתמש ממיין את scatter_indices
(במספר עולה, אחרי פיזור הערכים שלו לפי scatter_dims_to_operand_dims
). אם זה לא המצב, הסמנטיקה מוגדרת כהטמעה.
אם הערך של unique_indices
מוגדר כ-True, ה-XLA יכול להניח שכל הרכיבים שמפוזרים הם ייחודיים. כך XLA יכול להשתמש בפעולות לא אטומיות. אם הערך של unique_indices
מוגדר כ-true והאינדקסים שאליהם מתבצעת הפצת הנתונים הם לא ייחודיים, הסמנטיקה מוגדרת על ידי ההטמעה.
באופן לא מובן, אפשר לראות את אופציית הפיזור כהופכי של פעולת האיסוף, כלומר
לתיאור מפורט ולדוגמאות לא רשמיות, עיינו בקטע 'תיאור בלתי רשמי' ב-Gather
.
בחירה
מידע נוסף זמין במאמר XlaBuilder::Select
.
יצירת מערך פלט מרכיבים של שני מערכי קלט, על סמך הערכים של מערך תנאי.
Select(pred, on_true, on_false)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
pred |
XlaOp |
מערך מסוג PRED |
on_true |
XlaOp |
מערך מסוג T |
on_false |
XlaOp |
מערך מסוג T |
המערך on_true
והמערך on_false
חייבים להיות באותו צורה. זהו גם הצורה של מערך הפלט. למערך pred
צריכה להיות אותה מימדיות כמו של on_true
ו-on_false
, עם סוג הרכיב PRED
.
לכל רכיב P
של pred
, הרכיב התואם של מערך הפלט נלקח מ-on_true
אם הערך של P
הוא true
, ומ-on_false
אם הערך של P
הוא false
. כצורה מוגבלת של שידור, הערך של pred
יכול להיות סקלאר מסוג PRED
. במקרה כזה, מערך הפלט נלקח כולו מ-on_true
אם הערך של pred
הוא true
, ומ-on_false
אם הערך של pred
הוא false
.
דוגמה עם pred
שאינו סקלר:
let pred: PRED[4] = {true, false, false, true};
let v1: s32[4] = {1, 2, 3, 4};
let v2: s32[4] = {100, 200, 300, 400};
==>
Select(pred, v1, v2) = s32[4]{1, 200, 300, 4};
דוגמה עם סקלר pred
:
let pred: PRED = true;
let v1: s32[4] = {1, 2, 3, 4};
let v2: s32[4] = {100, 200, 300, 400};
==>
Select(pred, v1, v2) = s32[4]{1, 2, 3, 4};
יש תמיכה בבחירות בין צמדי מידע. למטרה הזו, צמדי מפתחות נחשבים לסוגי סקלר. אם on_true
ו-on_false
הן קבוצות (שצריכות להיות באותו צורה), אז pred
צריכה להיות סקלר מסוג PRED
.
SelectAndScatter
למידע נוסף, ראו XlaBuilder::SelectAndScatter
.
אפשר להתייחס לפעולה הזו כפעולה מורכבת שמחשבת קודם את ReduceWindow
במערך operand
כדי לבחור רכיב מכל חלון, ואז מפזרת את מערך source
למערכי האינדקסים של הרכיבים שנבחרו כדי ליצור מערך פלט באותו צורה כמו מערך המשתנה. הפונקציה select
הבינארית משמשת לבחירת רכיב מכל חלון על ידי החלה שלו על כל חלון, ונקראת באמצעות המאפיין שווקטור האינדקס של הפרמטר הראשון קטן מבחינה לקסיקוגרפית מהווקטור האינדקס של הפרמטר השני. הפונקציה select
מחזירה את הערך true
אם הפרמטר הראשון נבחר, ומחזירה את הערך false
אם הפרמטר השני נבחר. הפונקציה חייבת להיות טרנזיטיבית (כלומר, אם select(a, b)
ו-select(b, c)
הם true
, אז select(a, c)
הוא גם true
) כדי שהאלמנט שנבחר לא יהיה תלוי בסדר האלמנטים שעברתם בחלון נתון.
הפונקציה scatter
מוחלת על כל אינדקס שנבחר במערך הפלט. יש שני פרמטרים סקלריים:
- הערך הנוכחי במדד שנבחר במערך הפלט
- ערך הפיזור מ-
source
שחלה על האינדקס שנבחר
היא משלבת את שני הפרמטרים ומחזירה ערך סקלרי שמשמש לעדכון הערך באינדקס שנבחר במערך הפלט. בשלב הראשון, כל האינדקסים של מערך הפלט מוגדרים ל-init_value
.
מערך הפלט הוא באותו צורה כמו מערך operand
, וערך המערך source
חייב להיות באותו צורה כמו התוצאה של החלת הפעולה ReduceWindow
על מערך operand
. אפשר להשתמש ב-SelectAndScatter
כדי להעביר חזרה (backpropagate) את ערכי המדרון של שכבת אגירה ברשת עצבית.
SelectAndScatter(operand, select, window_dimensions, window_strides,
padding, source, init_value, scatter)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך מסוג T שעליו החלונות מחליקים |
select |
XlaComputation |
חישוב בינארי מסוג T, T -> PRED , שיחול על כל הרכיבים בכל חלון. הפונקציה מחזירה את הערך true אם נבחר הפרמטר הראשון, ומחזירה את הערך false אם נבחר הפרמטר השני |
window_dimensions |
ArraySlice<int64> |
מערך של מספרים שלמים לערכי מאפייני החלון |
window_strides |
ArraySlice<int64> |
מערך מספרים שלמים לערכים של צעדים בחלון הזמן |
padding |
Padding |
סוג המרווח הפנימי לחלון (ריפוד::kSame או מרווח פנימי::kValid) |
source |
XlaOp |
מערך מסוג T עם הערכים לפיזור |
init_value |
XlaOp |
ערך סקלרי מסוג T עבור הערך הראשוני של מערך הפלט |
scatter |
XlaComputation |
חישוב בינארי מסוג T, T -> T , כדי להחיל כל רכיב מקור של פיזור על רכיב היעד שלו |
האיור הבא מציג דוגמאות לשימוש ב-SelectAndScatter
, כשהפונקציה select
מחשבת את הערך המקסימלי בין הפרמטרים שלה. שימו לב שכאשר יש חפיפה בין החלונות, כמו באיור (2) שבהמשך, אינדקס של המערך operand
עשוי להיבחר מספר פעמים על ידי חלונות שונים. בתרשים, האלמנט עם הערך 9 נבחר על ידי שני החלונות העליונים (כחול ואדום), ופונקציית החיבור הבינארי scatter
יוצרת את אלמנט הפלט עם הערך 8 (2 + 6).
סדר ההערכה של הפונקציה scatter
הוא שרירותי ועשוי להיות לא קבוע. לכן, הפונקציה scatter
לא צריכה להיות רגישת מדי לשיוך מחדש. פרטים נוספים זמינים בחלק של הדיון על שיוכים בהקשר של Reduce
.
שליחה
מידע נוסף זמין במאמר XlaBuilder::Send
.
Send(operand, channel_handle)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
הנתונים לשליחה (מערך מסוג T) |
channel_handle |
ChannelHandle |
מזהה ייחודי לכל צמד שליחה/אישור |
הפונקציה שולחת את נתוני המשתנה שהוגדרו להוראה Recv
במחשבה אחרת שמשתתפת באותו מזהה ערוץ. לא מחזירה נתונים.
בדומה לפעולה Recv
, ה-API של הלקוח ב-Send
מייצג תקשורת סנכרונית, והוא מפורק באופן פנימי לשתי הוראות HLO (Send
ו-SendDone
) כדי לאפשר העברות אסינכרוניות של נתונים. מידע נוסף זמין במאמרים HloInstruction::CreateSend
ו-HloInstruction::CreateSendDone
.
Send(HloInstruction operand, int64 channel_id)
הפקודה מפעילה העברה אסינכררונית של המשתנה למשאבים שהוקצו על ידי ההוראה Recv
עם אותו מזהה ערוץ. הפונקציה מחזירה הקשר, שמשמש את ההוראה הבאה מסוג SendDone
כדי להמתין להשלמת העברת הנתונים. ההקשר הוא צירוף של {operand (shape), request id (U32)}, וניתן להשתמש בו רק בהוראה SendDone
.
SendDone(HloInstruction context)
בהתאם להקשר שנוצר על ידי הוראה Send
, הפונקציה ממתינה להשלמת העברת הנתונים. ההוראה לא מחזירה נתונים.
תזמון ההוראות לערוצים
סדר הביצוע של 4 ההוראות לכל ערוץ (Recv
, RecvDone
,
Send
, SendDone
) הוא כפי שמפורט בהמשך.
Recv
מתרחש לפניSend
Send
מתרחש לפניRecvDone
Recv
מתרחש לפניRecvDone
Send
מתרחש לפניSendDone
כשמקודדי הקצה העורפי יוצרים לוח זמנים לינארי לכל חישוב שמתקשר באמצעות הוראות ערוץ, אסור שיהיו מחזורים בתוך החישובים. לדוגמה, כשמופיעים מתחת ללוחות הזמנים, הדבר עלול לגרום לקיפאון.
תבנית של תוכן דינמי
למידע נוסף, ראו XlaBuilder::Slice
.
חיתוך מחלץ מערך משנה ממערך הקלט. מערך המשנה הוא בעל דירוג זהה לקלט ומכיל את הערכים בתוך תיבה תוחמת בתוך מערך הקלט, שבה המאפיינים והמדדים של התיבה התוחמת ניתנים כארגומנטים לפעולה של החלק המפריד.
Slice(operand, start_indices, limit_indices, strides)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
מערך N-מימדי מסוג T |
start_indices |
ArraySlice<int64> |
רשימה של N מספרים שלמים שמכילה את אינדקסי ההתחלה של הפלחים בכל מאפיין. הערכים חייבים להיות גדולים מ-0 או שווים לו. |
limit_indices |
ArraySlice<int64> |
רשימה של N מספרים שלמים שמכילה את אינדקסי הסיום (לא כולל) של הפלחים לכל מאפיין. כל ערך צריך להיות שווה לערך start_indices המתאים של המאפיין או גדול ממנו, וקטן מגודל המאפיין או שווה לו. |
strides |
ArraySlice<int64> |
רשימה של N מספרים שלמים שמחליטים על צעד הקלט של הפלחים. הפלח בוחר כל רכיב strides[d] במאפיין d . |
דוגמה במאפיין אחד:
let a = {0.0, 1.0, 2.0, 3.0, 4.0}
Slice(a, {2}, {4}) produces:
{2.0, 3.0}
דוגמה דו-ממדית:
let b =
{ {0.0, 1.0, 2.0},
{3.0, 4.0, 5.0},
{6.0, 7.0, 8.0},
{9.0, 10.0, 11.0} }
Slice(b, {2, 1}, {4, 3}) produces:
{ { 7.0, 8.0},
{10.0, 11.0} }
מיון
מידע נוסף זמין במאמר XlaBuilder::Sort
.
Sort(operands, comparator, dimension, is_stable)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operands |
ArraySlice<XlaOp> |
האופרנדים למיון. |
comparator |
XlaComputation |
החישוב של המשווא להשוואה שבו צריך להשתמש. |
dimension |
int64 |
המאפיין שלפיהם יתבצע המיון. |
is_stable |
bool |
האם כדאי להשתמש במיון יציב. |
אם צוין רק אופרטנד אחד:
אם המשתנה המבצע הוא טינסור מסדר 1 (מערך), התוצאה היא מערך ממוין. אם רוצים למיין את המערך בסדר עולה, כלי ההשוואה צריך לבצע השוואה של 'פחות מ-'. באופן רשמי, אחרי שהמערך ממוין, הוא שומר את כל מיקומי האינדקס
i, j
עםi < j
שלcomparator(value[i], value[j]) = comparator(value[j], value[i]) = false
אוcomparator(value[i], value[j]) = true
.אם לאופרנד יש דירוג גבוה יותר, האופרנד ממוין לאורך המאפיין שצוין. לדוגמה, בטנסור מסדר 2 (מטריצה), ערך המאפיין
0
יסדר כל עמודה בנפרד, וערך המאפיין1
יסדר כל שורה בנפרד. אם לא ציינתם מספר מאפיין, המאפיין האחרון נבחר כברירת מחדל. למאפיין שממוין, חל אותו סדר מיון כמו במקרה של דירוג ברמה 1.
אם מציינים אופרטורים של n > 1
:
כל האופרנדים מסוג
n
חייבים להיות טנזורים עם אותם מימדים. סוגי הרכיבים של הטנסורים עשויים להיות שונים.כל המשתנים המבצעיים ממוינים יחד, ולא בנפרד. באופן עקרוני, לאופרנדים מתייחסים כאל צינור. כשבודקים אם צריך להחליף את הרכיבים של כל אופרנד במיקומי האינדקס
i
ו-j
, מתבצעת קריאה למבצע ההשוואה עם2 * n
פרמטרים סקלריים, כאשר הפרמטר2 * k
תואם לערך במיקוםi
מהאופרטורk-th
, והפרמטר2 * k + 1
תואם לערך במיקוםj
מהאופרטורk-th
. בדרך כלל, כלי ההשוואה ישווה את הפרמטרים2 * k
ו-2 * k + 1
זה לזה, ואולי ישתמש בצמדי פרמטרים אחרים בתור מפרי שוויון.התוצאה היא קבוצת ערכים (tuple) שמכילה את אופרטנדים בסדר ממוין (לפי המאפיין שצוין, כפי שמתואר למעלה). המשתנה
i-th
של הטופל תואם למשתנהi-th
של Sort.
לדוגמה, אם יש שלושה אופרטנדים operand0 = [3, 1]
, operand1 = [42, 50]
ו-operand2 = [-3.0, 1.1]
, והמתבצע משווה רק את הערכים של operand0
עם פחות מ-, הפלט של המיון הוא הטופל ([1, 3], [50, 42], [1.1, -3.0])
.
אם הערך של is_stable
מוגדר כ-true, אפשר להבטיח שהמיון יהיה יציב. כלומר, אם יש אלמנטים שנחשבים זהים על ידי המבצע, הסדר היחסי של הערכים הזהים נשמר. שני אלמנטים e1
ו-e2
שווים רק אם comparator(e1, e2) = comparator(e2, e1) = false
. כברירת מחדל, הערך של is_stable
מוגדר כ-false.
טרנספוזיציה
אפשר לעיין גם בפעולה tf.reshape
.
Transpose(operand)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
operand |
XlaOp |
המשתנה שרוצים לבצע לו טרנספוזיציה. |
permutation |
ArraySlice<int64> |
איך להשתיק את המאפיינים. |
הפונקציה מבצעת תמורה של המאפיינים של המשתנה לפי התמורה שצוינה, כך ש-∀ i . 0 ≤ i < rank ⇒ input_dimensions[permutation[i]] = output_dimensions[i]
.
הפונקציה הזו זהה לפונקציה Reshape(operand, permutation, Permute(permutation, operand.shape.dimensions)).
TriangularSolve
למידע נוסף, ראו XlaBuilder::TriangularSolve
.
פתרון מערכות של משוואות ליניאריות עם מטריצות של ערכים טריגונומטרים עליונים או תחתונים באמצעות החלפה קדימה או אחורה. באמצעות שידור (broadcasting) לפי המאפיינים המובילים, התכנית הזו פותרת אחת ממערכות המטריצות op(a) * x =
b
או x * op(a) = b
עבור המשתנה x
, בהתאם ל-a
ו-b
, כאשר op(a)
הוא op(a) = a
, או op(a) = Transpose(a)
, או op(a) = Conj(Transpose(a))
.
TriangularSolve(a, b, left_side, lower, unit_diagonal, transpose_a)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
a |
XlaOp |
מערך דירוג > 2 מסוג מרוכב או מסוג נקודה צפה (floating-point) עם הצורה [..., M, M] . |
b |
XlaOp |
מערך דירוג > 2 מאותו סוג עם הצורה [..., M, K] אם left_side נכון, [..., K, M] אחרת. |
left_side |
bool |
מציין אם לפתור מערכת בצורה op(a) * x = b (true ) או x * op(a) = b (false ). |
lower |
bool |
האם להשתמש במשולש העליון או התחתון של a . |
unit_diagonal |
bool |
אם true , ההנחה היא שהרכיבים האלכסוןים של a הם 1 ואין גישה אליהם. |
transpose_a |
Transpose |
אם להשתמש ב-a כפי שהוא, לבצע בו טרנספוזיציה או לבצע טרנספוזיציה של הקונגרואנט שלו. |
נתוני הקלט נקראים רק מהמשולש התחתון/העליון של a
, בהתאם לערך של lower
. המערכת מתעלמת מהערכים מהמשולש השני. נתוני הפלט מוחזרים באותו משולש. הערכים במשולש השני מוגדרים בהטמעה ויכולים להיות כל ערך שהוא.
אם הדרגה של a
ו-b
גדולה מ-2, הן מטופלות כקבוצות של מטריצות, שבהן כל המאפיינים מלבד 2 המאפיינים המשניים הם מאפייני קבוצה. המידות של a
ו-b
צריכות להיות זהות.
Tuple
מידע נוסף זמין במאמר XlaBuilder::Tuple
.
קבוצת ערכים (tuple) שמכילה מספר משתנה של מזהים של נתונים, לכל אחד מהם יש צורה משלו.
הדבר מקביל ל-std::tuple
ב-C++. מבחינה רעיונית:
let v: f32[10] = f32[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
let s: s32 = 5;
let t: (f32[10], s32) = tuple(v, s);
אפשר לפרק צמדי-ערכי-מפתח (ולגשת אליהם) באמצעות הפעולה GetTupleElement
.
אף שהפונקציה
מידע נוסף זמין במאמר XlaBuilder::While
.
While(condition, body, init)
ארגומנטים | סוג | סמנטיקה |
---|---|---|
condition |
XlaComputation |
XlaComputation מסוג T -> PRED שמגדיר את תנאי הסיום של הלוופ. |
body |
XlaComputation |
XlaComputation מסוג T -> T שמגדיר את גוף הלולאה. |
init |
T |
הערך הראשוני של הפרמטר של condition ו-body . |
הפונקציה מבצעת את body
ברצף עד שה-condition
נכשל. הביטוי הזה דומה ל-while loop טיפוסי בשפות רבות אחרות, מלבד ההבדלים וההגבלות שמפורטים בהמשך.
- צומת
While
מחזיר ערך מסוגT
, שהוא התוצאה מההפעלה האחרונה שלbody
. - הצורה של הסוג
T
נקבעת באופן סטטי, והיא חייבת להיות זהה בכל האיטרציות.
הפרמטרים T של החישובים מופעלים עם הערך של init
בחזרה הראשונה, ומעודכנים באופן אוטומטי לתוצאה החדשה מ-body
בכל חזרה חוזרת.
אחד התרחיש לדוגמה העיקרי של הצומת While
הוא להטמיע ביצוע חוזר של אימון ברשתות נוירונים. בהמשך מוצג קוד מדומה פשוט עם תרשים שמייצג את החישוב. הקוד מופיע ב-while_test.cc
.
הסוג T
בדוגמה הזו הוא Tuple
שמורכב מ-int32
למספר החזרות ומ-vector[10]
למצטבר. במשך 1,000 חזרות, הלולאה ממשיכה להוסיף וקטור קבוע למצטבר.
// Pseudocode for the computation.
init = {0, zero_vector[10]} // Tuple of int32 and float[10].
result = init;
while (result(0) < 1000) {
iteration = result(0) + 1;
new_vector = result(1) + constant_vector[10];
result = {iteration, new_vector};
}