이번 포스팅에서는 값 배열에 가중치 배열을 곱해서 합한 가중합(weighted sum)을 구하는 3가지 방법을 소개하겠습니다. 


a 를 가중치, b 를 값 배열이라고 했을 때, 


(1) 내적을 이용한 가중합 계산: np.dot(a, b) or np.matmul(a, b)

(2) 브로드캐스팅(broadcasting)을 이용하여 가중치와 값을 원소끼리 곱한 후 합하는

     np.sum(a.reshape(5, 1) * b, axis=0)

(3) repeat()로 가중치를 값 배열 1축만큼 반복 생성한 후, 가중치와 값의 원소끼리 곱한 후 합하는, 

     np.sum(a.reshape(5, 1).repeat(3, axis=1) * b, axis=0)




먼저, numpy를 import하고, 예제로 사용할 가중치 배열 a와, 값의 행렬 b를 만들어보겠습니다. 



import numpy as np


# weights

a = np.array([0.5, 0.3, 0.1, 0.08, 0.02])


print('a shape:', a.shape)

a shape: (5,)


print(a)

[0.5  0.3  0.1  0.08 0.02]



# values

b = np.arange(15).reshape(5, 3)


print('b shape:', b.shape)

b shape: (5, 3)


print(b)

[[ 0  1  2]

 [ 3  4  5]

 [ 6  7  8]

 [ 9 10 11]

 [12 13 14]]

 




  (1) 내적을 이용한 가중합 계산: np.dot(a, b) 또는 np.matmul(a, b)


가장 편리한 방법은 np.dot() 또는 np.matmul() 메소드를 사용하여 내적(inner prodct, dot product)을 계산하는 것입니다. 이때 가중치 벡터 a 에 대해서는 형태 변환(reshape)을 할 필요가 없이 그대로 사용할 수 있습니다.  



np.dot(a, b)

Out[2]: array([2.46, 3.46, 4.46])


np.matmul(a, b)

Out[3]: array([2.46, 3.46, 4.46])

 




  (2) Broadcasting을 이용하여 가중치와 값을 원소끼리 곱한 후, axis=0으로 합하기


이번에는 위의 (1) 내적을 계산의 각 단계별로 분리해서 순서대로 해보겠습니다. 가중치 a와 값 b의 원소끼리 곱한 후에, axis=0을 기준으로 합할 것입니다. 


먼저, 가중치 a와 값 b를 원소끼리 곱하기 위해 가중치 a의 형태(shape)를 기존의 (5,)에서 a.reshape(5, 1) 을 적용하여 (5, 1) 의 형태로 변환을 해줍니다. 값이 들어있는 배열 b의 형태는 (5, 3) 이므로 가중치 배열 a의 (5, 1) 형태를 값 배열 b에 곱해주면 ==> 서로 형태가 같지 않으므로 numpy 는 가중치 a 배열 (5, 1) 을 (5, 3)으로 자동으로 형태 변환을 시켜서 값 배열 b 의 (5, 3) 형태와 동일하게 맞추어 주어 원소간 곱을 해줍니다. 이러한 기능을 브로드캐스팅(boradcasting) 이라고 합니다. 



# shape of a_rs and b are different

a_rs = a.reshape(5, 1)

print(a_rs.shape)

print(a_rs)

(5, 1)


print(b.shape)

(5, 3)


# multiply using boradcasting of a_rs

a_rs_b_mult = a_rs * b


print(a_rs_b_mult.shape)

(5, 3)


print(a_rs_b_mult)

[[0.   0.5  1.  ]

 [0.9  1.2  1.5 ]

 [0.6  0.7  0.8 ]

 [0.72 0.8  0.88]

 [0.24 0.26 0.28]]



# weighted sum

np.sum(a_rs_b_mult, axis=0)

Out[9]: array([2.46, 3.46, 4.46])



* numpy 배열들의 다른 차원의 배열 간 산술연산 시 Broadcasting 은 아래 포스팅을 참고하세요. 

https://rfriend.tistory.com/287




  (3) repeat()로 가중치를 반복 생성한 후, 가중치와 값을 원소끼리 곱한 후 합하기


