텐서는 저장 공간과 뷰 메타데이터로 이루어져
텐서는 두 부분으로 나눌 수 있어. 하나는 자료형이 정해진 1차원 연속 메모리 블록인 저장 공간이고, 다른 하나는 그 저장 공간을 해석하는 모양·스트라이드·오프셋 같은 뷰 메타데이터야. 슬라이스하거나 전치해도 저장 공간은 보통 그대로고 뷰만 바뀌어. 그래서 슬라이싱은 O(1)이고 .transpose()도 바이트를 옮기지 않지만, 결과의 연속성은 깨질 수 있어.
스트라이드가 뜻하는 것
t.stride()는 차원마다 정수 하나를 돌려줘. 각 값은 그 차원의 인덱스를 하나 늘릴 때 저장 공간에서 몇 칸 이동해야 하는지를 뜻해. 연속 배치인 (3, 4) float 텐서의 스트라이드는 (4, 1)이야. 다음 행으로 내려가려면 원소 네 칸을, 다음 열로 가려면 한 칸을 이동해.
전치는 저장 공간을 건드리지 않고 스트라이드만 맞바꿔. (3, 4) 텐서의 모양과 스트라이드가 각각 (4, 3), (1, 4)로 바뀌는 식이야. 그래서 전치한 뒤에는 .is_contiguous()가 False가 돼. 메모리에 놓인 순서대로 원소를 걷는 방식과 차원 순서대로 걷는 방식이 더 이상 일치하지 않기 때문이야.
왜 중요할까
- 어떤 연산이 새 메모리를 할당하고 어떤 연산이 메타데이터만 바꾸는지 예측할 수 있어.
- 이미지 배치를 NHWC에서 NCHW로 바꾸는 것처럼 겉보기에는 단순한 변환이 왜 시간과 메모리를 쓰는지 추론할 수 있어.
- 학습이 예상보다 메모리를 30% 더 쓰는 이유도 찾기 쉬워져. 원인은 대개 의도치 않은 복사 한 번이야.