テンソルの形状、ストレージ、NumPy の違い

System.Numerics.Tensors パッケージは、多次元データ用のTensor<T>、TensorSpan<T>、およびReadOnlyTensorSpan<T>を提供します。 テンソル形状は論理寸法を表します。ストライドは、各次元に沿った連続する位置間の距離を要素単位で表します。

ここで説明している動作は、サポートされている各ターゲット フレームワーク向けパッケージに含まれています。 パッケージを更新すると、以前の.NETバージョンを対象とするアプリに影響する可能性があります。

これらの型は NumPy と多くの規約を共有していますが、互換性があるわけではありません。 アルゴリズムを移植するときは、次の違いを考慮してください。

空の形状とスカラーのような結果

基になるデータなしで空の形状 ([]) からテンソルを作成すると、テンソルの形状は [0] で、要素はありません。 NumPy では、形状 ()はランク 0 であり、1 つのスカラー要素を含みます。 特に、 np.empty(()) は初期化されていない値を持つスカラーを割り当てます。"empty" は、要素がないことを意味するわけではありません。

これに対し、既存のデータに対して tensor-span コンストラクターから長さを省略すると、コンストラクターは指定された配列またはスパンから 1 次元の形状を推論します。

形状[1, 1]のテンソルを絞ると、結果は NumPy のランク 0 の形状[1]ではなく、形状()になります。 どちらの結果にも 1 つの要素が含まれており、スカラーとしてブロードキャストできますが、ランクを調べたり、軸を選択したり、インデックスを付けたりするコードは、保持されたディメンションを考慮する必要があります。

ストライドを省略すると、長さ 0 のディメンションを含む形状は、すべてのディメンションでストライドが 0 になります。 その要素数と必要なストレージは、他のディメンションの長さに関係なく、0 です。 負の長さは無効なままです。 明示的なストライドは、レイアウトの妥当性確認に合格する必要があります。

ストライドと逆順データ

ストライドは負でない必要があります。 NumPy は、逆のビューを負のストライド ( array[::-1]など) で表すことができます。

最初の軸を反転させるには、ディメンション 0 で Tensor.ReverseDimension を呼び出します:

using System.Numerics.Tensors;

Tensor<int> source = Tensor.Create(new[] { 1, 2, 3, 4 }, new nint[] { 2, 2 });
Tensor<int> reversed = Tensor.ReverseDimension<int>(source.AsReadOnlyTensorSpan(), 0);

reversedの論理要素は[3, 4, 1, 2]。 割り当てオーバーロードによって新しいテンソルが生成されます。元のストレージに対する負のストライド ビューではありません。

テンソルのサイズを変更する

Tensor.Resize と Tensor.ResizeTo は、追加された論理要素を default(T) で埋めます。 たとえば、 [1, 2] を 5 つの int 要素に拡張すると、結果には [1, 2, 0, 0, 0]が含まれます。 NumPy の np.resize は代わりに入力を繰り返し、 [1, 2, 1, 2, 1]を生成します。

変換先が小さい場合、操作は宛先の論理要素数を超える要素を破棄します。 それより大きい場合、宛先に要素間の隙間がある場合でも、この操作は残りの論理宛先要素を初期化します。 論理宛先の外にあるストレージは、サイズ変更操作の一部ではありません。

Tensor.ResizeTo および既存の宛先への連結では、複数の論理要素を含むゼ ロストライドのディメンションは拒否されます。 このようなディメンションでは、複数の出力位置が同じストレージを参照し、個別の出力値を保持することはできません。 単一要素のディメンションにおけるストライドが 0 の場合や、コピー先が空の場合には、この競合は発生しません。

ソースと宛先が重複しています

個別のテンソル オブジェクトとスパンは、同じ配列の重複する領域を参照できます。 サポートされるケースは、操作とレイアウトの両方によって異なります。

Operation サポートされるオーバーラップ
CopyTo と TryCopyTo 要素数が等しい高密度な送信元と宛先、あるいは完全に同一のビュー。 高密度コピーでは、オーバーラップ セーフなスパン コピーが使用されます。
FlattenTo と TryFlattenTo 高密度な送信元と宛先のスパン。
出力先を使用した要素ごとの操作 ビューに重複する論理記憶位置がない場合、開始基準、長さ、ストライドが同一である入出力。 各入力は個別にチェックされます。
Tensor.ResizeTo 送信元と宛先の密度が高い。 サポートされていないstrided overlapは拒否されます。
同じビューへの戻り 高密度のビューは、その場で元に戻すことができます。 No-op のリバーサルもサポートされています。

その他、サポートされていない重複レイアウトの場合、操作が出力を書き込む前に ArgumentException がスローされます。 密なテンソルだけでは、すべての操作のサポートが保証されるわけではありません。たとえば、シフトされた密なコピー先はコピーでサポートされますが、要素ごとの操作ではサポートされません。

TryCopyToメソッドとTryFlattenToメソッドは、コピー先バッファーの長さが足りない場合、コピーせずにfalseを返します。 TryCopyTo は、ソースと宛先の形状が互換性がない場合にも false を返します。 互換性があり、宛先の容量が十分な形状の場合でも、これらのメソッドはサポートされていないオーバーラップが発生すると例外をスローすることがあります。Try は、無効なレイアウトに関する例外を抑制しません。

たとえば、次のように、ストライド付きの送信元と高密度の宛先が重なり合う場合があります:

int[] storage = { 1, 2, 3, 4, 5, 6 };
ReadOnlyTensorSpan<int> source = new(storage, new nint[] { 2, 2 }, new nint[] { 3, 1 });
TensorSpan<int> destination = new(storage, new nint[] { 2, 2 });

このサポートされていない重複レイアウトからデータをコピーするには、まず独立したストレージにスナップショットを作成します。

int[] snapshot = new int[checked((int)source.FlattenedLength)];
source.FlattenTo(snapshot);
ReadOnlyTensorSpan<int> independentSource = new(snapshot, source.Lengths);
independentSource.CopyTo(destination);

スナップショットには [1, 2, 4, 5]が含まれています。 コピーは、読み取る前に元の値を変更せずに、それらの要素をコピー先に書き込みます。

出力先を指定する要素単位演算では、独立した結果テンソルを確保するか、出力を書き込む前に重複する各入力のスナップショットを取得します。 保存先が重複するブロードキャスト先については、代わりに独立した出力ストレージを使用してください。入力スナップショットを使用しても、そのブロードキャスト先が個別の値を保持できるようにはなりません。

ライブラリは、任意の重なり合うレイアウトをサポートするために、完全なテンソル スナップショットを暗黙的に割り当てません。 重複しないストライド コピーでは、その一時ストレージは必要ありません。

参照