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

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

꼬예 2021. 4. 17.

 

1편에 이어 2편 시작하겠습니다. 

 

 

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

오늘은 넘파이를 사용할때 필수적으로 사용하는 기능인 broadcasting 에 대해서 알아보도록 하겠습니다. 사실 이 녀석은 잘알면 너무나도 편한 기능이지만 애매 하게 알면.. 혼란을 야기하는 녀석

yeko90.tistory.com

2편에서는 연산하는 두 값이 서로 다른 차원일때 내부적으로 어떻게 연산이 되고

무엇을 주의해야하는지 알아보도록 하겠습니다. 

 

 

 

스칼라값과 연산

import numpy as np

a = np.array(3) # 스칼라값 =  0차
u = np.arange(5) # 1차 

print("shapes: {}/{}".format(a.shape, u.shape))
print("a: ", a)
print("u: ", u, '\n')
print("a+u: ", a+u)

output:

a 는 스칼라값으로 0차원이고 u는 5개의 원소를 가진 1차원 배열 입니다. 

1편에서 주로다루었던 것과 달리 차원이 다른 두개의 값을 연산하는데요.

 

1편에서는 shape의 1을 신경썼으면 되었는데 여기선 뭘 신경 써야할까요?

 

사실 스칼라값의 경우는 신경쓸 필요가 없습니다.

아무렇게나 해도 상관 없습니다.

 

그렇기때문에 여기선 논외로 하고 본격적으로 시작하도록 하겠습니다.

 

 

broadcasting이 가능한 경우 vs 불가능한 경우

차원이 다를 경우 브로드캐스팅은 연산하는 두 수간의 차원이 중요합니다.

 

우선 제가 미리 가능한경우와 불가능한경우의 shape이 있는 위의 표를 보여드리겠습니다.

가능한경우 그리고 불가능한 경우 어떤 규칙성이 있는것 같나요?

고민한 후 답을 해보시기 바랍니다.

 

정답은..

차원이 작은 쪽의 shape이 우측 정렬한다고 했을때 완전히 포개어지는가(?)로 확인해볼 수 있습니다. 

구체적으로 예를 들자면

 

(4,3,4) 와 (3,4)의 관계에선 (3,4)가 더 작은 차원입니다. 우측 정렬하면 (4,3,4)  에 포개어지는걸 알수있죠.

원론적으로 말하자면 (4,3,4)는 (3,4)로 이루어진 행렬이다 라는 말입니다.

1편에서도 설명드렸듯이 (4,3,4)는 (3,4)의 행렬의 4개 있다는 말이니까 당연한 말이죠.

 

반면 (4,3,4)와 (5,4)를 비교해볼까요? 우측정렬이 안되죠? 그말은 (3,4)가 4개있는 (4,3,4)는 (5,4)로 만들어질 수 없습니다.

즉 브로드캐스팅이 불가능하다는 뜻이지요.

 

이해를 돕기 위해 그림으로 재확인하며 다시 짚어 보겠습니다.

 

1. (4,) <-> (4,3,4)

 

차수가 낮은 (4,)가 (4,3,4) 우측에 정확히 포개지는걸 확인할수 있습니다. 즉 (4,3,4)는 (4,)로 구성됩니다.

====> broadcasting 가능

 

2. (1,3,2,4) <-> (1,3,2)

차수가 낮은 (1,3,2)가 오른쪽 정렬해서 (1,3,2,4)로 놓았을때 서로 같지 않습니다.

즉 (1,3,2,4) 는 (1,3,2)로 만들어질 수 없다는 것을 의미하죠.

=====> broadcasting 불가능

 

 

어떤 shape일때 브로드캐스팅이 가능한지 불가능한지 알아보았으니 이번에는 어떻게 연산이 진행되는지 코드를 통해서 확인해보겠습니다.

 

3차원 + 2차원 연산

import numpy as np

A = np.arange(2*3*4).reshape((2,3,4))
B = 10*np.arange(3*4).reshape((3,4))
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:

 

A는 3차 B는 2차로 서로 다른 차원입니다.  

작은 차원이 (3,4) 이고 큰차원이 (2,3,4) 입니다. 둘의 관계를 봤을때 브로드캐스팅이 가능한걸 확인할 수있겠죠?

그렇다면 어떻게 연산을 진행할까요?

 

브로드캐스팅을 하기 위해선 shape을 맞춰줘야 합니다. 이때 (2,3,4)는 (3,4)가 두개있는것이기 때문에 그대로 2개로 만들어주면됩니다.

 

그후 각각 요소별 연산 즉 element-wise 연산을 실시합니다.

 

 

 

3차원 + 1차원 연산

import numpy as np

A = np.arange(2*3*4).reshape((2,3,4))
B = 10*np.arange(4)
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))

A는 3차원 그리고 B는 1차원으로 (2,3,4) (4,) 의 shape을 가진 배열입니다.

형태를 봤을때 둘은 당연히 브로드캐스팅 가능하고요, 어떻게 연산이 되는지 확인해보겠습니다.

 

우선 브로드캐스팅을 위해서 A ,B 의 shape이 같아야합니다. 

(2,3,4)는 (4,)를 기준으로 봤을때

(4,)가 3개 있는것이 (3,4) 이고 이 (3,4)가 2개 있는것으로 표현할 수 있습니다. 

다시말하면 (4,)를 3개 를 만든후 그만든것을 두개 만들어주면 A와 같은 형태를 만들 수 있는 것이지요.

 

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

 

shape이 맞춰진 A,B 는 element-wise 연산을 수행합니다. 

 

지금까지는 되는 상황만 전달드렸으니 브로드 캐스팅 연산이 안되는 경우도 확인해볼까요?

import numpy as np

A = np.arange(2*3*4).reshape((2,3,4))
B = 10*np.arange(5) #기존코드에서 (5,) 의 형태로 변경
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))

기존코드 (2,3,4)과 (4,)에서 (4,) -> (5,) 형태로 변경했습니다. 

앞에서 배운것으로 봤을때 둘은 연산 불가이죠? 결과도 그럴까요?

네 그렇습니다. 보시다시피 ValueError: operands could not be broadcast together with shapes 라는 문구가 뜨면서 친절하게 shape이 안맞다고 알려주네요.

 

정리

지금까지 차원이 같을경우와 차원이 다를경우 브로드캐스팅이 언제 가능한지 또 내부적으로 어떤 연산을 수행하는지 알아보았습니다. 

둘은 비슷한거같으면서도 다르기때문에 헷갈리기 쉬우니 다시 한번 복습하시길 추천드립니다.

 

 

 

이 글과 읽으면 좋은글

 

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

댓글