इंडेक्स करने का विश्लेषण

इस दस्तावेज़ में, एचएलओ के इंडेक्स होने के विश्लेषण के बारे में जानकारी दी गई है. इसकी मदद से, एचएलओ ऑपरेशन के लिए इंडेक्स किए जाने वाले मैप का सिंबल तरीके से पता लगाया जा सकता है. इंडेक्स करने वाला मैप एक ऐसा फ़ंक्शन है जो एक टेंसर के इंडेक्स को दूसरे की इंडेक्स से मैप करता है. जैसे, एचएलओ निर्देश के आउटपुट के इंडेक्स को एचएलओ निर्देश के इंडेक्स से मैप करना या इसी तरह के इंडेक्स को एचएलओ निर्देश के इंडेक्स से मैप करना होता है.

उदाहरण

tensor<20xf32> से tensor<10x20x30xf32> तक ब्रॉडकास्ट के लिए

p0 = f32[20] parameter(0)
bc0 = f32[10, 20, 30] broadcast(p0), dimensions={1}

आउटपुट से इनपुट तक पहुंचने के लिए इंडेक्स किया जाने वाला मैप \((i, j, k) \mapsto (j)\) $i \in [0, 10]\(, \)j \in [0, 20]\( and \)k \in [0, 30]$ है.

वजह

XLA जीपीयू, कोलेसिंग, ऑपरेंड के इस्तेमाल, और टाइलिंग स्कीम के बारे में जानने के लिए, ज़रूरत के हिसाब से कई समाधान इस्तेमाल करता है (ज़्यादा जानकारी नीचे दी गई है). इंडेक्स करने के विश्लेषण का मकसद, इस तरह के इस्तेमाल के लिए, एक ऐसा कॉम्पोनेंट उपलब्ध कराना होता है जिसका फिर से इस्तेमाल किया जा सके. इंडेक्स करने का विश्लेषण, एमएलआईआर के अफ़ीन मैप इन्फ़्रास्ट्रक्चर पर बनाया गया है और इसमें एचएलओ के सिमेंटिक्स जोड़े गए हैं.

कोलेसिंग

सामान्य मामलों में, मेमोरी कोलेस्किंग के बारे में जानकारी देना मुमकिन हो जाता है. ऐसा तब होता है, जब हमें पता हो कि आउटपुट के एलिमेंट का पता लगाने के लिए, इनपुट के कौनसे एलिमेंट/स्लाइस पढ़े गए हैं.

ऑरेंज यूटिलाइज़ेशन

XLA में ऑरेंज का इस्तेमाल शुरू करने से यह पता चलता है कि निर्देश के हर इनपुट का कितना इस्तेमाल किया गया है. यह मानते हुए कि इसका आउटपुट पूरी तरह से इस्तेमाल किया जा रहा है. फ़िलहाल, इस्तेमाल की गणना किसी सामान्य मामले के लिए भी नहीं की जाती है. इंडेक्स करने की प्रोसेस का विश्लेषण करने पर, इस्तेमाल की सटीक जानकारी मिलती है.

टाइलिंग

टाइल/स्लाइस, एक टेंसर का हाइपर-रेक्टैंग्युलर सबसेट होता है. इसे ऑफ़सेट, साइज़, और स्ट्राइड के आधार पर पैरामीटर में बांटा जाता है. टाइल को फैलाना, ऑप के टाइलिंग पैरामीटर का इस्तेमाल करके, ऑप के निर्माता/उपभोक्ता के टाइल पैरामीटर का पता लगाने का एक तरीका है. पहले से ही एक लाइब्रेरी मौजूद है, जो सॉफ़्टमैक्स और डॉट के लिए ऐसा करती है. टाइल के प्रसार को अगर इंडेक्स मैप में दिखाया जाए, तो उसे और सामान्य और बेहतर बनाया जा सकता है.

फ़ंक्शन और डोमेन

