무효 클릭 IP 추적 중...
머신러닝,딥러닝/넘파이,numpy

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편

꼬예 2021. 4. 16.
[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편

오늘은 넘파이를 사용할때 필수적으로 사용하는 기능인 broadcasting 에 대해서 알아보도록 하겠습니다.

사실 이 녀석은 잘알면 너무나도 편한 기능이지만 애매 하게 알면.. 혼란을 야기하는 녀석이기도 한데요.

왜냐하면 shape에 따라 작동을 안하기도 하고, 또한 차원에 따라 다른 동작을 하거든요!

 

각 차원별로 어떻게 내부적으로 연산을 하는지 그림을 통해서 하나하나 뜯어보도록 하겠습니다. 

 

브로드캐스팅이 어떤 기능인지 낯선 분들을 위해 간단한 설명을 하고 시작하도록 하겠습니다. 

 

 

브로드 캐스팅(broadcasting)

실제 수학에서는 (2,3,4) X 2 = (4,6,8) 이런식으로 가능하죠?

하지만 실제수학에서는 (2,3,4) + (2)에 연산은 가능하지 않습니다. 하지만.. 넘파이에선 가능하죠.

import numpy as np

A = np.array([2,3,4])
B = A + 2
print(B)

output:

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 브로드 캐스팅(broadcasting)

숫자 를 하나 더했을뿐인데 각요소에 알아서 들어간걸 볼수가있죠. 

(2,3,4) + 2 만 했을 뿐인데 자동적으로 (2, 3, 4) + (2, 2, 2) 로 내부적으로 같은 shape을 만들어 계산을 해주는것입니다.

 

예시와 벡터의 갯수가 작을때는 티가 안나지만 데이터가 커지면 커질수록  아주 유용한 기능이 겠지요..?

 

덧셈 연산 뿐만 아니라 다양한 연산이가능 합니다.

import numpy as np

A = np.array([2,3,4])
B = 2
print("a * b: ", A.__mul__(B))
print("a / b: ", A.__truediv__(B))
print("a // b: ", A.__floordiv__(B))
print("a % b: ", A.__mod__(B))
print("a ** b: ", A.__pow__(B))

output:

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 브로드 캐스팅(broadcasting)

 

 

2차원일때 broadcasting

import numpy as np

A = np.arange(9).reshape(3,3)
B = 10*np.arange(3).reshape((1,-1)) # row 자리에 1을 넣어서 row 벡터 형태로 만듬.
C = A + B

print("A : {}/{}\n{}".format(A.ndim, A.shape, A))  # ndim은 몇차원인지 알기 위한 property임.
print("B : {}/{}\n{}\n".format(B.ndim, B.shape, B))
print("A + B: {}/{}\n{}".format(C.ndim,C.shape, C))

output :

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

A, B 둘다 2차원으로 같은 차원인걸 확인할 수있네요!

 

넘파이에서 내부적으로 아래와 같은 식으로 연산을 수행하는데요.

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

연산을 수행하기 위해서 넘파이는 계산하게될 두 데이터값의 shape을 맞춰주는 작업을 합니다.

b의 shape (1,3)으로 A shape과 다르므로 (3,3)으로 변경 시켜줍니다. 변경 시켜줄때 기존에 있는 값을 복사하면서 같은 shape으로 만들어줍니다.

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

shape가 맞춰졌기 때문에 각 위치에 상응하는 값끼리 연산을 수행할 수있습니다.

 

 

이해를 돕기위해 좀 다른 형태로 볼까요?

 

 

import numpy as np

A = np.arange(9).reshape(3,3)
B = 10*np.arange(3).reshape((-1,1)) # 컬럼에 자리에 1을 넣어서 컬럼 형태로 만듬. 
C = A + B

print("A : {}/{}\n{}".format(A.ndim, A.shape, A))
print("B : {}/{}\n{}\n".format(B.ndim, B.shape, B))
print("A + B: {}/{}\n{}".format(C.ndim,C.shape, C))

output:

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

A, B 둘다 2차원으로 같은 차원인걸 확인할 수있네요!

 

마찬가지로 넘파이에서 내부적으로 아래와 같은 식으로 연산을 수행합니다.

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

 

(3,1)의 shape을 가지고 있는 B는 (3,3)의 형태로 바꾸기 위해  기존값을 복사하면서 shape을 맞춥니다. 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

shape가 맞춰졌기 때문에 각 위치에 상응하는 값끼리 연산을 수행할 수 있습니다.

 

여기까지 보시면 어떠한 규칙을 발견하실 수 있을 겁니다. 

앞선 첫번째 예에서 B 의 shape이 (1, 3) -> (3, 3)  1 이 3으로 바뀌었죠?

두번째 예에선 B의 shape이 (3, 1) -> (3,3) 마찬가지로 1이 3으로 바뀌었습니다. 

 

그렇다면..

A 벡터가 (3,1) 의 형태이고 B 벡터가 (1,3)일 형태일때는 어떻게 연산을 수행할까요??

 

 

A벡터에서 1은 column 입니다. B 벡터의 column을 shape을 보니 3이네요.1을 3으로 변경해주면 됩니다.

 

반대로 B백터에서 1은 row입니다. A 벡터의 row의 shape을보니 3이네요.마찬가지로 1을 3으로 변경해주면 됩니다.

 

코드와 그림으로 확인해보겠습니다. 

 

import numpy as np

A = np.arange(3).reshape(-1,1)
B = 10*np.arange(3).reshape((1,-1)) 
C = A + B

print("A : {}/{}\n{}".format(A.ndim, A.shape, A))
print("B : {}/{}\n{}\n".format(B.ndim, B.shape, B))
print("A + B: {}/{}\n{}".format(C.ndim,C.shape, C))

output:

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

 

 

A, B 둘다 2차원으로 같은 차원인걸 확인했습니다.

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

위 그림과 같이 1인 부분을 연산할 상대 데이터의 (row, column)값으로 각각 바꿔주기 위해,

복사를 수행하는걸 볼 수 있습니다.

 

shape이 같아지면 같은 위치에 있는 값끼리 연산을 수행합니다.

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting

 

 

 

3차원일때 broadcasting

3차원일때를 확인해보겠습니다. 

앞서 배운 2차원일때랑 같은 원리가 적용되니 차근차근 확인해보세요!

 

import numpy as np

A = np.arange(18).reshape((2,3,3))
B = 10*np.arange(9).reshape((1,3,3)) 
C = A + B

print("A : {}/{}\n{}".format(A.ndim, A.shape, A))
print("B : {}/{}\n{}\n".format(B.ndim, B.shape, B))
print("A + B: {}/{}\n{}".format(C.ndim,C.shape, C))

Output:

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

이번에도 3차원으로 A , B 둘다 차원이같습니다.

2차원이랑 같은 원리라면

B 의 shape(1,3,3)에서 1인 부분인 A의 (2,3,3)에서 2인 부분으로 바뀌어야 된다는걸 이제 다들 눈치채셨겠죠?

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

A 의 (2,3,3)에서 2는 3x3 행렬이 2개 있다는 의미입니다. 

다시 말하면 B의 (1,3,3)을 (2,3,3)으로 변경해준다는 말은 3x3 행렬을 1개에서 2개로 만들어줘야된다는 뜻이기때문에 위와 같은 연산을 진행하게 됩니다.

 

 

shape이 맞춰진 두 텐서는 아래와 같이 상응하는 위치의 값끼리 연산을 합니다.

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

 

이해를 돕기 위해 다른 예를 들어보겠습니다. 

 

이번엔 A는 (2,3,3) 의 형태이고 B는 (2,1,3)의 형태입니다. 어떻게 연산이 진행될까요?

 

 

import numpy as np

A = np.arange(18).reshape((2,3,3))
B = 10*np.arange(6).reshape((2,1,3)) 
C = A + B

print("A : {}/{}\n{}".format(A.ndim, A.shape, A))
print("B : {}/{}\n{}\n".format(B.ndim, B.shape, B))
print("A + B: {}/{}\n{}".format(C.ndim,C.shape, C))

output:

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

 

차원은 3차원으로 같은걸 확인할 수있습니다.

 

 

마찬가지로 B의 (2,1,3)에서 row 부분이 1에서 3으로 바뀌어야합니다. 

그림으로 확인해볼까요?

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

row가 바뀌어야하니 위와 같은 형태로 복사를 실시합니다. 

2차원에서 배웠던 내용과 비슷하죠? 다만 여기선 3차원으로 행렬이 두개가 있다보니 동시에 두개가 열을 복사하는 것을 볼 수 있습니다. 

 

최종 연산 결과입니다.

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

 

마지막으로  A : (2,3,3) 과 B : (2,3,1) 연산은 어떻게 될까요?

 

import numpy as np

A = np.arange(18).reshape((2,3,3))
B = 10*np.arange(6).reshape((2,3,1)) 
C = A + B

print("A : {}/{}\n{}".format(A.ndim, A.shape, A))
print("B : {}/{}\n{}\n".format(B.ndim, B.shape, B))
print("A + B: {}/{}\n{}".format(C.ndim,C.shape, C))

output:

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

차원은 3차원으로 같습니다. 

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

column이 바뀌어야하니 위와 같은 형태로 복사를 실시합니다.

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting

최종결과는 위와 같습니다. 

 

정리

2차원이든 3차원이든 상관없이 우리는 1을 주목! 하고 그값을 변경하면 모든게 해결되었습니다.

하지만 오늘 배운내용은 A, B 둘의 shape은 달랐지만 차원은 같았습니다.

 

다음 시간에는 차원이 다를경우 어떻게 연산되는지 알아보겠습니다. 

많은 분들이 이둘의 차이를 막연히 이해하고있기때문에 많은 어려움을 겪는데요.

 

1편을 복습하신후! 2편도 꼭 확인해주시기 바랍니다.

broadcasting 2편

 

[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 2편

1편에 이어 2편 시작하겠습니다. [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 오늘은 넘파이를 사용할때 필수적으로 사용하는 기능인 broadcasting 에 대해서 알아보도록 하겠습니다. 사실...

yeko90.tistory.com

 

 

 

이 글과 읽으면 좋은글

 

 

 

 

  • 트위터 공유하기
  • 페이스북 공유하기
  • 카카오톡 공유하기
이 컨텐츠가 마음에 드셨다면 커피 한잔(후원) ☕

댓글