pytorch를 사용하다보면 코드속에 .contiguous()가 가끔 보일때가 있다. 이는 연속적인 메모리 텐서를 반환하는 메서드로 만약 어떤 연산을 사용할때 이를 사용하지 않으면 에러가 발생하는 경우가 생긴다.

 

예로 아래와 같이 a라는 텐서를 만들고 텐서의 형태(shape)와 보폭(stride)을 살펴보면 다음과 같다.[1]

>>> a = torch.randn(2, 3, 4)
>>> a.size()
torch.Size([2, 3, 4])
>>> a.stride()
(12, 4, 1)

여기서 보폭은 해당하는 차원의 원소에 접근할때 건너 뛰어야 할 원소들의 수(보폭)를 의미한다. 위의 예로 0차원 에서 건너 다음 원소를 가져올때는 12개의 원소를 뛰어 넘어야 한다는 의미이다.

이는 말이 된다. 하지만 메모리에 연속적으로 값이 들어있지 않는 경우에는 말이좀 달라진다.

 

예로 아래와 같이 a텐서의 차원 0과 1을 바꾸어 보았다. 그리고 텐서의 형태가 바뀐 a와 같은 형태의 b를 만들어서 비교 해보면 다음과 같다.[1]

>>> a = a.transpose(0, 1)
>>> a.size()
torch.Size([3, 2, 4])
>>> a.stride()
(4, 12, 1)
>>> a.is_contiguous()
False
>>> b = torch.randn(3, 2, 4)
>>> b.stride()
(8, 4, 1)
>>> b.is_contiguous()
True

여기서 눈여겨 볼점은 a와 b가 같은 형태로(3, 2, 4) 유지되나 실제 원소에 접근하기 위한 보폭을 살펴보면 값이 다르다는 점(a: [4, 12, 1], b: [8, 4, 1])을 확인할 수 있다.

이런 이유는 텐서 a의 형태가 변화될때 실제로 해당 원소에 접근할때는(메모리에서는 원소들의) 위치가 변화되지 않았고 접근 인덱스만 변화되었기 때문이다. 이는 pytorch에서 일부 텐서 연산을 수행함에 있어서 성능향상을 위한것으로 생각된다.(만약 텐서의 형태가 바뀔때마다 메모리에 있는 그래도 배치하려면 재 할당을 해주어야 하는데 이러한 연산이 잦으면 오히려 성능을 떨어뜨리는 원인이 될수있다.)  그래서 형태가 바뀐 a의 보폭을 해석해보면 차원 0 에서 다음 원소에 접근할때 8개가 아닌 4개의 원소만 건너면 b와 같은 텐서처럼 사용을 할수가 있다는 의미로, 실제 메모리에는 a와 b가 다른 순서로 배열이 되어있으나 메모리 재할당을 할필요 없이 접근 인덱스만 바꾸면 b처럼 사용을 할수가 있다는 의미가 된다.

 

그리고 .contiguous() 메서드는 다음과 같이 이러한 a와 같은 비연속적인 텐서를 연속적으로 만들어주는 역할을 한다.

>>> a = a.contiguous()
>>> a.stride()
(8, 4, 1)

이제 텐서 b랑 같아졌다.

 

아래의 예는 contiguous하지 않은 텐서를 펴서 사용할 때 접할 수 있는 에러이다.

>>> a = a.transpose(0, 1)
>>> a = a.view(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
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.

 

이를 해결하려면 텐서를 연속적으로 만들어주면 된다.

>>> a = a.contiguous()
>>> a = a.view(-1)
>>> a.size()
torch.Size([24])

 

또는 .contiguous() 없이 아래와 같이 사용이 가능하다.

>>> a = torch.randn(3, 2, 4)
>>> a = a.transpose(0, 1)
>>> a = a.reshape(-1) # view() 대신 reshape() 사용
>>> a.size()
torch.Size([24])

위의 예 처럼 연속을 고려하는 메서드(reshape)와 고려하지 않는 텐서변환 메서드(view)가 존재하니 알맞게 사용하면 에러를 예방할수 있다.

참고문헌

1. "Contiguous vs non-contiguous tensor", Nov 2018, discuss.pytorch.org/t/contigious-vs-non-contigious-tensor/30107

+ Recent posts