इंडेक्स करने वाला मैप एक ऐसा फ़ंक्शन है \(\boldsymbol{f}(\boldsymbol{d}, \boldsymbol{s})\) जो टेंसर के मल्टी-इंडेक्स \(\boldsymbol{d}\) को टेंसर के एलिमेंट/रेंज \(B\)से मैप करता है. \(A\) पैरामीटर \(\boldsymbol{s}\) , उन डाइमेंशन के इंडेक्स की रेंज के बारे में बताता है जो टेन्सर में मौजूद होते हैं \(B\), लेकिन टेंसर में नहीं \(A\).

उदाहरण के लिए, अगर tensor<2x4x8x16xf32> से tensor<4x8xf32> को कम किया जाता है, तो 2D आउटपुट से 4D इनपुट तक का इंडेक्स मैप\((d_0, d_1) \mapsto (s_0, d_0, d_1, s_1)\)होगा. इसमें \(d_i\) , आउटपुट टेंसर के इंडेक्स से जुड़े डाइमेंशन पैरामीटर होते हैं. पैरामीटर \(s_j\) एक से ज़्यादा वैल्यू को कोड में बदलते हैं, जैसे कि आउटपुट के \((d_0, d_1)\) एलिमेंट की गिनती करने के लिए, हमें इनपुट के \((s_0, d_0, d_1, s_1)\) एलिमेंट की ज़रूरत होती है, जहां \(s_0 \in [0, 2)\) और \(s_1 \in [0, 16)\).

यह मैपिंग एचएलओ के निर्देशों के एट्रिब्यूट से बनाई जा सकती है या फ़्यूज़न के लिए इंडेक्स करने के लिए, गलत निर्देशों की मैपिंग की जा सकती है. मैपिंग में एक डोमेन भी होता है, जो यह बताता है कि टेंसर के किन एलिमेंट में मैपिंग मौजूद है.

\[ \begin{eqnarray} \boldsymbol{f}(\boldsymbol{d}, \boldsymbol{s})\; &s.t.& \\ \boldsymbol{lb}_d &\leq& \boldsymbol{d} \leq \boldsymbol{ub}_d \\ \boldsymbol{lb}_s &\leq& \boldsymbol{s} \leq \boldsymbol{ub}_s \\ \boldsymbol{lb}_g &\leq& \boldsymbol{g}(\boldsymbol{d}, \boldsymbol{s}) \leq \boldsymbol{ub}_g \end{eqnarray} \]

हम कंप्यूटेशन को कम करना चाहते हैं, इसलिए सिंबॉलिक कंप्यूटेशन के लिए हमें एक लाइब्रेरी की ज़रूरत है. XLA पहले से ही MLIR पर निर्भर है, इसलिए हम अंकगणितीय लाइब्रेरी लिखने के बजाय mlir::AffineMap का इस्तेमाल करते हैं.

सामान्य AffineMap ऐसा दिखता है

(d0)[s0, s1] -> (s0 + 5, d0 * 2, s1 * 3 + 50)

AffineMap में दो तरह के पैरामीटर आसानी से इस्तेमाल किए जा सकते हैं: डाइमेंशन और प्रतीक जिनके लिए हम \(\boldsymbol d\) और \(\boldsymbol s\) क्रम से इस्तेमाल कर सकते हैं. AffineMap में डाइमेंशन की रेंज के बारे में कोई मेटाडेटा नहीं होता. इसलिए, हमें यह डेटा खुद उपलब्ध कराना होगा.

struct Range {
 int64_t lower_bound;
 int64_t upper_bound;
};

struct IndexingMap {
 mlir::AffineMap affine_map;
 std::vector<Range> dimension_ranges;
 std::vector<Range> symbol_ranges;
 llvm::DenseMap<mlir::AffineExpr, Range> expr_ranges;
};

dim_ranges, इंडेक्स करने वाले मैप के डाइमेंशन पैरामीटर के लिए, इनक्लूसिव बॉक्स कंस्ट्रेंट को कोड में बदलता है. यह आम तौर पर, ट्रांसपोज़, रिडक्शन, एलिमेंटवाइज़, डॉट जैसे ऑपरेटर के लिए आउटपुट टेंसर के आकार से मेल खाता है. हालांकि, HloConcatenateInstruction जैसे कुछ अपवाद भी होते हैं. \(\boldsymbol{d}\)

