IT_World

[error]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. 본문

Artificial intelligence, AI/error

[error]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.

engine 2021. 5. 31. 16:40

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.

파이토치에서 Pytorch code를 돌리던 중 에러발생

x = x.view(x.size(0), -1)

에러 발생 위치는 

그러다가 .contiguous() 를 앞에 붙이라는 글을 보게됐다.

x = x.contiguous().view(x.size(0), -1)

무사히 돌아간다.

RuntimeError: invalid argument 1: input is not contiguous at

contiguous()손실을 계산할 때 오류 메시지가 표시되기도 한다.

 

개념정리 (PyTorch의 view, transpose, reshape 함수들의 차이점)


Contiguous(인접한, 근접한, 연속)

View(견해, 관점, 여기다, 보다)

Transpose(뒤바꾸다, 바꾸다, 이동시키다)

Reshape(모양[구조]을 고치다, 개장[개조]하다)

contiguous

그렇다면 강제로 물리적 위치를 연속적으로 만들어버리면 어떻게 될까? 겉보기에는 tt 와 별 차이가 없어보인다. 실제로 텐서의 복사본을 만들어 메모리에있는 요소의 순서가 동일한 데이터로 처음부터 생성 된 것처럼 동일하다. 텐서의 복사본을 만들고 복사본의 요소는 연속적인 방식으로 메모리에 저장한다.

메모리가 연속적으로 저장되었는지 여부를 나타내는 플래그입니다.
비 연속적인 텐서를 얻는 방법을보기 위해 예제를 사용합니다.

# Create a tensor of shape [4, 3]
x = torch.arange(12).view(4, 3)
print(x, x.stride())
> tensor([[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11]]) 
> (3, 1)

보시다시피 텐서는 원하는 모양을 가지고 있습니다.
이 경우 보폭도 흥미 롭습니다. 기본적으로 특정 축을 따라 다음 위치로 이동하기 위해 메모리에서 건너 뛰는 "단계"수를 알려줍니다.
보폭을 살펴보면 새 행으로 이동하려면 3 개의 값을 건너 뛰어야하고 다음 열로 이동하려면 1 개만 건너 뛰어야합니다. 지금까지는 말이됩니다. 값은 메모리에 순차적으로 저장됩니다. 즉, 메모리 셀은 데이터를 [0, 1, 2, 3, ..., 11].
이제 텐서를 조옮김하고 보폭을 다시 살펴 보겠습니다.

y = x.t()
print(y, y.stride())
print(y.is_contiguous())
> tensor([[ 0,  3,  6,  9],
        [ 1,  4,  7, 10],
        [ 2,  5,  8, 11]]) 
> (1, 3)
> False

텐서의 print 문은 원하는 전치 뷰를 생성합니다 x.
그러나 이제 보폭이 바뀝니다. 다음 행으로 이동하려면 1 개의 값만 건너 뛰고 다음 열로 이동하려면 3 개만 건너 뛰면됩니다.
이것은 텐서의 메모리 레이아웃을 떠올려 보면 의미
[0, 1, 2, 3, 4, ..., 11]
가 있습니다. 다음 열로 이동하려면 (예 : from 0to 3, 우리는 3 개의 값을 건너 뛰어야합니다.
따라서 텐서는 더 이상 연속적이지 않습니다!

일부 작업이 작동하지 않는다는 점을 제외하면 우리에게는 실제로 문제가되지 않습니다.
예를 들어 평면화 된 뷰를 얻으려고 y하면 다음과 같이 실행됩니다 RuntimeError.

try:
    y = y.view(-1)
except RuntimeError as e:
    print(e)
> 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().

view 하기전에 해준다.

y = y.contiguous()
print(y.stride())
> (4, 1)
y = y.view(-1)

이제 메모리 레이아웃이 다시 한 번 연속적이며 (스트라이드를 살펴보면) 제대로 view작동합니다.
나는 완전히 확실하지 않지만 contiguous호출이 메모리를 복사하여 다시 연속되도록 한다고 가정 합니다.

즉, 일부 벡터화 된 명령어가 작동하려면 연속 배열이 필요합니다. 또한 일반적으로 최신 CPU의 메모리 액세스 패턴이 최적의 방식으로 사용되기 때문에 성능상의 이점이 있어야합

 

view

원소의 수를 유지하면서 텐서의 크기 변경한다. 파이토치(pytorch) 텐서(tensor)의 뷰(View)는 넘파이(numpy)에서의 리쉐이프(Reshape)와 같은 역할을 한다. Reshape라는 이름에서 알 수 있듯이, 텐서의 크기(Shape)를 변경해주는 역할을 한다

아래는 예시로 만든 3차원 텐서이다.

t = np.array([[[0, 1, 2],
               [3, 4, 5]],
              [[6, 7, 8],
               [9, 10, 11]]])
ft = torch.FloatTensor(t)

print(ft.shape)
torch.Size([2, 2, 3])

transpose

호출 할 때 transpose()PyTorch는 새 레이아웃으로 새 텐서를 생성하지 않고 오프셋 및 스트라이드가 원하는 새 모양을 설명하도록 Tensor 개체의 메타 정보를 수정한다. 

아래 예시에서 전치된 텐서와 원래 텐서는 동일한 메모리를 공유한다.

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

reshape

reshape() == contiguous().view() 와 같다.

view  contiguous 하지 않은 tensor 에 대해서는 적용할 수 없지만 reshape 은 강제로 tensor를 contiguous 하게 만들면서 shape을 변경하기 때문에 가능하다.

 

Summary

  • view : tensor 에 저장된 데이터의 물리적 위치 순서와 index 순서가 일치할 때 (contiguous) shape을 재구성한다. 이 때문에 항상 contiguous 하다는 성질이 보유된다.
  • transpose : tensor 에 저장된 데이터의 물리적 위치 순서와 상관없이 수학적 의미의 transpose를 수행한다. 즉, 물리적 위치와 transpose가 수행된 tensor 의 index 순서는 같다는 보장이 없으므로 항상 contiguous 하지 않다.
  • reshape : tensor 에 저장된 데이터의 물리적 위치 순서와 index 순서가 일치하지 않아도 shape을 재구성한 이후에 강제로 일치시킨다. 이 때문에 항상 contiguous 하다는 성질이 보유된다.

참조

예시 : https://discuss.pytorch.org/t/contigious-vs-non-contigious-tensor/30107/2

Comments