콘텐츠로 이동

브로드캐스팅 실전 예시

가장 단순한 형태입니다. 스칼라(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 + bonus
print(result) # tensor([77., 90., 96., 73., 84.])
# 파이썬 숫자도 동일하게 동작
result2 = scores + 5.0
print(result2) # tensor([77., 90., 96., 73., 84.])

딥러닝에서 가장 자주 만나는 패턴입니다. 배치의 각 샘플에 같은 값을 더하거나 빼는 연산에 해당합니다.

케이스 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 - mean
print(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 * weights
print(scaled.shape) # torch.Size([4, 3])
print(scaled)
# tensor([[ 1., 2., 3.],
# [ 8., 10., 12.],
# [ 3.5, 4., 4.5],
# [30., 33., 36.]])

배치 학습에서 가장 중요한 패턴입니다. 배치 전체에 동일한 변환을 적용할 때 사용합니다.

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_mean
print(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])

연산A shapeB 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를 미리 확인 가능

다음 장에서는 브로드캐스팅 사용 시 주의해야 할 함정과 디버깅 방법을 다룹니다.