PyTorch-연속 ()


90

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_length10 inputstargets같은 모습을 :

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여전히 인접?

편집 : 나는 전화를 생략하려고했지만 contiguous()손실을 계산할 때 오류 메시지가 나타납니다.

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

그래서 분명히이 contiguous()예제에서 호출 이 필요합니다.

(이를 읽기 쉽게 유지하기 위해 여기에 전체 코드를 게시하는 것을 피했습니다. 위의 GitHub 링크를 사용하여 찾을 수 있습니다.)

미리 감사드립니다!


좀 더 설명적인 제목이 유용합니다. 제목을 개선하거나 최소한 tldr; to the point summary요점 요약을 간결하게 작성하는 것이 좋습니다 .
Charlie Parker 19


답변:


186

실제로 텐서의 내용을 변경하지 않는 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

이것은 연속적인 개념이 등장하는 곳입니다. 위의 내용 x은 연속적이지만 y메모리 레이아웃이 처음부터 만들어진 동일한 모양의 텐서와 다르기 때문이 아닙니다. "contiguous" 라는 단어 는 텐서의 내용이 연결이 끊긴 메모리 블록 주위에 퍼져 있지 않기 때문에 약간 오해의 소지가 있습니다. 여기서 바이트는 여전히 한 블록의 메모리에 할당되지만 요소의 순서는 다릅니다!

를 호출 contiguous()하면 실제로 텐서의 복사본을 만들므로 요소의 순서는 처음부터 동일한 모양의 텐서가 생성 된 것과 같습니다.

일반적으로 이것에 대해 걱정할 필요가 없습니다. PyTorch가 연속 텐서를 기대하지만, 경우의 당신은 얻을 것이다하지 않을 경우 RuntimeError: input is not contiguous다음 방금 호출을 추가 contiguous().


나는 이것을 다시 만났다. 당신의 설명은 아주 좋습니다! 저는 단지 궁금합니다. 만약 메모리의 블록이 널리 퍼지지 않았다면, "처음부터 만든 동일한 모양의 텐서와 다른" 메모리 레이아웃의 문제는 무엇 입니까? 일부 작업에 대해 연속적으로 만 요구되는 이유는 무엇입니까?
MBT

4
확실히 대답 할 수는 없지만 PyTorch 코드 중 일부는 C ++로 구현 된 연산의 고성능 벡터화 된 구현을 사용하고이 코드는 Tensor의 메타 정보에 지정된 임의의 오프셋 / 스트 리드를 사용할 수 없습니다. 이것은 단지 추측 일뿐입니다.
Shital Shah

1
수신자 contiguous()가 스스로 전화를 걸 수없는 이유는 무엇 입니까?
information_interchange

왜냐하면 당신은 연속적인 방식으로 그것을 원하지 않기 때문에 당신이하는 일을 통제하는 것은 항상 좋은 일입니다.
shivam13juna

2
또 다른 인기있는 텐서 연산은 permute"연속되지 않은"텐서를 반환 할 수도 있습니다.
올렉

31

[pytorch 문서] [1]에서 :

contiguous () → 텐서

Returns a contiguous tensor containing the same data as self 

텐서. 자체 텐서가 연속적이면이 함수는 자체 텐서를 반환합니다.

어디 contiguous여기 지표 주문으로뿐만 아니라 메모리에 연속뿐만 아니라 메모리에 같은 순서로 의미 메모리에 데이터를 변경하지 않는 전치을하고 예를 들어, 단순히 경우, 메모리 포인터로 인덱스에서지도 변경 적용 contiguous()하면 인덱스에서 메모리 위치로의 맵이 표준이되도록 메모리의 데이터가 변경 됩니다 . [1] : http://pytorch.org/docs/master/tensors.html


1
답변 주셔서 감사합니다! 데이터가 연속적이어야하는 이유 /시기를 말씀해 주시겠습니까? 성능일까요, 아니면 다른 이유일까요? PyTorch는 일부 작업에 연속 데이터가 필요합니까? 타겟이 연속적이어야하고 입력이 아닌 이유는 무엇입니까?
MBT

성능을위한 것입니다. 코드가 왜 입력이 아닌 대상에 대해 수행하는지 모르겠습니다.
patapouf_ai

2
따라서 분명히 pytorch는 손실 대상이 메모리에서 연속적이어야하지만 신경망의 입력은이 요구 사항을 충족 할 필요가 없습니다.
patapouf_ai

2
감사합니다! 나는 이것이 나에게 의미가 있다고 생각합니다 .contiguous ()는 forward 함수의 출력 데이터 (물론 이전에 입력이었던)에도 적용되므로 손실을 계산할 때 출력과 대상이 모두 연속적입니다. 감사합니다!
MBT

1
@patapouf_ai 아니요. 설명이 잘못되었습니다. 정답에서 지적했듯이 연속적인 메모리 블록에 관한 것이 아닙니다.
Akaisteph7

14

tensor.contiguous ()는 텐서의 복사본을 만들고 복사본의 요소는 연속적인 방식으로 메모리에 저장됩니다. contiguous () 함수는 일반적으로 처음 텐서를 전치 () 한 다음 모양을 변경 (보기) 할 때 필요합니다. 먼저 연속 텐서를 생성 해 보겠습니다.

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