symbol_ranges ऐसी वैल्यू को कोड में बदलता है जिन्हें \(\boldsymbol {s}\) पैरामीटर इस्तेमाल कर सकते हैं.

आइए, उदाहरण के आधार पर यह समझें कि ऊपर दिए गए सभी उदाहरणों का असल में क्या मतलब है.

अनफ़्यूज्ड ऑपरेशन के लिए Maps को इंडेक्स करना

एलिमेंट के हिसाब से

एलिमेंट के हिसाब से ऑपरेशन के लिए, इंडेक्स करने वाला मैप एक पहचान होता है.

  p0 = f32[10, 20] parameter(0)
  p1 = f32[10, 20] parameter(1)
  add = f32[10, 20] add(p0, p1)

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट ->input_0: \((d_0, d_1) \mapsto (d_0, d_1)\) $\boldसिंबोल{d} \in [0,9] \times [0, 19]\(, i.e. \)\boldसिंबोल{d} \in {\rm Dom}(आउटपुट)$ के लिए
  • आउटपुट ->input_1: \((d_0, d_1) \mapsto (d_0, d_1)\) $\boldसिंबोल{d} \in {\rm Dom} (आउटपुट)$ के लिए

आउटपुट मैप के लिए इनपुट

  • इनपुट_i -> आउटपुट: \((d_0, d_1) \mapsto (d_0, d_1)\) $\boldसिंबोल{d} \in {\rm Dom}(आउटपुट)$ के लिए

ब्रॉडकास्ट करना

ब्रॉडकास्ट करने का मतलब है कि इनपुट के लिए इनपुट को मैप करते समय, कुछ डाइमेंशन हटा दिए जाएंगे. साथ ही, इनपुट को आउटपुट में मैप करते समय, कुछ डाइमेंशन को जोड़ दिया जाएगा.

p0 = f32[20] parameter(0)
bc0 = f32[10, 20, 30] broadcast(p0), dimensions={1}

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: \((d_0, d_1, d_2) \mapsto (d_1)\) $\boldसिंबोल{d} के लिए \in {\rm Dom}(आउटपुट)$

आउटपुट मैप का इनपुट

  • इनपुट -> आउटपुट: \((d_0) \mapsto (s_0, d_1, s_1)\) $\boldसिंबोल{d} \in {\rm Dom}(आउटपुट)\( and \)\boldसिंबल{s} \in [0, 9] \times [0, 29]$ के लिए.

ध्यान दें कि अब इनपुट-टू-आउटपुट मैपिंग के लिए, \(\boldsymbol s\) दाईं ओर मौजूद है. ये सिंबल, वैल्यू की रेंज दिखाते हैं. उदाहरण के लिए, इस मामले में, इंडेक्स वाले इनपुट के हर एलिमेंट \(d_0\) को आउटपुट के 10x1x30 स्लाइस के साथ मैप किया जाता है.

कॉन्सटेंट और आयोटा

आसानी से, इनमें कोई इनपुट पैरामीटर नहीं होता. इसलिए, इंडेक्स करने की कोई प्रोसेस नहीं होती.

ट्रांसपोज़ करें

ट्रांसपोज़ करने के लिए इंडेक्स करने वाला मैप, इनपुट/आउटपुट डाइमेंशन का क्रमचय होता है.

p0 = f32[3, 12288, 6, 128] parameter(0)
transpose = f32[3, 6, 128, 12288] transpose(p0), dimensions={0, 2, 3, 1}

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: \((d_0, d_1, d_2, d_3) \mapsto (d_0, d_3, d_1, d_2)\) \(\boldsymbol{d} \in {\rm Dom}(output)\)के लिए

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट:\(\boldsymbol{d} \in {\rm Dom}(input)\) के लिए \((d_0, d_1, d_2, d_3) \mapsto (d_0, d_2, d_3, d_1)\)

रिवर्स डंक

