私は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リンクを使用して見つけることができます。)
前もって感謝します!
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()
への呼び出しを追加するだけです。
contiguous()→テンソル
Returns a contiguous tensor containing the same data as self
テンソル。自己テンソルが連続している場合、この関数は自己テンソルを返します。
ここでcontiguous
は、メモリ内で連続していることを意味します。したがって、contiguous
関数はターゲットテンソルにまったく影響を与えず、連続したメモリチャンクに格納されることを確認するだけです。
前の答えのように、contigous()は連続したメモリチャンクを割り当てます。テンソルをcまたはc ++バックエンドコードに渡すここでテンソルはポインターとして渡される
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.]])