위의 (2)번에서는 가중치 배열 a의 형태를 바꾼 후의 a_rs 배열과 값 b 배열을 곱할 때, 사람 눈에는 보이지않게 numpy가 알아서 자동으로 가중치 a_rs 배열 (5, 1) 형태를 브로드캐스팅(broadcasting)을 해주어서 (5, 3) 형태로 만들어서 원소끼리 곱해주었습니다. 




반면에, 이번 (3)번에서는 사람이 repeat(n, axis) 메소드를 사용해서 명시적으로 배열을 n번 만큼 axis 축을 기준으로 반복해주어서 (2)번의 브로드캐스팅의 역할을 수행해주는 것입니다. 


구현 관점에서 보면 브로드케스팅이 편리한 장점이 있고, 반면에 repeat() 메소드로 명시적으로 기입을 해주면 코딩하는 사람이 이해하기 쉬운 장점이 있습니다. 



# match the shape of a and b by repeatition 

a_rs_rp = a.reshape(5, 1).repeat(3, axis=1)


print(a_rs_rp.shape)

(5, 3)


print(a_rs_rp)

[[0.5  0.5  0.5 ]

 [0.3  0.3  0.3 ]

 [0.1  0.1  0.1 ]

 [0.08 0.08 0.08]

 [0.02 0.02 0.02]]



# multiplication of a_rs_rp and b per each elements

a_rs_rp_b_mult = a_rs_rp * b


print(a_rs_rp_b_mult.shape)

(5, 3)


print(a_rs_rp_b_mult)

[[0.   0.5  1.  ]

 [0.9  1.2  1.5 ]

 [0.6  0.7  0.8 ]

 [0.72 0.8  0.88]

 [0.24 0.26 0.28]]



# weighted sum

np.sum(a_rs_rp_b_mult, axis=0)

Out[17]: array([2.46, 3.46, 4.46])

 



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



반응형
Posted by Rfriend

댓글을 달아 주세요

지난번 포스팅에서는 벡터의 기본 개념과 벡터의 합, 차, 스칼라배에 대해서 알아보았습니다.


이번 포스팅에서는 2가지의 벡터의 곱중에서 먼저 내적(inner product, dot product, scalar product,  projection product)을 소개하고, 다음번 포스팅에서 외적(outer product, cross product, vector product, directed area product)에 대해서 다루도록 하겠습니다.



[ 벡터 곱 2가지 종류 (2 types of Vector Multiplication) ]



내적(inner product, dot product)과 외적(outer product, cross product)을 비교해보면 아래 표와 같은데요, 처음에 보면 무슨 뜻인가 이해가 안갈 수도 있겠습니다. 내적과 외적의 설명을 다 읽어보시고 마지막으로 종합해서 다시 한번 정리하는데 활용하면 좋겠습니다.



[ 벡터의 내적과 외적 비교 ]

(comparison between inner(or dot) product and outer(or cross) product of vector)






이제 내적(inner product)에 대해서 하나씩 설명해보도록 하겠습니다.



(1) 내적의 표기 (notation and symbol of the inner product)


두 벡터의 내적(inner product)은 아래 그림처럼 「.」(dot)으로 표기하며, 이래서 점곱(dot product)이라고도 말합니다.  혹은 (a, b)와 같이 표기하기도 합니다.  결과값이 스칼라이기 때문에 스칼라곱(scalar product)이라고도 하며, 계산할 때 한쪽 벡터의 코사인값을 사용하기 때문에 (즉, 한쪽 벡터에 직사광선을 쪼였을 때 그 그림자에 해당하는 코사인값을 사용) 영사곱(projection product)이라고도 말합니다.


똑같은 개념을 두고 왜 이리 표현하는 말들이 많은지 의아할 수도 있겠는데요, 대수학, 기하학, 물리학 등의 학문영역별로 명칭, 표기가 다르다는 정도만 이해하면 되겠습니다.  최적화에서도 민감도분석할 때 보면 수학자는 쌍대변수(dual variable)이라고 말하는데 경제학자들은 잠재가격(shadow price)라고 말하거든요.  헛갈리는데 다 같은 말입니다. ^^;







(2) 내적의 정의 (difinition of the inner product)


R^n 내의 두 열 벡터 a, b에 대하여 곱 T(a)b 의 결과인 단일 성분을 갖는 1 * 1 행렬, 즉 하나의 실수인 Scalar 가 내적(inner product)이 되겠습니다.  그 수식을 풀어써보면 아래와 같은 계산식이 되며, 그 output은 스칼라(Scalar)가 됩니다. 