stride () return (3,1)은 다음을 의미합니다. 각 단계 (행 단위)로 첫 번째 차원을 따라 이동할 때 메모리에서 3 단계를 이동해야합니다. 두 번째 차원 (열 단위)을 따라 이동할 때 메모리에서 한 단계 이동해야합니다. 이것은 텐서의 요소가 연속적으로 저장되었음을 나타냅니다.

이제 텐서에 come 함수를 적용 해 보겠습니다.

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

#(1, 3)
#False


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

#(3, 1)
#False


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

#(3, 1)
#True


## 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())

#(3, 1, 0)
#False


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

#(24, 2, 1)
#True

좋아, 우리는 transpose (), 좁은 (), 텐서 슬라이싱, 그리고 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.]])

10

이전 답변에서와 같이 contigous ()는 연속 메모리 청크를 할당 하므로 텐서를 포인터전달 하는 c 또는 C ++ 백엔드 코드 에 텐서를 전달할 때 유용 합니다 .


3

받아 들여지는 답변이 너무 좋았고 transpose()기능 효과 를 속이려고했습니다 . 내가 확인할 수있는 두 가지 기능 생성 samestorage()과를 contiguous.

def samestorage(x,y):
    if x.storage().data_ptr()==y.storage().data_ptr():
        print("same storage")
    else:
        print("different storage")
def contiguous(y):
    if True==y.is_contiguous():
        print("contiguous")
    else:
        print("non contiguous")

이 결과를 확인하고 표로 얻었습니다.

기능

아래의 검사기 코드를 검토 할 수 있지만 텐서가 연속적이지 않은 경우 한 가지 예를 들어 보겠습니다 . 우리는 view()그 텐서를 간단하게 호출 할 수 없으며 , 필요 reshape()하거나 .contiguous().view().

x = torch.randn(3,2)
y = x.transpose(0, 1)
y.view(6) # RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
  
x = torch.randn(3,2)
y = x.transpose(0, 1)
y.reshape(6)

x = torch.randn(3,2)
y = x.transpose(0, 1)
y.contiguous().view(6)

또한 연속 생성하는 방법이 있습니다. 끝에 및 비 연속 텐서 . 동일한 스토리지 에서 작동 할 수있는 메서드와 반환 전에 새 스토리지 (read : clone the tensor)를 flip()생성하는 일부 메서드 가 있습니다 .

검사기 코드 :

import torch
x = torch.randn(3,2)
y = x.transpose(0, 1) # flips two axes
print("\ntranspose")
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nnarrow")
x = torch.randn(3,2)
y = x.narrow(0, 1, 2) #dim, start, len  
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\npermute")
x = torch.randn(3,2)
y = x.permute(1, 0) # sets the axis order
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nview")
x = torch.randn(3,2)
y=x.view(2,3)
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nreshape")
x = torch.randn(3,2)
y = x.reshape(6,1)
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nflip")
x = torch.randn(3,2)
y = x.flip(0)
print(x)
print(y)
contiguous(y)
samestorage(x,y)

print("\nexpand")
x = torch.randn(3,2)
y = x.expand(2,-1,-1)
print(x)
print(y)
contiguous(y)
samestorage(x,y) 

0

내가 이해하는 것에서 더 요약 된 대답은 다음과 같습니다.

Contiguous는 텐서의 메모리 레이아웃이 광고 된 메타 데이터 또는 모양 정보와 일치하지 않음을 나타내는 데 사용되는 용어입니다.

내 생각에 coniguous라는 단어는 혼란 스럽거나 오해의 소지가있는 용어입니다. 정상적인 상황에서는 메모리가 연결이 끊어진 블록 (즉, "연속 / 연결 / 연속")에 퍼지지 않을 때를 의미하기 때문입니다.

일부 작업에는 어떤 이유로이 연속 속성이 필요할 수 있습니다 (GPU의 효율성 등).

참고 .view이 문제가 발생할 수 있습니다 또 다른 작업입니다. 연속 호출하여 수정 한 다음 코드를 살펴보십시오 (여기에서 발생하는 일반적인 전치 문제 대신 RNN이 입력에 만족하지 않을 때 발생하는 예입니다).

        # normal lstm([loss, grad_prep, train_err]) = lstm(xn)
        n_learner_params = xn_lstm.size(1)
        (lstmh, lstmc) = hs[0] # previous hx from first (standard) lstm i.e. lstm_hx = (lstmh, lstmc) = hs[0]
        if lstmh.size(1) != xn_lstm.size(1): # only true when prev lstm_hx is equal to decoder/controllers hx
            # make sure that h, c from decoder/controller has the right size to go into the meta-optimizer
            expand_size = torch.Size([1,n_learner_params,self.lstm.hidden_size])
            lstmh, lstmc = lstmh.squeeze(0).expand(expand_size).contiguous(), lstmc.squeeze(0).expand(expand_size).contiguous()
        lstm_out, (lstmh, lstmc) = self.lstm(input=xn_lstm, hx=(lstmh, lstmc))

내가 얻는 데 사용한 오류 :

RuntimeError: rnn: hx is not contiguous


출처 / 자원 :

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.