Direct3D 12 バッファーによってサポートされる DirectML テンソルは、テンソルの サイズ と ストライド と呼ばれるプロパティによって記述されます。 テンソルの サイズ は、テンソルの論理寸法を表します。 たとえば、2D テンソルの高さは 2、幅は 3 です。 論理的には、テンソルには 6 つの異なる要素がありますが、サイズによってそれらの要素をメモリに格納する方法は指定されません。 テンソルの ストライドは、 テンソルの要素の物理メモリ レイアウトを記述します。
2 次元 (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 |
ディメンションの ストライド は、そのディメンション内の次の要素にアクセスするためにスキップする要素の数です。 ストライドを使用すると、メモリ内のテンソルのレイアウトが表現されます。 行優先順では、次元に沿って隣接する要素が連続して格納されるため、幅ディメンションのストライドは常に 1 になります。 高さ寸法のストライドは、幅寸法のサイズによって異なります。上の例では、高さ寸法に沿った連続する要素間の距離 (たとえば、A から D) はテンソルの幅 (この例では 3) と等しくなります。
別のレイアウトを示すには、列優先の順序を検討してください。 つまり、高さ寸法に沿った連続する要素は、線形メモリ空間に連続して格納されます。 この場合、高さストライドは常に 1、幅ストライドは 2 (高さ寸法のサイズ) です。
オフセット: | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
値: | ある | D | B | E | C | F |
高次元
2 つ以上のディメンションに関しては、レイアウトを行メジャーまたは列メジャーとして参照するのは扱いにくいです。 そのため、このトピックの残りの部分では、次のような用語とラベルを使用します。
- 2D: "HW"—高さが最高位の次元です (行優先)。
- 2D: "WH"—幅が最高位の次元です (列優先)。
- 3D: "DHW" - 深度は最も高い次元で、その後に高さ、幅が続きます。
- 3D: "WHD" — 幅は最も高い次元で、その後に高さ、奥行きが続きます。
- 4D: "NCHW"— 画像の数 (バッチ サイズ)、チャネルの数、高さ、幅。
一般的に、ある次元の "パックされた" ストライドは、下位の次元のサイズの積と等しくなります。 たとえば、"DHW" レイアウトでは、D ストライドは H * W と等しくなります。HストライドはWと等しい。W ストライドは 1 に等しくなります。 ストライドは、テンソルの合計物理サイズがテンソルの合計論理サイズと等しい場合に パック されると言われます。つまり、余分なスペースや重複する要素はありません。
2D の例を 3 次元に拡張して、深さ 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 ストライド = 高さ (2) * 幅 (3) = 6 (たとえば、'A' と 'G' の間の距離)。
- H ストライド = 幅 (3) = 3 (たとえば、'A' と 'D' の間の距離)。
- W ストライド = 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 テンソルがパックされます。 パックされたテンソルは最も一般的なケースですが、ストライドを利用すると、さらに複雑なメモリ レイアウトも可能になります。
ストライドを使用したブロードキャスト
テンソルのバッファー サイズ (要素単位) が論理ディメンションの積より小さい場合は、要素の重複が存在する必要があります。 この場合の通常のケースは ブロードキャストと呼ばれます。ディメンションの要素が別のディメンションの複製である場合。 たとえば、2D の例をもう一度見てみましょう。 論理的に 2x3 のテンソルが必要ですが、2 番目の行が 1 番目の行と同一であるとします。 その外観を次に示します。
A B C
A B C
これは、パックされた HW/行優先テンソルとして格納できます。 ただし、よりコンパクトなストレージには 3 つの要素 (A、B、C) のみが含まれており、3 ではなく 0 の高さストライドが使用されます。 この場合、テンソルの物理サイズは 3 要素ですが、論理サイズは 6 要素です。
一般に、ディメンションのストライドが 0 の場合、下位ディメンションのすべての要素がブロードキャストされたディメンションに沿って繰り返されます。たとえば、テンソルが NCHW で C ストライドが 0 の場合、各チャネルは H と W に沿って同じ値を持ちます。
ストライドを使用したパディング
テンソルは、その物理的なサイズが要素が収まるために必要な最小サイズを超えている場合、パディングされるとされます。 ブロードキャストや重複する要素がない場合、テンソルの最小サイズ (要素単位) は、単にその次元の積です。 ヘルパー関数 DMLCalcBufferTensorSize
(その関数の一覧については DirectML ヘルパー関数 を参照) を使用して、DirectML テンソルの 最小 バッファー サイズを計算できます。
たとえば、バッファーに次の値が含まれているとします ('x' 要素はパディング値を示します)。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
ある | B | C | x | x | D | E | F | x | x |
このパディングされたテンソルを表現するには、高さのストライドとして 3 ではなく 5 を使用します。 次の行に到達するには、3 要素分進むのではなく、5 要素分進みます ("実際の" 3 要素に加えてパディングの 2 要素)。 パディングは、たとえば、イメージが 2 の累乗配置を持っていることを確認するために、コンピューター グラフィックスで一般的です。
A B C
D E F
DirectML バッファーテンソルの説明
DML_BUFFER_TENSOR_DESC構造体にはメンバーと メンバーの両方があるため、DirectML はさまざまな物理テンソル レイアウトで動作します。 一部の演算子の実装は、特定のレイアウトでより効率的な場合があるため、パフォーマンスを向上させるためにテンソル データの格納方法を変更することは珍しくありません。
ほとんどの DirectML 演算子には 4D または 5D テンソルが必要であり、サイズとストライドの値の順序は固定されています。 テンソルの説明でサイズとストライド値の順序を修正することで、DirectML でさまざまな物理レイアウトを推論できます。
4Dの
- DML_BUFFER_TENSOR_DESC::サイズ = { Nサイズ、Cサイズ、Hサイズ、Wサイズ }
- DML_BUFFER_TENSOR_DESC::Strides = { N のストライド, C のストライド, H のストライド, W のストライド }
5Dの
- DML_BUFFER_TENSOR_DESC::Sizes = { Nサイズ、Cサイズ、Dサイズ、Hサイズ、Wサイズ }
- DML_BUFFER_TENSOR_DESC::Strides = { N ストライド、C ストライド、D ストライド、H ストライド、W ストライド }
DirectML 演算子が 4D または 5D テンソルを必要とするが、実際のデータのランクが小さい場合 (2D など)、先頭のディメンションに 1 を入力する必要があります。 たとえば、"HW" テンソルは 、DML_BUFFER_TENSOR_DESC::Sizes = { 1, 1, H, W } を使用して設定されます。
テンソル データが NCHW/NCDHW に格納されている場合、ブロードキャストやパディングが必要な場合を除き、 DML_BUFFER_TENSOR_DESC::Strides を設定する必要はありません。 ストライド フィールドを nullptr
に設定できます。 ただし、テンソル データが NHWC などの別のレイアウトに格納されている場合は、NCHW からそのレイアウトへの変換を表現するためにストライドが必要です。
簡単な例として、高さ 3 と幅 5 の 2D テンソルの説明を考えてみましょう。
パックされた NCHW (暗黙的なストライド)
- DML_BUFFER_TENSOR_DESC::サイズ = { 1, 1, 3, 5 }
-
DML_BUFFER_TENSOR_DESC::ストライド =
nullptr
パックされた NCHW (明示的なストライド)
- N ストライド = C サイズ * H サイズ * W サイズ = 1 * 3 * 5 = 15
- C のストライド = H のサイズ * W のサイズ = 3 * 5 = 15
- H のストライド = W のサイズ = 5
- W のストライド = 1
- DML_BUFFER_TENSOR_DESC::サイズ = { 1, 1, 3, 5 }
- DML_BUFFER_TENSOR_DESC::ストライド = { 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::サイズ = { 1, 1, 3, 5 }
- DML_BUFFER_TENSOR_DESC::ストライド = { 15, 1, 5, 1 }