transpose와 permute
transpose(): 두 차원 교환
섹션 제목: “transpose(): 두 차원 교환”transpose(dim0, dim1) 은 지정한 두 차원의 순서를 교환합니다. 결과는 원본과 메모리를 공유 합니다.
import torch
t = torch.randn(3, 4) # shape: (3, 4)
# 2D: dim 0과 dim 1 교환 (행렬 전치)t_T = torch.transpose(t, 0, 1)print(t_T.shape) # torch.Size([4, 3])
# .T 속성은 2D에서 transpose(0, 1)과 동일print(t.T.shape) # torch.Size([4, 3])3D 텐서에서 두 특정 차원을 교환합니다.
t3 = torch.randn(2, 3, 4) # shape: (2, 3, 4)
# dim 1과 dim 2 교환print(torch.transpose(t3, 1, 2).shape) # torch.Size([2, 4, 3])
# dim 0과 dim 2 교환print(torch.transpose(t3, 0, 2).shape) # torch.Size([4, 3, 2])permute(): 임의의 차원 재배열
섹션 제목: “permute(): 임의의 차원 재배열”permute() 는 모든 차원의 순서를 한 번에 지정 합니다. transpose가 두 차원의 교환에 한정된 반면, permute는 임의의 순열을 지원합니다.
t = torch.randn(2, 3, 4) # shape: (2, 3, 4)
# (0, 1, 2) → (2, 0, 1): 원래 dim 2가 첫 번째로print(t.permute(2, 0, 1).shape) # torch.Size([4, 2, 3])
# (0, 1, 2) → (1, 2, 0)print(t.permute(1, 2, 0).shape) # torch.Size([3, 4, 2])
# 역순print(t.permute(2, 1, 0).shape) # torch.Size([4, 3, 2])permute 인수 읽는 방법
섹션 제목: “permute 인수 읽는 방법”t.permute(2, 0, 1) ↑ ↑ ↑결과의 0 1 2 번째 차원이원본의 2 0 1 번째 차원에서 옴t = torch.randn(A, B, C) # shape: (A, B, C)# t.permute(2, 0, 1).shape == (C, A, B)실전: 이미지 텐서 형식 변환
섹션 제목: “실전: 이미지 텐서 형식 변환”딥러닝에서 이미지 텐서는 두 가지 형식이 혼용됩니다.
| 형식 | 설명 | 사용처 |
|---|---|---|
(H, W, C) | Height, Width, Channel | NumPy, PIL, OpenCV, matplotlib |
(C, H, W) | Channel, Height, Width | PyTorch, torchvision |
import torch
# NumPy/PIL 방식: (H, W, C)image_hwc = torch.randn(256, 256, 3)print(image_hwc.shape) # torch.Size([256, 256, 3])
# PyTorch 방식으로 변환: (H, W, C) → (C, H, W)image_chw = image_hwc.permute(2, 0, 1)print(image_chw.shape) # torch.Size([3, 256, 256])
# 배치 포함: (B, H, W, C) → (B, C, H, W)batch_hwc = torch.randn(32, 256, 256, 3)batch_chw = batch_hwc.permute(0, 3, 1, 2)print(batch_chw.shape) # torch.Size([32, 3, 256, 256])
# 역변환: (C, H, W) → (H, W, C) — matplotlib 출력용restored = image_chw.permute(1, 2, 0)print(restored.shape) # torch.Size([256, 256, 3])contiguous 이슈
섹션 제목: “contiguous 이슈”transpose: (2, 3) → (3, 2) — 원소 재배치 확인
변환 전
1
2
3
4
5
6
형태: [2, 3]
변환 후
1
2
3
4
5
6
형태: [3, 2]
메모리 순서 (1D 평탄화)
1
2
3
4
5
6
transpose() 와 permute() 는 메모리 레이아웃을 바꾸지 않고 스트라이드(stride) 만 변경합니다. 따라서 결과 텐서는 non-contiguous 상태가 됩니다.
t = torch.randn(3, 4)t_T = t.T
print(t.is_contiguous()) # Trueprint(t_T.is_contiguous()) # False
# non-contiguous 텐서에 view() 적용 시 오류# t_T.view(12) # RuntimeError!해결 방법: contiguous() 호출
섹션 제목: “해결 방법: contiguous() 호출”t = torch.randn(3, 4)t_T = t.T
# 방법 1: contiguous() 후 view()r1 = t_T.contiguous().view(12)print(r1.shape) # torch.Size([12])
# 방법 2: reshape() — 내부적으로 자동 처리r2 = t_T.reshape(12)print(r2.shape) # torch.Size([12])스트라이드 확인
섹션 제목: “스트라이드 확인”t = torch.randn(3, 4)print(t.stride()) # (4, 1) — 행 이동 시 4칸, 열 이동 시 1칸
t_T = t.Tprint(t_T.stride()) # (1, 4) — 전치 후 스트라이드 역전
t_c = t_T.contiguous()print(t_c.stride()) # (3, 1) — 복사 후 새로운 연속 메모리transpose vs permute 선택
섹션 제목: “transpose vs permute 선택”| 상황 | 권장 함수 |
|---|---|
| 2D 행렬 전치 | .T 또는 transpose(0, 1) |
| 두 특정 차원 교환 | transpose(dim0, dim1) |
| 세 개 이상의 차원 재배열 | permute(순열) |
| 이미지 형식 변환 (HWC ↔ CHW) | permute() |
# transpose 두 번으로 permute 대체 가능 (비권장)t = torch.randn(2, 3, 4)r1 = t.permute(2, 0, 1) # 권장r2 = t.transpose(0, 2).transpose(0, 1) # 비권장 (가독성 낮음)print(torch.allclose(r1, r2)) # True