programing

.contiguous()는 PyTorch에서 무엇을 합니까?

abcjava 2023. 6. 15. 21:30
반응형

.contiguous()는 PyTorch에서 무엇을 합니까?

가 입니까?x.contiguous() 서를보 ▁a▁for에 대해 do.x?

PyTorch의 텐서에는 텐서의 내용을 변경하지 않고 데이터가 구성되는 방식을 변경하는 몇 가지 작업이 있습니다.이러한 작업에는 다음이 포함됩니다.

narrow(),view(),expand()그리고.transpose()

예: 전화를 걸 때transpose()PyTorch는 새로운 레이아웃을 가진 새로운 텐서를 생성하지 않고 단지 오프셋과 스트라이드가 원하는 새로운 모양을 설명하도록 텐서 객체의 메타 정보를 수정합니다.에서, 를 공유합니다: 예 에 서 전 텐 텐 유 를 메 합 공 니 리 다 모 이 한 동 일 는 서 원 치 래 서 와 다 ▁in 니 ▁share 합 ▁and 이 : , 공 ▁the ▁original ▁the

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

이것이 바로 연속이라는 개념이 들어오는 부분입니다.위의 예에서,x하지만 연적이만지속▁is만지▁contig연.y메모리 레이아웃이 처음부터 만든 동일한 모양의 텐서와 다르기 때문이 아닙니다."연속"이라는 단어는 텐서의 내용이 연결되지 않은 메모리 블록을 중심으로 퍼져 있는 것이 아니기 때문에 약간 오해를 불러일으킬 수 있습니다.여기서 바이트는 여전히 하나의 메모리 블록에 할당되지만 요소의 순서는 다릅니다!

에 전화할 때.contiguous()실제로 텐서의 복사본을 만들어 메모리에 있는 요소의 순서가 동일한 데이터로 처음부터 생성된 것과 동일하도록 합니다.

일반적으로 이것에 대해 걱정할 필요가 없습니다.당신은 일반적으로 모든 것이 잘 될 것이라고 가정하고, 당신이 수술을 받을 때까지 기다리는 것이 안전합니다.RuntimeError: input is not contiguous가 여기 PyTorch는연텐서호출추것예으상다합에 할 것으로 합니다.contiguous().

피토치 설명서에서 다음을 확인합니다.

contiguous() → Tensor
자체 텐서와 동일한 데이터를 포함하는 연속 텐서를 반환합니다.자가 텐서가 연속적인 경우 이 함수는 자가 텐서를 반환합니다.

에▁where디contiguous하는 것은만 아니라 입니다. 를 들어,포인터로 입니다. 예를 들어, 메모리에서 데이터를 변경하지 않고, 단순히 맵을 인덱스에서 메모리 포인터로 변경합니다. 만약 당신이 적용한다면.contiguous()인덱스에서 메모리 위치로의 맵이 표준 맵이 되도록 메모리의 데이터를 변경합니다.

배열 1원배열[0, 1, 2, 3, 4]항목이 아래와 같이 메모리에 서로 나란히 배치되어 있는 경우는 다음과 같습니다.

contiguous memory allocation

저장된 메모리 영역이 다음과 같은 경우 연속되지 않습니다.

non contiguous allocation

2차원 배열 이상의 경우 항목도 서로 옆에 있어야 하지만 순서는 서로 다른 규칙을 따릅니다.아래의 2D 어레이를 고려해 보겠습니다.

>>> t = torch.tensor([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]])

two dimensional array

다음과 같이 행이 서로 옆에 저장된 경우 메모리 할당은 C 연속입니다.

two dimensional memory

이것이 PyTorch가 연속적이라고 생각하는 것입니다.

>>> t.is_contiguous()
True

PyTorch의 Tensor 클래스 방법은 각 차원에서 다음 요소를 얻기 위해 건너뛸 바이트 수를 제공합니다.

>>> t.stride()
(4, 1)

다음 줄로 이동하려면 4바이트를 건너뛰어야 하지만 같은 줄의 다음 요소로 이동하려면 1바이트만 건너뜁니다.

다른 답변에서 언급했듯이 일부 Pytorch 작업은 메모리 할당을 변경하지 않고 메타데이터만 변경합니다.

