브로드캐스팅 실전 예시
예시 1 — 벡터 + 스칼라
섹션 제목: “예시 1 — 벡터 + 스칼라”가장 단순한 형태입니다. 스칼라(0D)가 벡터의 모든 원소에 적용됩니다.
shape 변환 과정: A: (5,) ← 벡터 B: () ← 스칼라 (0D)
규칙 1: B의 shape 앞에 1을 추가 B: () → (1,)
규칙 2: 크기 1을 5로 확장 B: (1,) → (5,)
결과: (5,)import torch
scores = torch.tensor([72.0, 85.0, 91.0, 68.0, 79.0])bonus = torch.tensor(5.0) # 스칼라
result = scores + bonusprint(result) # tensor([77., 90., 96., 73., 84.])
# 파이썬 숫자도 동일하게 동작result2 = scores + 5.0print(result2) # tensor([77., 90., 96., 73., 84.])예시 2 — 행렬 + 벡터
섹션 제목: “예시 2 — 행렬 + 벡터”딥러닝에서 가장 자주 만나는 패턴입니다. 배치의 각 샘플에 같은 값을 더하거나 빼는 연산에 해당합니다.
케이스 A — 열 방향 브로드캐스팅
섹션 제목: “케이스 A — 열 방향 브로드캐스팅”벡터 shape이 (features,) 일 때, 배치의 각 행에 동일하게 적용됩니다.
shape 변환 과정: 행렬 A: (4, 3) ← 4개 샘플, 3개 특성 벡터 B: (3,) ← 특성 수와 동일
규칙 1: B → (1, 3)규칙 2: (1, 3) → (4, 3)
결과: (4, 3)# 4개 샘플의 3차원 특성 벡터 (배치)batch = torch.tensor([ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0], [10.0, 11.0, 12.0],]) # shape: (4, 3)
# 각 특성의 평균값mean = torch.tensor([2.5, 5.5, 8.5]) # shape: (3,)
# 평균 빼기 (정규화)normalized = batch - meanprint(normalized.shape) # torch.Size([4, 3])print(normalized)# tensor([[-1.5, -3.5, -5.5],# [ 1.5, -0.5, -2.5],# [ 4.5, 2.5, 0.5],# [ 7.5, 5.5, 3.5]])케이스 B — 행 방향 브로드캐스팅
섹션 제목: “케이스 B — 행 방향 브로드캐스팅”벡터를 (n, 1) 형태로 만들면 각 열에 적용됩니다.
shape 변환 과정: 행렬 A: (4, 3) 벡터 B: (4, 1) ← unsqueeze로 열 차원 추가
규칙 2: (4, 1) → (4, 3)
결과: (4, 3)# 각 샘플의 가중치 (스케일링 인자)weights = torch.tensor([1.0, 2.0, 0.5, 3.0]).unsqueeze(1)print(weights.shape) # torch.Size([4, 1])
scaled = batch * weightsprint(scaled.shape) # torch.Size([4, 3])print(scaled)# tensor([[ 1., 2., 3.],# [ 8., 10., 12.],# [ 3.5, 4., 4.5],# [30., 33., 36.]])예시 3 — 3D 텐서 브로드캐스팅
섹션 제목: “예시 3 — 3D 텐서 브로드캐스팅”배치 학습에서 가장 중요한 패턴입니다. 배치 전체에 동일한 변환을 적용할 때 사용합니다.
배치 이미지에 채널 평균 빼기
섹션 제목: “배치 이미지에 채널 평균 빼기”shape 변환 과정: 이미지 배치: (8, 3, 32) ← (배치, 채널, 픽셀) 채널 평균: (3, 1) ← 채널별 평균값
규칙 1: (3, 1) → (1, 3, 1)규칙 2: (1, 3, 1) → (8, 3, 32)
결과: (8, 3, 32)3D 브로드캐스팅: (2, 3, 1) + (1, 1, 4)
1단계: 원래 shape — 두 텐서의 원래 shape을 확인합니다.
텐서 A
1
2
3
shape: (2, 3, 1)
텐서 B
1
2
3
4
shape: (1, 1, 4)
1 / 3
import torch
# 8장의 이미지, 3채널, 32픽셀 (단순화된 예시)images = torch.randint(0, 256, (8, 3, 32), dtype=torch.float32)print(images.shape) # torch.Size([8, 3, 32])
# 채널별 평균 (ImageNet 평균과 유사한 형태)channel_mean = torch.tensor([0.485, 0.456, 0.406]).reshape(3, 1)print(channel_mean.shape) # torch.Size([3, 1])
# 브로드캐스팅으로 배치 전체 정규화normalized = images / 255.0 - channel_meanprint(normalized.shape) # torch.Size([8, 3, 32])배치에 마스크 적용
섹션 제목: “배치에 마스크 적용”# 배치: (4, 5, 6) — 4개 샘플, 5행, 6열data = torch.randn(4, 5, 6)
# 마스크: (5, 6) — 배치 전체에 동일하게 적용mask = torch.randint(0, 2, (5, 6)).bool()
print(data.shape) # torch.Size([4, 5, 6])print(mask.shape) # torch.Size([5, 6])
# 마스크가 (1, 5, 6) → (4, 5, 6) 으로 브로드캐스팅masked = data * mask.float()print(masked.shape) # torch.Size([4, 5, 6])shape 변환 과정 요약 비교
섹션 제목: “shape 변환 과정 요약 비교”| 연산 | A shape | B shape | 브로드캐스팅 후 | 결과 shape |
|---|---|---|---|---|
| 스칼라 더하기 | (5,) | () | B → (5,) | (5,) |
| 배치 정규화 | (4, 3) | (3,) | B → (1,3) → (4,3) | (4, 3) |
| 행 스케일링 | (4, 3) | (4, 1) | B → (4, 3) | (4, 3) |
| 3D 채널 정규화 | (8, 3, 32) | (3, 1) | B → (1,3,1) → (8,3,32) | (8, 3, 32) |
| 외적 패턴 | (3, 1) | (1, 4) | A → (3,4), B → (3,4) | (3, 4) |
브로드캐스팅 결과 미리 확인하기
섹션 제목: “브로드캐스팅 결과 미리 확인하기”실제 연산 전에 결과 shape을 미리 확인할 수 있습니다.
import torch
# 연산 없이 결과 shape만 확인shape = torch.broadcast_shapes((4, 3), (3,))print(shape) # torch.Size([4, 3])
shape = torch.broadcast_shapes((2, 1, 4), (3, 1))print(shape) # torch.Size([2, 3, 4])
# 비호환 shape 확인try: torch.broadcast_shapes((3, 4), (3, 5))except RuntimeError as e: print(f"오류: {e}")핵심 요약
섹션 제목: “핵심 요약”- 스칼라 + 벡터: 스칼라가 벡터 길이만큼 확장
- 행렬 + 벡터: 벡터가
(1, n)형태로 변환 후 행 수만큼 확장 - 3D 텐서: 배치 차원 전체에 동일한 연산을 적용할 때 유용
torch.broadcast_shapes()로 연산 전 결과 shape를 미리 확인 가능
다음 장에서는 브로드캐스팅 사용 시 주의해야 할 함정과 디버깅 방법을 다룹니다.