共用方式為


使用步伐來表示填充和記憶體佈局

DirectML 張量由 Direct3D 12 緩衝區支援,並以稱為張量的 大小步幅 的屬性來描述。 張量 的大小 描述張量邏輯維度。 例如,2D 張量的高度可能為2,寬度為3。 邏輯上,張量有 6 個不同的元素,不過大小不會指定這些元素儲存在記憶體中的方式。 張量的 步幅 描述張量元素的物理記憶體配置。

二維 (2D) 陣列

請考慮一個具有高度 2 且寬度 3 的 2D 張量,其中數據由字符組成。 在 C/C++中,這可能使用多維度陣列來表示。

constexpr int rows = 2;
constexpr int columns = 3;
char tensor[rows][columns];
tensor[0][0] = 'A';
tensor[0][1] = 'B';
tensor[0][2] = 'C';
tensor[1][0] = 'D';
tensor[1][1] = 'E';
tensor[1][2] = 'F';

上述張量的邏輯視圖如下所示。

A B C
D E F

在 C/C++中,多維度陣列會以數據列主要順序儲存。 換句話說,沿著寬度維度的連續元素會連續儲存在線性記憶體空間中。

抵消: 0 1 2 3 4 5
值: 一個 B C D E F

維度的 步幅 是要跳過的元素數目,以便存取該維度中的下一個元素。 Strides 表示記憶體中張量的配置。 以列優先的順序,寬度維度的步幅一律為 1,因為沿著維度的相鄰元素會連續儲存。 高度維度的步幅取決於寬度維度的大小;在上述範例中,沿著高度維度的連續元素之間的距離(例如,A 到 D)等於張量的寬度(在此範例中為 3)。

若要說明不同的排列方式,請考慮以列優先順序。 換句話說,沿著高度維度的連續元素會連續儲存在線性記憶體空間中。 在此情況下,高度步幅一律為1,而寬度步幅為2(高度維度的大小)。

抵消: 0 1 2 3 4 5
值: 一個 D B E C F

更高維度

當涉及到大於兩個維度時,將佈局稱為列優先或行優先會變得不再合適。 因此,本主題的其餘部分會使用這類詞彙和標籤。

  • 2D:「HW」—高度是最高的維度(行優先)。
  • 2D:「WH」—寬度是最高階維度(列優先)。
  • 3D:“DHW”-深度是最高階維度,後面接著高度,然後寬度。
  • 3D:「WHD」—寬度是最高階維度,後面接著高度,然後是深度。
  • 4D:“NCHW”—影像數目(批次大小)、通道數目、高度、寬度。

一般而言,維度 的封裝 步幅等於較低順序維度大小的乘積。 例如,使用「DHW」配置時,D 步長等於 H * W。H 步長等於 W。W 步長等於 1。 當張量的總物理大小等於張量的總邏輯大小時,據說步幅是 緊密 的。換句話說,沒有多餘的空間,也沒有重疊的元素。

讓我們將 2D 範例延伸至三個維度,因此我們有深度 2、高度 2 和寬度 3 的張量(共 12 個邏輯元素)。

A B C
D E F

G H I
J K L

使用 「DHW」版面配置時,此張量會儲存如下。

抵消: 0 1 2 3 4 5 6 7 8 9 10 11
值: 一個 B C D E F G H J K L
  • D-stride 等於高度(2)乘以寬度(3)等於 6(舉例來說,即『A』和『G』之間的距離)。
  • H-stride = width (3) = 3 (例如,'A' 與 'D' 之間的距離)。
  • W-stride = 1 (例如,'A' 與 'B' 之間的距離)。

元素的索引/座標與步幅的內積提供了該元素在緩衝區中的位移。 例如,H 元素的位移 (d=1, h=0, w=1) 為 7。

{1, 0, 1} ⋅ {6, 3, 1} = 1 * 6 + 0 * 3 + 1 * 1 = 7

包裝的張量

上述範例說明 緊湊排列的 張量。 當張量的邏輯大小(以元素計)等於緩衝區的物理大小,且每個元素都有唯一的位址或位移時,張量被稱為封裝。 例如,如果緩衝區長度為12個元素,而且沒有一組元素在緩衝區中共用相同的位移,則2x2x3張量會封裝起來。 緊湊的張量是最常見的情況,但步幅設定允許更複雜的記憶體配置。