예를 들어 전치법입니다.텐서를 전치해 보겠습니다.

two dimensional array

메모리 할당이 변경되지 않았습니다.

two dimensional memory non contiguous

하지만 보폭은 다음과 같습니다.

>>> t.T.stride()
(1, 4)

다음 줄로 이동하려면 1바이트를 건너뛰고 같은 줄의 다음 요소로 이동하려면 4바이트를 건너뛰어야 합니다.텐서는 더 이상 C continuous가 아닙니다(사실 Fortran continuous입니다: 각 열은 서로 옆에 저장됩니다).

>>> t.T.is_contiguous()
False

contiguous() 텐서가 C 연속이 되도록 메모리 할당을 재정렬합니다.

two dimensional memory contiguous

>>> t.T.contiguous().stride()
(3, 1)

및 같은 일부 작업은 기본 데이터의 연속성에 다른 영향을 미칩니다.

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

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

strid() 리턴(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

좋아요, 우리는 전치(), 좁은() 텐서 슬라이싱, 그리고 확장()이 생성된 텐서를 연속적이지 않게 만들 것이라는 것을 찾을 수 있습니다.흥미롭게도()와 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

이 문제를 해결하려면, 인접하지 않은 텐서에 인접()을 추가하고, 인접 복사본을 만든 다음 뷰()를 적용하면 됩니다.

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

이전 답변 continuous()에서와 같이 연속 메모리 청크를 할당합니다. 텐서를 corc++ 백엔드 코드에 전달할 때 유용합니다. 여기서 텐서는 포인터로 전달됩니다.

받아들여진 답변들이 너무 좋았고, 저는 속이려고 노력했습니다.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")

확인한 결과 표로 다음과 같은 결과를 얻었습니다.

functions

아래에서 체커 코드를 검토할 수 있지만 텐서가 비연속적인 경우 한 가지 예를 들어 보겠습니다.우리는 단순히 전화할 수 없습니다.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)

또한 연속 텐서와 비연속 텐서를 생성하는 방법도 있습니다.동일한 스토리지에서 작동할 수 있는 방법과 다음과 같은 방법이 있습니다.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)

가장 오른쪽 차원에서 시작하여 스토리지에 배치된 값을 가진 텐서(즉, 2D 텐서의 경우 행을 따라 이동)는 로 정의됩니다.연속 텐서는 스토리지에서 이리저리 뛰어다니지 않고 효율적으로 순서대로 방문할 수 있기 때문에 편리합니다(데이터 인접성 향상은 최신 CPU에서 메모리 액세스가 작동하는 방식으로 인해 성능을 향상시킵니다).물론 이러한 장점은 알고리즘이 방문하는 방식에 따라 다릅니다.

PyTorch의 일부 텐서 연산은 다음과 같은 인접 텐서에서만 작동합니다.view, [...].그런 경우, PyTorch는 정보를 제공하는 예외를 던지고 우리에게 계속해서 명시적으로 전화를 걸 것을 요구할 것입니다.주목할 만한 것은 전화가contiguous텐서가 이미 연속적인 경우에는 아무 일도 하지 않으며 성능에도 영향을 주지 않습니다.

이것은 컴퓨터 과학에서 "연속적인"이라는 단어를 일반적으로 사용하는 것보다 더 구체적인 의미입니다(즉, 연속적이고 순서대로).

예: 주어진 텐서:

[[1, 2]
 [3, 4]]
메모리에 저장 contiguous? 일반적으로 기억 공간에서 "연속적인"?
1 2 3 4 0 0 0
1 3 2 4 0 0 0
1 0 2 0 3 0 4

제가 이해한 바로는 이것은 좀 더 요약된 답변입니다.

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

제 생각에 연속이라는 단어는 일반적인 맥락에서 메모리가 연결되지 않은 블록(즉, "연속적인/연결된/연속적인" 블록)에서 확산되지 않는 경우를 의미하기 때문에 혼란스럽고 오해의 소지가 있는 용어입니다.

일부 작업에서는 어떤 이유로 이 연속 속성이 필요할 수 있습니다(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


소스/리소스:

언급URL : https://stackoverflow.com/questions/48915810/what-does-contiguous-do-in-pytorch

반응형