오늘은 넘파이를 사용할때 필수적으로 사용하는 기능인 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) [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 브로드 캐스팅(broadcasting)](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
숫자 를 하나 더했을뿐인데 각요소에 알아서 들어간걸 볼수가있죠.
(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) [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 브로드 캐스팅(broadcasting)](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
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 [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
A, B 둘다 2차원으로 같은 차원인걸 확인할 수있네요!
넘파이에서 내부적으로 아래와 같은 식으로 연산을 수행하는데요.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
연산을 수행하기 위해서 넘파이는 계산하게될 두 데이터값의 shape을 맞춰주는 작업을 합니다.
b의 shape (1,3)으로 A shape과 다르므로 (3,3)으로 변경 시켜줍니다. 변경 시켜줄때 기존에 있는 값을 복사하면서 같은 shape으로 만들어줍니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/d5BW6s/btq2LmsEHeL/bKpwECdA41Sm8LoBb2MyrK/img.png)
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 [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/lLgpm/btq2IMk90Z1/tjx9gP1Zk3cWp6QIRyda31/img.png)
A, B 둘다 2차원으로 같은 차원인걸 확인할 수있네요!
마찬가지로 넘파이에서 내부적으로 아래와 같은 식으로 연산을 수행합니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/bJBpdF/btq2KxgJMnT/dUvGnetpLVAPn4wXeHGLcK/img.png)
(3,1)의 shape을 가지고 있는 B는 (3,3)의 형태로 바꾸기 위해 기존값을 복사하면서 shape을 맞춥니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/zmRFI/btq2HsnqguK/0LwMeWnLLBsqOAWoBDAUK1/img.png)
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 [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/W1FnT/btq2LkuP0Go/GzMuU6gqz3Rh12kdIP4ng0/img.png)
A, B 둘다 2차원으로 같은 차원인걸 확인했습니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/WdhFi/btq2Jk22FRi/ldMExuhVkBhdA8iYtDmwY0/img.png)
위 그림과 같이 1인 부분을 연산할 상대 데이터의 (row, column)값으로 각각 바꿔주기 위해,
복사를 수행하는걸 볼 수 있습니다.
shape이 같아지면 같은 위치에 있는 값끼리 연산을 수행합니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 2차원일때 broadcasting](https://blog.kakaocdn.net/dn/LeNOu/btq2JxgUV0d/eDjbhkFIvrN8XI6KU88G0K/img.png)
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 [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/mKA06/btq2ILGBgpO/QtLk38TGgGl0K3F134pde0/img.png)
이번에도 3차원으로 A , B 둘다 차원이같습니다.
2차원이랑 같은 원리라면
B 의 shape(1,3,3)에서 1인 부분인 A의 (2,3,3)에서 2인 부분으로 바뀌어야 된다는걸 이제 다들 눈치채셨겠죠?
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/cP8uGV/btq2Jl8ID8J/KsK7R5lDuMNVoDABMS7M20/img.png)
A 의 (2,3,3)에서 2는 3x3 행렬이 2개 있다는 의미입니다.
다시 말하면 B의 (1,3,3)을 (2,3,3)으로 변경해준다는 말은 3x3 행렬을 1개에서 2개로 만들어줘야된다는 뜻이기때문에 위와 같은 연산을 진행하게 됩니다.
shape이 맞춰진 두 텐서는 아래와 같이 상응하는 위치의 값끼리 연산을 합니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/FAk2f/btq2JxOLpa5/pdDK9W6HDJDVz4Nk58Eq01/img.png)
이해를 돕기 위해 다른 예를 들어보겠습니다.
이번엔 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 [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/T8OnH/btq2IQVkuUn/RcrmOofNZYfM2ayg2NceYK/img.png)
차원은 3차원으로 같은걸 확인할 수있습니다.
마찬가지로 B의 (2,1,3)에서 row 부분이 1에서 3으로 바뀌어야합니다.
그림으로 확인해볼까요?
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/wRJEF/btq2ILT77Jt/6MDEIsKiIvyH9T003Vx710/img.png)
row가 바뀌어야하니 위와 같은 형태로 복사를 실시합니다.
2차원에서 배웠던 내용과 비슷하죠? 다만 여기선 3차원으로 행렬이 두개가 있다보니 동시에 두개가 열을 복사하는 것을 볼 수 있습니다.
최종 연산 결과입니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/boOdPC/btq2IPvkXGN/mIvTFLPE7iToOhrvqKxHQ1/img.png)
마지막으로 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 [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/bPJcK4/btq2DrXgR2G/DWqrIGSOFbDTs3u7PEraZ0/img.png)
차원은 3차원으로 같습니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/Jc0QV/btq2IhlrSU3/nNUx0aH76S6qFQRB5exbO0/img.png)
column이 바뀌어야하니 위와 같은 형태로 복사를 실시합니다.
![[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 - undefined - 3차원일때 broadcasting](https://blog.kakaocdn.net/dn/b2HM4z/btq2Jx2iKCT/x4ZvKJVKUNJyxJiLyzqAV0/img.png)
최종결과는 위와 같습니다.
정리
2차원이든 3차원이든 상관없이 우리는 1을 주목! 하고 그값을 변경하면 모든게 해결되었습니다.
하지만 오늘 배운내용은 A, B 둘의 shape은 달랐지만 차원은 같았습니다.
다음 시간에는 차원이 다를경우 어떻게 연산되는지 알아보겠습니다.
많은 분들이 이둘의 차이를 막연히 이해하고있기때문에 많은 어려움을 겪는데요.
1편을 복습하신후! 2편도 꼭 확인해주시기 바랍니다.
[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 2편
1편에 이어 2편 시작하겠습니다. [넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 1편 오늘은 넘파이를 사용할때 필수적으로 사용하는 기능인 broadcasting 에 대해서 알아보도록 하겠습니다. 사실...
yeko90.tistory.com
이 글과 읽으면 좋은글
'머신러닝,딥러닝 > 넘파이,numpy' 카테고리의 다른 글
[넘파이 기초] int ndarray로 인덱싱 (indexing) 하기 (0) | 2021.04.22 |
---|---|
[넘파이 기초] broadcasting(브로드 캐스팅) 파헤치기 2편 (0) | 2021.04.17 |
[넘파이 기초] flatten 와 ravel의 차이 | 메모리 관리(.copy() vs .view()) (0) | 2021.04.10 |
[넘파이 기초] np.resize vs np.reshape 시리즈[2] (0) | 2021.04.09 |
[넘파이 기초] np.resize vs np.reshape 시리즈[1] (0) | 2021.04.09 |
댓글