與時俱進的廣播

如果 tensor 的緩衝區大小(以元素計)小於其邏輯維度的乘積,那麼必定會有一些元素重疊。 這種常見的情況稱為廣播,其中一個維度的元素是另一個維度的重複。 例如,讓我們重新流覽 2D 範例。 假設我們想要一個在邏輯上是 2x3 的張量,但是第二行與第一行相同。 以下是它的外觀。

A B C
A B C

這可以儲存為打包的 HW/行優先張量。 但是,更精簡的儲存空間只會包含3個元素(A、B和 C),並使用0的高度步幅,而不是3個。 在此情況下,張量的實際大小為3個元素,但邏輯大小為6個元素。

一般而言,如果維度的步幅為 0,則較低順序維度中的所有元素都會沿著廣播維度重複;例如,如果張量是 NCHW,而 C-stride 為 0,則每個通道在 H 和 W 上都有相同的值。

填補步幅

如果張量的物理大小大於其元素所需的最小大小,則該張量稱為填充。 當沒有廣播或重疊的元素時,張量的最小大小(以元素計算)只是其維度的乘積。 您可以使用輔助函式 (請參閱 DirectML 輔助函式以查看該函式的清單)來計算 DirectML 張量的最小緩衝區大小

假設緩衝區包含下列值(『x』 元素表示填補值)。

0 1 2 3 4 5 6 7 8 9
一個 B C x x D E F x x

使用步幅為 5 而不是 3 的高度,即可描述填充的張量。 步驟不是每次跨越 3 個元素來到達下一行,而是跨越 5 個元素(3 個實際元素加上 2 個填充元素)。 例如,在計算機圖形中,填充很常見,用於確保影像具有兩的冪次對齊。

A B C
D E F

DirectML 緩衝區張量描述

DirectML 可以使用各種不同的實體張量配置,因為 DML_BUFFER_TENSOR_DESC 結構 同時具有 SizesStrides 成員。 某些算子實作在特定配置下可能更有效率,因此改變張量數據的存儲方式以提升效能並不罕見。

大部分 DirectML 運算元都需要 4D 或 5D 張量,且大小和步幅值的順序是固定的。 藉由調整張量描述中大小和步幅值的順序,DirectML 可以推斷不同的實體版面配置。

4D

5D

  • DML_BUFFER_TENSOR_DESC::Size = { N-size, C-size, D-size, H-size, W-size }
  • DML_BUFFER_TENSOR_DESC::Strides = { N-stride,C-stride,D-stride,H-stride,W-stride }

如果 DirectML 運算子要求 4D 或 5D 張量,但實際數據的維度較小(例如 2D),則最前面的維度應填入 1。 例如,將 "HW" 張量設置為 DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, H, W }。

如果 tensor 資料儲存在 NCHW/NCDHW 中,則除非您想要廣播或填充,否則不需要設定 DML_BUFFER_TENSOR_DESC::Strides。 您可以將 [步幅] 欄位設定為 nullptr。 不過,如果張量數據儲存在另一個配置中,例如 NHWC,則您需要大步,才能表達從 NCHW 到該版面配置的轉換。

以簡單的例子為例,考慮描述一個高度為 3、寬度為 5 的 2D 張量。

封裝的 NCHW (隱含步幅)

  • DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Strides = nullptr

包裝的 NCHW (明確步幅)

  • N 步幅 = C 碼 * H 碼 * W 碼 = 1 * 3 * 5 = 15
  • C-stride = 高度(H-size)* 寬度(W-size) = 3 * 5 = 15
  • H-步幅 = W-大小 = 5
  • W 步幅 = 1
  • DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Strides = { 15, 15, 5, 1 }

填充的 NHWC

  • N 步幅 = H 大小 * W 大小 * C 大小 = 3 * 5 * 1 = 15
  • H 步幅 = W 大小 * C 大小 = 5 * 1 = 5
  • W 步幅 = C 大小 = 1
  • C 步幅 = 1
  • DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, 3, 5 }
  • DML_BUFFER_TENSOR_DESC::Strides = { 15, 1, 5, 1 }

另請參閱