최근에 pytorch로 간단한 모듈을 재구현하다가 loss와 dev score가 원래
구현된 결과와 달라서 의아해하던 찰나, tensor 차원을 변경하는 과정에서
의도하지 않은 방향으로 구현된 것을 확인하게 되었다. 그리고 그 이유는
transpose 와 view 의 기능을 헷갈려했기
때문이었다. 두 함수의 차이는 contiguous 를 이해해야 알 수
있는 내용이었고, 혹시 이 개념이 헷갈리는 사람들을 위해 간단한 예시를
바탕으로 정리해보았다.
contiguous 란 matrix 의 눈에 보이는 (advertised)
순차적인 shape information 과 실제로 matrix 의 각 데이터가 저장된 위치가
같은지의 여부이다. 아래의 예시에서 t 는
contiguous 하다. 왜냐하면 t[0][0][0] →
t[0][0][1] → t[0][1][0] ... 의 데이터 포인터
위치 (0 → 1 → 2 ... ) 또한
연속적이기 때문이다. 아직 이해가 되지 않아도 괜찮다. 예시를 좀 더
보자!
또한 t 와 tv 의 데이터는 pointer 값이
동일하여 한 쪽의 tensor data 를 수정하면 다른 쪽의 값도 동시에
변경된다.
t.storage().data_ptr() == tv.storage().data_ptr() # data pointer 값이 일치함 --- True
# Modifying view tensor changes base tensor as well. t[0][0][0] = 99 tv[0][0][0] --- tensor(99)
transpose
transpose 를 통해 t 라는 텐서의 shape을
변경시켜보았다. shape은 tv와 동일하나, 구성이 조금 다른
것을 확인할 수 있다. 참고로, 보통
(batch_size, hidden_dim, input_dim) 을
(batch_size, input_dim, hidden_dim) 으로 바꿔주는 작업을 할
때에 transpose 를 사용한다.
tt = t.transpose(2, 1) # (4, 2, 3)
print(tt) >folded
tensor([[[ 0, 2, 4], [ 1, 3, 5]],
[[ 6, 8, 10], [ 7, 9, 11]],
[[12, 14, 16], [13, 15, 17]],
[[18, 20, 22], [19, 21, 23]]])
앞선 예시에서 t 의 데이터 포인터는 0 →
1 → 2 ... 순서대로 저장된 것을 알 수 있었다.
t와 tv 모두 데이터 포인터의 물리적 순서와
shape 상에서의 데이터 순서가 같았기 때문에 contiguous 했다.
하지만 tt 의 경우 0 → 1 →
2 ... ≠ tt[0][0][0] → tt[0][0][1]
→ tt[0][0][2] ... 이기 때문에 contiguous 하지
않다.
tt.is_contiguous() --- False
tt 를 flatten 시키면 물리적 순서 (0 →
1 → 2 ... ) 와 shape 상의 순서가 다른 것을
확인할 수 있다.
RuntimeError: view size isnot compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.