Skip to main content

브로드캐스팅

브로드캐스팅(Broadcasting)은 서로 다른 shape의 배열 간 산술 연산을 가능하게 하는 NumPy의 핵심 메커니즘입니다. 명시적으로 배열을 복제하지 않고도 shape이 다른 배열 간 연산을 수행할 수 있어, 메모리 효율성과 코드 간결성을 동시에 얻을 수 있습니다.

학습 목표

  • 브로드캐스팅이 적용되는 조건(호환 규칙)을 설명할 수 있다
  • 브로드캐스팅의 3단계 규칙을 적용하여 연산 결과의 shape을 예측할 수 있다
  • 실무에서 브로드캐스팅을 활용하여 효율적인 배열 연산을 작성할 수 있다

왜 중요한가

ML에서는 피처 행렬의 각 열에 서로 다른 스케일링을 적용하거나, 배치 데이터에 동일한 변환을 적용하는 등 shape이 다른 배열 간 연산이 빈번합니다. 브로드캐스팅을 이해하면 반복문 없이 이 모든 연산을 한 줄로 처리할 수 있습니다.

브로드캐스팅 규칙

두 배열의 shape을 뒤쪽(오른쪽) 차원부터 비교하며, 다음 세 가지 규칙을 순서대로 적용합니다.

규칙 요약

규칙설명예시
규칙 1차원 수가 다르면, 적은 쪽 앞에 크기 1인 차원 추가(3,) -> (1, 3)
규칙 2각 차원의 크기가 같거나, 한쪽이 1이면 호환(3, 1) + (1, 4) -> 호환
규칙 3크기가 1인 차원은 상대방 크기에 맞게 확장(3, 1) -> (3, 4)

예시로 이해하기

import numpy as np

# 예시 1: 스칼라 + 배열
arr = np.array([1, 2, 3])
result = arr + 10         # 스칼라 10이 [10, 10, 10]으로 확장
print(result)             # [11 12 13]

# 예시 2: 1D + 2D
# (3,)  →  (1, 3)  →  (2, 3)
# (2, 3)              (2, 3)
a = np.array([1, 2, 3])          # shape: (3,)
b = np.array([[10, 20, 30],
              [40, 50, 60]])     # shape: (2, 3)
print(a + b)
# [[11 22 33]
#  [41 52 63]]

# 예시 3: 열 벡터 + 행 벡터
col = np.array([[1], [2], [3]])  # shape: (3, 1)
row = np.array([10, 20, 30])    # shape: (3,) → (1, 3)
print(col + row)
# [[11 21 31]     (3, 1) + (1, 3) → (3, 3)
#  [12 22 32]
#  [13 23 33]]

호환되지 않는 경우

a = np.array([[1, 2, 3],
              [4, 5, 6]])    # shape: (2, 3)
b = np.array([1, 2])         # shape: (2,) → (1, 2)

# (2, 3) vs (1, 2) — 마지막 차원 3 != 2, 둘 다 1이 아님
# a + b  # ValueError: operands could not be broadcast together

실무 활용 패턴

피처 정규화 (Z-score)

# 피처 행렬: 100개 샘플, 5개 피처
X = np.random.randn(100, 5)

# 각 피처의 평균과 표준편차 (열 방향 집계)
mean = X.mean(axis=0)  # shape: (5,)
std = X.std(axis=0)    # shape: (5,)

# 브로드캐스팅: (100, 5) - (5,) → (100, 5) - (1, 5) → (100, 5)
X_normalized = (X - mean) / std
print(X_normalized.mean(axis=0))  # 거의 0에 가까운 값 5개
print(X_normalized.std(axis=0))   # 거의 1에 가까운 값 5개

거리 행렬 계산

# 3개 점의 2D 좌표
points = np.array([[0, 0], [3, 4], [1, 1]])  # shape: (3, 2)

# 모든 점 쌍의 거리 계산 (브로드캐스팅 활용)
# (3, 1, 2) - (1, 3, 2) → (3, 3, 2)
diff = points[:, np.newaxis, :] - points[np.newaxis, :, :]
distances = np.sqrt(np.sum(diff**2, axis=2))
print(distances)
# [[0.    5.    1.414]
#  [5.    0.    3.606]
#  [1.414 3.606 0.   ]]

원-핫 인코딩

# 클래스 레이블
labels = np.array([0, 2, 1, 0, 3])
num_classes = 4

# 브로드캐스팅으로 원-핫 인코딩
# (5, 1) == (1, 4) → (5, 4) 불리언 행렬
one_hot = (labels[:, np.newaxis] == np.arange(num_classes)).astype(int)
print(one_hot)
# [[1 0 0 0]
#  [0 0 1 0]
#  [0 1 0 0]
#  [1 0 0 0]
#  [0 0 0 1]]

AI/ML에서의 활용

  • 배치 정규화: 배치 내 각 피처의 평균/분산으로 정규화할 때 브로드캐스팅이 핵심입니다
  • 손실 함수 계산: 예측값과 실제값의 차이를 배치 전체에 대해 한 번에 계산합니다
  • 어텐션 메커니즘: 쿼리와 키 벡터 간 유사도 계산에 브로드캐스팅이 활용됩니다
  • 데이터 증강: 전체 데이터셋에 동일한 변환(평균 빼기, 스케일링 등)을 적용합니다
아닙니다. 브로드캐스팅은 실제로 배열을 복제하지 않습니다. 내부적으로 stride 트릭을 사용하여 마치 복제된 것처럼 연산합니다. 따라서 np.tile()이나 np.repeat()로 명시적으로 복제하는 것보다 훨씬 메모리 효율적입니다.
np.newaxisreshape()를 사용하여 한쪽 배열에 크기 1인 차원을 추가하세요. 예를 들어 (3,) shape의 배열을 (3, 1)로 바꾸면 (3, 4) 배열과 연산할 수 있습니다.

체크리스트

  • 브로드캐스팅의 3단계 규칙을 설명할 수 있다
  • 두 배열의 shape을 보고 브로드캐스팅 가능 여부를 판단할 수 있다
  • 연산 결과의 shape을 예측할 수 있다
  • np.newaxis를 활용하여 차원을 추가하고 브로드캐스팅을 유도할 수 있다
  • 피처 정규화, 거리 행렬 등 실무 패턴에 브로드캐스팅을 적용할 수 있다

다음 문서