참고로, 외적(outer product)은 output이 벡터(Vector)가 됩니다. 




아래에 두개의 성분(2차원, 2 dimension)을 가지는 열벡터 a=(-5, 6), b=(3, 9)의 내적 예와 세 개의 성분(3차원, 3 dimensions)을 가지는 열벡터 c=(5, 3, 6), d=(2, 7, 4)에 대한 내적 계산을 소개하였습니다.  같은 행의 component끼리 각각 곱해서 모두 더하면 됩니다.







위의 2개의 문제를 R의 '%*%' 함수를 사용해서 풀어보면 아래와 같습니다. 

위에서 소개한 공식을 가지고 아래에 사용자정의 함수도 한번 만들어봤습니다.  R에 내적계산을 위한 함수로 '%*%'가 있기 때문에 굳이 사용자정의함수를 사용할 필요는 없겠습니다만, 개념을 이해하는 차원에서 참고하시기 바랍니다.



> ##--------------------------------------
> ## multiplying a vector by a vector
> ##--------------------------------------
> 
> 
> ##---------
> ## (1) dot product
> 
> ## vector with 2 components (dimensions)
> a <- c(-5, 6)
> b <- c(3, 9)
> 
> # dot product of vector with 2 components (dimensions) : (a) %*%
> a %*% b
     [,1]
[1,]   39
> 
> 
> # dot product of vector with 2 components : (b) user defined function
> dot_prod_fun_v2 <- function(a, b) {
+   if(length(a)!=2 | length(b) !=2) stop('number of vector component is not 2')
+   d <- a[1]*b[1] + a[2]*b[2]
+   return(d)
+ }
> 
> dot_prod_fun_v2(a=a, b=b)
[1] 39
> 
> 
> 
> ## vector with 3 components (dimensions)
> c <- c(5, 3, 6)
> d <- c(2, 7, 4)
> 
> # dot product of vector with 3 components (dimensions) : (a) %*%
> c %*% d
     [,1]
[1,]   55
> 
> 
> # dot prduct of vector with 3 components : (b) user defined function
> dot_prod_fun_v3 <- function(c, d) {
+   if(length(c)!=3 | length(d) !=3) stop('number of vector component is not 3')
+   dot_prd <- c[1]*d[1] + c[2]*d[2] + c[3]*d[3]
+   return(dot_prd)
+ }
> dot_prod_fun_v3(c=c, d=d)
[1] 55
 




(3) 내적의 성질 (properties of the inner product)


수학자들이 정의하는 실내적공간(real inner product space, or real pre-Hilbert space)의 정의와 공리는 아래와 같습니다.  실내적의 공리로 교환법칙(commutative law), 임의의 실수 c곱의 자유로운 이동 가능, 분배법칙(distributive law) 등의 공리도 아래에 소개하였습니다.





(4) 내적의 계산 원리, 방법 1 (1st formula of the inner product calculation)


내적을 계산하는 원리, 방법중에 벡터를 성분분해해서 각 성분들의 벡터의 길이(length of vector, norm)를 가지고 곱한 후 더하는 방법으로 구하는 방법이 있습니다.  아래 그림에 벡터 a를 ax, ay로, 벡터 b를 bx, by로 분해한 후에 내적을 구하는 방법을 소개해 보겠습니다. 힘과 방향을 가지는 벡터를 분해할 수 있다는 것은 벡터 연산을 할 때 굉장히 중요한 개념입니다. 참고로, 벡터와 스칼라를 곱했을 때도 분해한 벡터 성분에 스칼라를 각각 구해서 구하게 됩니다.






(5) 내적의 계산 원리, 방법 2 (2nd formula of the inner product calculation)


내적을 계산하는 또 한가지 방법은 벡터의 힘의 크기 또는 길이(magnitude or length of vector, norm)와 각도(angle between vector a and b)를 이용하는 방법입니다.  벡터의 힘의 크기 또는 길이는 "norm" 이라고도 불립니다.  두 개중 한개의 벡터에 빛을 비추었을 때 직각으로 생기는 그림자 (=vector a * cosine(θ))에다가 나머지 다른 한개의 벡터를 곱하는 개념입니다.





위의 내적 구하는 공식에 cos(θ)가 사용이 되었는데요, 참고로 직각삼각형 ABC의 삼각함수(trigonometric functions) sine, cosine, tangent 구하는 공식은 아래와 같습니다. 





