앞서 살펴본 단순한 덧셈 구현인 naive_add는 동일한 크기의 2D 텐서만 지원합니다. 하지만 이전에 보았던 Dense 층에서는 2D 텐서와 벡터를 더했습니다. 크기가 다른 두 텐서가 더해질 때 무슨일이 일어날까요?
모호하지 않고 실행 가능하다면 작은 텐서가 큰 텐서의 크기에 맞추어 브로드캐스팅(broadcasting)됩니다. 브로드캐스팅은 두 단계로 이루어집니다.
1. 큰 텐서의 ndim에 맞도록 작은 텐서에(브로드캐스팅 축이라고 부르는) 축이 추가 됩니다.
2. 작은 텐서가 새 축을 따라서 큰 텐서의 크기에 맞도록 반복됩니다.
구체적인 옐르 살펴보겠습니다.
x의 크기는 (32, 10)이고 y의 크기는 (10,)라고 가정합시다. 먼저 y에 비어 있는 첫 번째 축을 추가하여 크기를 (1, 10)으로 만듭니다. 그런 다음 y를 이 축에 32번 반복하면 텐서 Y의 크기는 (32, 10)이 됩니다. 여기에는 Y[i, :] == y for i in range(0, 32)입니다. 이제 X와 Y의 크기가 같으므로 더할 수 있습니다.
구현 입장에서는 새로운 텐서가 만들어지면 매우 비효율적이므로 어떤 2D 텐서도 만들어지지 않습니다. 반복된 연산은 완전히 가상적입니다. 이 과정은 메모리 수준이 아니라 알고리즘 수준에서 일어납니다. 하지만 새로운 축을 따라 벡커가 32번 반복된다고 생각하는 것이 이해하기 쉽습니다. 다음은 단순하게 구현한 예입니다.
def naive_add_matrixa_and_vector(x, y):
assert len(x.shape) == 2 ..... x 는 2D 넘파이 배열입니다.
assert len(y.shape) == 1 .... y는 넘파이 벡터입니다.
assert x.shape[1] == y.shape[0]
x = x.copy() ....... 입력 텐서 자체를 바꾸지 않도록 복사합니다.
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[j]
return x
(a, b, ... n, n + 1, .... m) 크기의 텐서와 (n, n + 1, ,..... m) 크기의 텐서 사이에 브로드 캐스팅으로 원소별 연산을 적용할 수 있습니다. 이때 브로드캐스팅은 a부터 n - 1까지의 축에 자동으로 일어납니다.
다음은 크기가 다른 두 텐서에 브로드 캐스팅으로 원소별 maximum 연산을 적용하는 예입니다.
import numpy as np
x = np.random.random((64, 3, 32, 10)) ..... x 는 (64, 3,32, 10)크기의 랜덤 텐서입니다.
y = np.random.random((32, 10)) ..... y 는 (32, 10) 크기의 랜덤 텐서입니다.
z = np.maximum(x, y) .... 출력 z크기는 x와 동일하게 (64, 3, 32, 10)입니다.
댓글 없음:
댓글 쓰기