web-dev-qa-db-ja.com

PyTorch-contiguous()

私はgithub (link) でLSTM言語モデルのこの例を試していました。それが一般的にすることは私にはかなり明らかです。しかし、私はまだcontiguous()の呼び出しが何をするかを理解するのに苦労しています。これはコード内で何度も発生します。

たとえば、コードの74/75行目では、LSTMの入力シーケンスとターゲットシーケンスが作成されます。データ(idsに格納)は2次元であり、最初の次元はバッチサイズです。

for i in range(0, ids.size(1) - seq_length, seq_length):
    # Get batch inputs and targets
    inputs = Variable(ids[:, i:i+seq_length])
    targets = Variable(ids[:, (i+1):(i+1)+seq_length].contiguous())

簡単な例として、バッチサイズ1およびseq_length 10 inputsおよびtargetsを使用する場合、次のようになります。

inputs Variable containing:
0     1     2     3     4     5     6     7     8     9
[torch.LongTensor of size 1x10]

targets Variable containing:
1     2     3     4     5     6     7     8     9    10
[torch.LongTensor of size 1x10]

それで、一般的に私の質問は、contiguous()とは何で、なぜそれが必要なのですか?

さらに、ターゲットシーケンスに対してメソッドが呼び出される理由がわかりませんが、両方の変数が同じデータで構成されているため、入力シーケンスはわかりません。

どうしてtargetsは連続しておらず、inputsはまだ連続しているのでしょうか?

EDIT:_contiguous()の呼び出しを省略しようとしましたが、損失を計算するときにエラーメッセージが表示されます。

RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231

そのため、この例では明らかにcontiguous()を呼び出す必要があります。

(これを読みやすくするために、ここに完全なコードを投稿することは避けました。上のGitHubリンクを使用して見つけることができます。)

前もって感謝します!

42
blue-phoenox

PyTorchのTensorには、実際にはテンソルの内容を変更せず、インデックスをバイト位置にテンソルに変換する方法のみを変更する操作がいくつかあります。これらの操作は次のとおりです。

narrow()view()expand()およびtranspose()

例:transpose()を呼び出すと、PyTorchは新しいレイアウトで新しいテンソルを生成せず、Tensorオブジェクトのメタ情報を変更するだけなので、オフセットとストライドは新しい形状になります。転置テンソルと元のテンソルは確かにメモリを共有しています!

x = torch.randn(3,2)
y = torch.transpose(x, 0, 1)
x[0, 0] = 42
print(y[0,0])
# prints 42

これがcontiguousの概念の出番です。上記のxは隣接していますが、yはメモリレイアウトがゼロから作られた同じ形状のテンソルと異なるためではありません。 Word "contiguous"は、メモリの切断されたブロックの周りにテンソルの内容が広がることではないため、少し誤解を招くことに注意してください。ここでも、バイトはメモリの1ブロックに割り当てられていますが、要素の順序は異なります!

contiguous()を呼び出すと、実際にはテンソルのコピーが作成されるため、要素の順序は同じ形状のテンソルがゼロから作成された場合と同じになります。

通常、これについて心配する必要はありません。 PyTorchが連続テンソルを期待しているが、そうでない場合はRuntimeError: input is not contiguousを取得し、contiguous()への呼び出しを追加するだけです。

92
Shital Shah

pytorch documentation から:

contiguous()→テンソル

Returns a contiguous tensor containing the same data as self 

テンソル。自己テンソルが連続している場合、この関数は自己テンソルを返します。

ここでcontiguousは、メモリ内で連続していることを意味します。したがって、contiguous関数はターゲットテンソルにまったく影響を与えず、連続したメモリチャンクに格納されることを確認するだけです。

24
patapouf_ai

前の答えのように、contigous()は連続したメモリチャンクを割り当てます。テンソルをcまたはc ++バックエンドコードに渡すここでテンソルはポインターとして渡される

7
p. vignesh

tensor.contiguous()はテンソルのコピーを作成し、コピーの要素は連続した方法でメモリに保存されます。 contensuous()関数は、通常、テンソルを最初にtranspose()してから、それを変形(表示)するときに必要です。まず、連続テンソルを作成しましょう。

aaa = torch.Tensor( [[1,2,3],[4,5,6]] )
print(aaa.stride())
print(aaa.is_contiguous())
#(3,1)
#True

Stride()return(3,1)は、各ステップ(行ごと)で最初の次元に沿って移動する場合、メモリ内で3ステップ移動する必要があることを意味します。 2番目の次元に沿って(列ごとに)移動する場合、メモリ内で1ステップ移動する必要があります。これは、テンソル内の要素が連続して格納されていることを示しています。

次に、テンソルにcome関数を適用してみます。

bbb = aaa.transpose(0,1)
print(bbb.stride())
print(bbb.is_contiguous())

ccc = aaa.narrow(1,1,2)   ## equivalent to matrix slicing aaa[:,1:3]
print(ccc.stride())
print(ccc.is_contiguous())


ddd = aaa.repeat(2,1 )   # The first dimension repeat once, the second dimension repeat twice
print(ddd.stride())
print(ddd.is_contiguous())

## expand is different from repeat  if a tensor has a shape [d1,d2,1], it can only be expanded using "expand(d1,d2,d3)", which
## means the singleton dimension is repeated d3 times
eee = aaa.unsqueeze(2).expand(2,3,3)
print(eee.stride())
print(eee.is_contiguous())

fff = aaa.unsqueeze(2).repeat(1,1,8).view(2,-1,2)
print(fff.stride())
print(fff.is_contiguous())

#(1, 3)
#False
#(3, 1)
#False
#(3, 1)
#True
#(3, 1, 0)
#False
#(24, 2, 1)
#True

わかりました、transpose()、narrow()and tensor slicing、and expand()が生成されたテンソルを隣接させないことがわかります。興味深いことに、repeat()とview()は不連続ではありません。だから今問題は:不連続なテンソルを使用するとどうなりますか?

答えは、view()関数を不連続なテンソルに適用できないということです。これは恐らく、view()がメモリ内で高速に形状を変更できるように、テンソルを連続して保存する必要があるためです。例えば:

bbb.view(-1,3)

エラーが発生します:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-63-eec5319b0ac5> in <module>()
----> 1 bbb.view(-1,3)

RuntimeError: invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Call .contiguous() before .view(). at /pytorch/aten/src/TH/generic/THTensor.cpp:203

これを解決するには、単純にcontiguous()を不連続なテンソルに追加し、連続したコピーを作成してからview()を適用します

bbb.contiguous().view(-1,3)
#tensor([[1., 4., 2.],
        [5., 3., 6.]])
1
avatar