참고로 벡터의 힘의 크기 또는 길이(magnitude or length of vector), 또는 다른 말로 "norm" |a|, |b|를 구하는 방법은 아래와 같습니다. 





(6) 내적을 활용한 두 벡터의 각도 계산 (calculating the angle between vector a and b)


위에서 소개한 두 벡터의 내적을 구하는 공식을 사용하면 벡터의 길이(length of vector)를 구하거나 혹은 두 벡터간 각도(angle between vector a and b)를 구할 수 있습니다.  왜 내적을 공부해야 하지, 내적을 어디에 써먹을 수 있지 궁금하셨을 것 같은데요, 위에서 예로 들었던 두 벡터 a=(-5, 6), b=(3, 9)의 각도를 내적을 활용해서 구해보도록 하겠습니다.



참고로, 위 계산 절차의 제일 마지막에 cos^-1(0.526354)*57.3 을 했는데요, inverse(cos(0.526354))를 통해서 계산된 값이 degree 가 아니라 radian 이기 때문에 1 radian 은 약 57.3 이므로 이를 곱해준 것입니다.  degree는 우리가 일반적으로 사용하는 것처럼 원을 0~360도 표기하는 방법이구요, radian은 부채꼴의 호의 길이와 반지름의 길이가 같게 되는 각도를 1 radian이라고 합니다.
180 degree = π radian 이며, 1 radian = 180 degree/ π = 57.3 입니다.

이를 R을 사용해서 계산해보면 아래와 같습니다.  사용자정의함수로 작성했습니다. 참고로, 함수 acos()는 cos()의 inverse 함수입니다.

> # angle between vector a and b
> a <- c(-5, 6)
> b <- c(3, 9)
> 
> angle_theta <- function(a, b){
+   dot.prod <- a%*%b 
+   norm.a <- norm(a, type="2")
+   norm.b <- norm(b, type="2")
+   theta_radian <- acos(dot.prod / (norm.a * norm.b))
+   angle_theta <- 57.3*theta_radian
+   as.numeric(angle_theta)
+   
+   return(angle_theta)
+ }
> 
> angle_theta(a, b)
         [,1]
[1,] 58.24481

 



위의 예에서는 이해를 돕기 위해서 2차원 (구성요소가 2개) 벡터 a, b를 가지고만 설명을 했는데요, 내적은 실벡터공간 내 3차원, 4차원, ..., n차원 벡터 모두에 대해서 가능합니다.  반면 외적은 3차원 유클리디안 공간에서만 가능합니다.


다음 번에는 벡터의 곱 두번째로 외적 (outer product, cross product, vector product, tensor product) 에 대해서 소개하도록 하겠습니다.

많은 도움되었기를 바랍니다.

행렬, 벡터 관련 포스팅은 아래 링크를 걸어놓았습니다.

 

행렬 기본 이해

특수한 형태의 행렬

가우스 소거법을 활용한 역행렬 계산

행렬의 기본 연산 (+, -, *, /, ^, %*%, colMeans(), rowMeans(), colSums(), rowSums()) 

벡터의 기본 이해와 연산 (vector: addition, subtraction, multiplication by scalar)

벡터의 곱 (2) 외적 (outer product, cross product, vector product, tensor product)

 

이번 포스팅이 도움이 되었다면 아래의 공감 ♡ 꾸욱~ 눌러주세요. ^^

 





반응형
Posted by Rfriend

댓글을 달아 주세요

  1. 꽃경남 2017.04.27 11:23 신고  댓글주소  수정/삭제  댓글쓰기

    많이 배우고 갑니다!

  2. 임태준 2017.05.08 12:35  댓글주소  수정/삭제  댓글쓰기

    학생인데 정말 유용한 공간이군요 친절한 설명 감사합니다. 무슨 일을 하시길래 이렇게 자세한 내용정리가 가능한거죠 ??;;

    • Rfriend 2017.05.08 16:38 신고  댓글주소  수정/삭제

      안녕하세요 임태준 님, 블로그 좋게 봐주셔서 감사합니다.
      저는 데이터 분석 업무하고 있는 직장인이예요. 틈틈히 공부하면서 정리하고 있는데요, 도움이 되었다니 저도 기쁘네요.
      댓글 감사합니다. ^^