रिवर्स मोड के लिए मैप को इंडेक्स करने पर, वापस लाए गए डाइमेंशन $upper_bound(d_i) - d_i$ में बदल जाते हैं:

p0 = f32[1, 17, 9, 9] parameter(0)
reverse = f32[1, 17, 9, 9] reverse(p0), dimensions={1, 2}

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: $(d_0, d_1, d_2, d_3) \mapsto (d_0, -d_1 + 16, -d_2 + 8, d_3)\( for \)\boldसिंबोल{d} \in {\rm Dom}(आउटपुट)$

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट: $(d_0, d_1, d_2, d_3) \mapsto (d_0, -d_1 + 16, -d_2 + 8, d_3)\( for \)\boldसिंबोल{d} \in {\rm Dom}(इनपुट)$

(वेरियाडिक) कम करना

वैरिएडिक रिडक्शन में कई इनपुट और कई इनिट होते हैं. आउटपुट से इनपुट तक का मैप, कम डाइमेंशन को जोड़ता है. इसलिए, यह कुछ हद तक ब्रॉडकास्ट के बिलकुल उलट काम करता है.

p0 = f32[256,10] parameter(0)
p0_init = f32[] constant(-inf)
p1 = s32[256,10] parameter(1)
p1_init = s32[] constant(0)
reduce = (f32[10], s32[10]) reduce(p0, p1, p0_init, p1_init),
  dimensions={0}, to_apply=min

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट ->input_j: \((d_0) \mapsto (s_0, d_0)\) के लिए $\boldsimbol{d} \in {\rm Dom}(आउटपुट)\( and \)\boldsignbol{s} \in [0, 9]$
  • आउटपुट -> init_j: \((d_0) \mapsto ()\) $\boldsignbol{d} \in {\rm Dom}(आउटपुट)$ के लिए

आउटपुट मैप के लिए इनपुट:

  • तो इनपुट_i ->input_j: \((d_0, d_1) \mapsto (d_1)\) $\boldसिंबोल{d} के लिए \in {\rm Dom}(इनपुट)$
  • init_i ->product_j: \(() \mapsto (s_0)\) के लिए \(\boldsymbol{s} \in [0, 9]\)

\(i, j = 0, \ldots, INPUT\\_COUNT\)के लिए.

स्लाइस

स्लाइस के नतीजों के लिए आउटपुट से इनपुट को इंडेक्स करने पर, स्ट्रिंग किए गए इंडेक्स करने का मैप दिखता है. यह मैप, आउटपुट के हर एलिमेंट के लिए मान्य होता है. इनपुट से आउटपुट तक मैप करना, इनपुट में एलिमेंट की स्ट्रिंग की रेंज तक सीमित होता है.

p0 = f32[10, 20, 50] parameter(0)
slice = f32[5, 3, 25] slice(f32[10, 20, 50] p0),
  slice={[5:10:1], [3:20:7], [0:50:2]}

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: \((d_0, d_1, d_2) \mapsto (d_0 + 5, 7d_1 + 3, 2d_2)\) \(\boldsymbol{d} \in {\rm Dom}(output)\)के लिए

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट: \((d_0, d_1, d_2) \mapsto (d_0, d_1 / 7, d_2 / 2)\) के लिए \(\boldsymbol{d} \in [5, 9] \times [3, 19] \times [0, 49]\) स्ट्रेड $[1, 7, 2]$.

TBD: इनपुट-टू-आउटपुट इंडेक्स

आकार बदलें

आकार अलग-अलग फ़्लेवर में आते हैं.

आकार छोटा करें

यह N-D से 1D तक एक "लीनियरिंग" नया आकार है.

p0 = f32[4,8] parameter(0)
reshape = f32[32] reshape(p0)

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: \((d_0) \mapsto (d_0 / 8, d_0 \mod 8)\) $\boldसिंबोल{d} के लिए \in {\rm Dom}(आउटपुट)$

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट: \((d_0, d_1) \mapsto (8 d_0 + d_1)\) $\boldसिंबोल{d} \in {\rm Dom}(input)$ के लिए.

आकार को बड़ा करें

यह एक उलटा "छोटा आकार" ऑप है. यह 1D इनपुट को N-D आउटपुट में नया आकार देता है.

p0 = f32[32] parameter(0)
reshape = f32[4, 8] reshape(p0)

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: \((d_0, d_1) \mapsto (8 d_0 + d_1)\) $\boldसिंबोल{d} के लिए \in {\rm Dom}(आउटपुट)$

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट: \((d_0) \mapsto (d_0 / 8, d_0 \mod 8)\) $\boldसिंबोल{d} के लिए \in {\rm Dom}(input)$.

सामान्य रीशेप

ये आकार बदलने वाले ऐसे ऑपरेशन हैं जिन्हें एक बार बड़ा या छोटा करने के आकार के तौर पर नहीं दिखाया जा सकता. उन्हें सिर्फ़ दो या उससे ज़्यादा आकृतियों को जोड़कर दिखाया जा सकता है उन्हें बड़ा या छोटा किया जा सकता है.

उदाहरण 1: लीनियराइज़ेशन-डिलाइनराइज़ेशन.
p0 = f32[4,8] parameter(0)
reshape = f32[2, 4, 4] reshape(p0)

इस नए आकार को दिखाने के लिए, आकार को छोटा करके tensor<4x8xf32> से tensor<32xf32> किया जा सकता है. इसके बाद, आकार को बड़ा करके tensor<2x4x4xf32> बनाया जा सकता है.

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: $(d_0, d_1, d_2) \mapsto (2d_0 + (4d_1 + d_2) / 8, 4d_1 + d_2) \mod 8)$

\(\boldsymbol{d} \in {\rm Dom}(output)\)के लिए

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट: $(d_0, d_1) \mapsto ((8d_0 + d_1) / 16, ((8d_0 + d_1) \mod 16) / 4, d_1 \mod 4)$

\(\boldsymbol{d} \in {\rm Dom}(input)\)के लिए.

उदाहरण 2: बड़े और छोटे किए गए सबशेप
p0 = f32[4, 8, 12] parameter(0)
reshape = f32[32, 3, 4] reshape(p0)

इस नए आकार को दो रूपों की संरचना के रूप में पेश किया जा सकता है. पहला वाला सबसे बाहरी डाइमेंशन tensor<4x8x12xf32> को छोटा करके tensor<32x12xf32> कर रहा है. दूसरा, सबसे अंदरूनी डाइमेंशन tensor<32x12xf32> को tensor<32x3x4xf32> में बड़ा कर देता है.

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट: \((d_0, d_1, d_2) \mapsto (d_0 / 8, d_0 \mod 8, 4d_1 + d_2)\) इसके लिए \(\boldsymbol{d} \in {\rm Dom}(output)\)

आउटपुट मैप के लिए इनपुट:

  • इनपुट -> आउटपुट: \((d_0, d_1, d_2) \mapsto (8d_0 + d_1, d_2 / 4, d_2 \mod 4)\) के लिए \(\boldsymbol{d} \in {\rm Dom}(input)\).

बिटकास्ट

बिटकास्ट ऑपर्च्यूनिटी को ट्रांसपोज़-रीशे-ट्रांसपोज़ की वजह से दिखाया जा सकता है. इसलिए, इसके इंडेक्स होने वाले मैप, इस क्रम के लिए सिर्फ़ इंडेक्स किए जाने वाले मैप का एक हिस्सा होते हैं.

स्ट्रिंग जोड़ें

Concat के लिए आउटपुट-टू-इनपुट मैपिंग की सुविधा, सभी इनपुट के लिए तय की जाती है. हालांकि, नॉन-ओवरलैपिंग डोमेन में, यानी एक बार में सिर्फ़ एक इनपुट का इस्तेमाल किया जाएगा.

p0 = f32[3,50] parameter(0)
p1 = f32[3,30] parameter(1)
concat = f32[3,80] concatenate(f32[3,50] p0, f32[3,30] p1),
  dimensions={1}

मैप इनपुट करने के लिए आउटपुट:

  • आउटपुट -> इनपुट 1:

\(\boldsymbol{d} \in [0, 2] \times [0, 49]\)के लिए\((d_0, d_1) \mapsto (d_0, d_1)\)

  • आउटपुट -> इनपुट 2:

[0, 2] \tाइम [50, 79]$ में $\boldसिंबोल{d} के लिए\((d_0, d_1) \mapsto (d_0, d_1 - 50)\)

आउटपुट मैप के इनपुट:

  • इनपुट 1 -> आउटपुट: \((d_0, d_1) \mapsto (d_0, d_1)\) $\boldसिंबोल{d} \in {\rm Dom}(input_1)$ के लिए.
  • इनपुट 2 -> आउटपुट: \((d_0, d_1) \mapsto (d_0, d_1 + 50)\) $\boldसिंबोल{d} के लिए \in {\rm Dom}(input_2)$.

डॉट (आउटपुट-टू-इनपुट लागू किया गया

डॉट के लिए इंडेक्स करने के मैप, रिडक्शन के मैप से काफ़ी मिलते-जुलते हैं.

p0 = f32[4, 128, 256] parameter(0)
p1 = f32[4, 256, 64] parameter(1)
dot = f32[4, 128, 64] dot(p0, p1),
  lhs_batch_dims={0}, rhs_batch_dims={0},
  lhs_contracting_dims={2}, rhs_contracting_dims={1}

इनपुट मैप का आउटपुट:

  • आउटपुट ->input_1: \((d_0, d_1, d_2) \mapsto (d_0, d_1, s_0)\) के लिए \(\boldsymbol{d} \in {\rm Dom}(output)\) और \(\boldsymbol{s} \in [0, 255]\)
  • आउटपुट ->input_2: \((d_0, d_1, d_2) \mapsto (d_0, s_0, d_2)\) के लिए \(\boldsymbol{d} \in {\rm Dom}(output)\) और \(\boldsymbol{s} \in [0, 255]\)

आउटपुट मैप के इनपुट:

  • इनपुट_1 -> आउटपुट:\(\boldsymbol{d} \in {\rm Dom}(input_1)\) और \(\boldsymbol{s} \in [0, 63]\)के लिए \((d_0, d_1, d_2) \mapsto (d_0, d_1, s_0)\)
  • इनपुट_2 -> आउटपुट: \((d_0, d_1, d_2) \mapsto (d_0, s_0, d_1)\) \(\boldsymbol{d} \in {\rm Dom}(input_2)\) और \(\boldsymbol{s} \in [0, 127]\)के लिए

विंडो कम करें (टीबीडी)

पैड (टीबीडी)

फ़्यूज़न के लिए मैप को इंडेक्स करना

फ़्यूज़न ऑप के लिए इंडेक्स करने वाला मैप, क्लस्टर में मौजूद हर ऑप के लिए इंडेक्स किए जाने वाले मैप का एक कंपोज़िशन है. ऐसा हो सकता है कि कुछ इनपुट अलग-अलग ऐक्सेस पैटर्न के साथ कई बार पढ़े जाते हैं.

एक इनपुट, इंडेक्स करने वाले कई मैप

यहां \(p_0 + p_0^T\)के लिए एक उदाहरण दिया गया है

f {
  p0 = f32[1000, 1000] parameter(0)
  transpose_p0 = f32[1000, 1000]{0, 1} transpose(p0), dimensions={1, 0}
  ROOT a0 = f32[1000, 1000] add(p0, transpose_p0)
}

p0 के लिए आउटपुट-टू-इनपुट इंंडेक्सिंग मैप $(d_0, d_1) \mapsto (d_0, d_1)\( and \)(d_0, d_1) \mapsto (d_1, d_0)$ होगा. इसका मतलब है कि आउटपुट के एक एलिमेंट की गणना करने के लिए, हमें इनपुट पैरामीटर को दो बार पढ़ना पड़ सकता है.

एक इनपुट, डुप्लीकेट इंडेक्स को हटाने की जानकारी

img

कुछ मामलों में, इंडेक्स करने वाले मैप असल में एक जैसे होते हैं, भले ही यह तुरंत साफ़ तौर पर पता न चले.

f {
  p0 = f32[20, 10, 50] parameter(0)
  lhs_transpose_1 = f32[10, 20, 50] transpose(p0), dimensions={1, 0, 2}
  lhs_e = f32[10, 20, 50] exponential(lhs_transpose_1)
  lhs_transpose_2 = f32[10, 50, 20] transpose(lhs_e), dimensions={0, 2, 1}
  rhs_transpose_1 = f32[50, 10, 20] transpose(p0), dimensions={2, 1, 0}
  rhs_log = f32[50, 10, 20] exponential(rhs_transpose_1)
  rhs_transpose_2 = f32[10, 50, 20] transpose(rhs_log), dimensions={1, 0, 2}
  ROOT add = f32[10, 50, 20] add(lhs_transpose_2, rhs_transpose_2)
}

इस मामले में, p0 के लिए आउटपुट-टू-इनपुट इंडेक्स करने वाला मैप सिर्फ़ $(d_0, d_1, d_2) \mapsto (d_2, d_0, d_1)$ है.

सॉफ़्टमैक्स

img

सॉफ़्टमैक्स के लिए parameter 0 के लिए आउटपुट-टू-इनपुट इंडेक्स करने वाले मैप:

  • \((d_0, d_1, d_2) \mapsto (d_0, d_1, d_2)\)
  • \((d_0, d_1, d_2)[s_0] \mapsto (d_0, d_1, s_0)\)

\(\boldsymbol{d} \in {\rm Dom}(output)\) और \(\boldsymbol{s} \in [0, 124]\) इनपुट के सबसे अंदरूनी डाइमेंशन को दिखाता है.

मैप को इंडेक्स करने की प्रोसेस को आसान बनाने की सुविधा

mlir::AffineMap अपस्ट्रीम के लिए डिफ़ॉल्ट सिंप्लफ़ायर, डाइमेंशन/सिंबल की रेंज के बारे में कोई अनुमान नहीं लगा सकता. इसलिए, यह mod और div के साथ, एक्सप्रेशन को आसान नहीं बना सकता.

हम अफ़िलिएट मैप में सब-एक्सप्रेशन के लोअर और अपर बाउंड के बारे में जानकारी का इस्तेमाल करके, उन्हें और आसान बना सकते हैं.

सिंप्लफ़ायर इन एक्सप्रेशन को फिर से लिख सकता है.

  1. [0, 6] \time [0, 14]\( becomes \)(d_0, d_1) \mapsto (d_0, d_1)$ में $\boldसिंबोल{d} के लिए\((d_0, d_1) \mapsto (d_0 + d1 / 16, d1 \mod 16)\)
  2. $(d_0, d_1, d_2) \mapsto ((100d_0 + 10d_1 + d_2) /100, ((100d_0 + 10d_1 + d_2) \mod 100) / 10, d_2 \mod 10 / d_1, d_1\( for \)\( becomes \)
  3. $(d_0, d_1, d_2) \mapsto ((16d_0 + 4d_1 + d_2) /8, (16d_0 + 4d_1 + d_2) \mod 8)\( for \)d_i \in [0, 9]\\( becomes \)(d_0, d_1, d_1, d_2) d_2
  4. \((d_0, d_1) \mapsto (-(-11d_0 - d_1 + 109) / 11 + 9)\) $\boldsignbol{d} \ में [0, 9] \times [0, 10]\( becomes \)(d_0, d_1) \mapsto (d_0)$.

मैप को इंडेक्स करने को आसान बनाने की सुविधा से, हमें यह समझने में मदद मिलती है कि एचएलओ में चेन वाले कुछ साइज़ एक-दूसरे को रद्द कर देते हैं.

p0 = f32[10, 10, 10] parameter(0)
reshape1 = f32[50, 20] reshape(p0)
reshape2 = f32[10, 10, 10] reshape(reshape1)

इंडेक्स करने के मैप बनाने और उनके सरल होने के बाद, हम

\((d_0, d_1, d_2) \mapsto (d_0, d_1, d_2)\).