내적(Inner product)
a, b 두 벡터의 내적(inner product, dot product)은 식 1과 같이 정의합니다.
\begin{align}a&=\begin{bmatrix} a_1\\a_2\end{bmatrix}\; b=\begin{bmatrix} b_1\\b_2\end{bmatrix}\\ a\cdot b& = a_1\times b_1 + a_2\times b_2\end{align} | (식 1) |
식 1의 결과와 같이 두 벡터의 내적은 스칼라입니다. 같은 인덱스를 가진 성분들사이의 곱들의 총합으로 식 2와 같이 나타낼 수 있습니다. 행렬은 두 개 이상의 벡터들로 구성된 객체로 두 행렬 사이에 내적을 계산할 수 있습니다. 행렬들 사이에 이루어지는 내적을 행렬곱(matrix product)라고 하지만 구분없이 닷곱, 내적곱이라고 명명합니다.
\begin{align}\text{dot prodcut}:&\;\begin{bmatrix} a_1& a_2\end{bmatrix}\begin{bmatrix} b_1\\b_2\end{bmatrix}= a_1 b_1 + a_2 b_2\\\text{matrix product}:&\; \begin{bmatrix} a_{11}& a_{12}\\a_{21}& a_{22}\end{bmatrix}\begin{bmatrix} b_{11}& b_{12}\\b_{21}& b_{22}\end{bmatrix}=\begin{bmatrix} a_{11}b_{11}+a_{12}b_{21} & a_{11}b_{12}+a_{12}b_{22} \\a_{21}b_{11}+a_{22}b_{21} & a_{21}b_{12}+a_{22}b_{22} \end{bmatrix}\end{align} | (식 2) |
식 2의 두 행렬의 내적 연산은 앞 객체의 행과 뒤 객체의 열의 사이에서 연산이 이루어집니다. 행렬의 행과 열을 각각 r과 c로 표시하면 표 1에 나타낸 것과 같이 앞 행렬(행렬 1)와 뒤 행렬(행렬 2)의 모양은 각각 r1 × c1 그리고 r2 × c2입니다. 그러므로 행렬곱의 결과는 r1 × c2의 모양을 갖습니다.
행렬 1 | · | 행렬 2 | ⇒ | 결과 |
(r1 × c1) | · | (r2 × c2) | ⇒ | (r1 × c2) |
행렬곱은 numpy.dot(x, y) 함수 또는 x@y와 같이 연산자 @
를 사용하여 계산합니다.
a=np.array([[1,2]]) b=np.array([[1],[2]]) print(f'a의 형태: {a.shape}, b의 형태: {b.shape}')
a의 형태: (1, 2), b의 형태: (2, 1)
a[0,0]*b[0,0]+a[0,1]*b[1,0]
5
print(a.dot(b))#=np.dot(a, b)
[[5]]
print(a@b)
[[5]]
예 1)
다음 두 벡터에 대해 u · u, v · u를 계산해 봅니다.
u = [-3 8] v = [7 3]
u=np.array([-3, 8]) v=np.array([7,3]) uu=np.dot(u, u) uv=u@v uu, uv
(73, 3)
벡터의 경우 각 요소는 1개의 인덱스만을 가집니다. 즉, 한 방향을 고려하므로 행렬곱 연산시 전치과정을 고려할 필요가 없습니다. 그러나 행렬의 경우 각 요소는 행과 열 인덱스들을 가지므로 행렬곱의 선행조건인 앞 행렬의 열과 뒤 행렬의 행의 수를 같게 만들기 위해 전치과정이 요구됩니다.
numpy의 배열객체에서 벡터는 한 개의 행으로 표시됩니다. 다시 말하면 한 개의 대괄호 내에 모든 성분을 표현하므로 열벡터 또는 행벡터를 구분할 수 없습니다(벡터의 기본은 열벡터입니다.). 예 1의 벡터 u, v와 다르게 예 2의 U, V는 1개의 외부 대괄호에 2개의 내부 대괄호를 포함하는 행렬이므로 행렬곱을 위해 행렬의 행과 열을 교환하는 전치과정이 필요합니다.
예 2)
다음 두 행렬에 U, V에 대해 내적 U · U, V · U를 계산해 봅니다.
$$U=\begin{bmatrix}-3\\8 \end{bmatrix}\quad V=\begin{bmatrix}7\\3 \end{bmatrix}$$
U=np.array([[-3], [8]]) V=np.array([[7],[3]]) U.shape == V.shape
True
위 결과와 같이 두 행렬 U와 V의 형태는 같습니다. 벡터의 내적은 스칼라, 즉 크기를 나타냅니다. 행렬곱 역시 하나의 행과 하나의 열로 계산된 두 행렬의 크기를 나타내므로 다음 U·U와 U·V의 결과 역시 스칼라 이어야 합니다. 그러기 위해서는 앞 행렬과 뒤 행렬은 각각 1행과 1열로 구성되어야 합니다. 결과적으로 모두 2 × 1의 형태이므로 앞 행렬의 형태를 1 × 2로 만들기 위해 전치시켜야 합니다.
UU=np.dot(U.T, U) print(UU)
[[73]]
UV=U.T@V print(UV)
[[3]]
내적은 식 3과 같이 두 벡터의 사잇각을 적용하여 계산할 수 있습니다. 역으로 두 벡터 u, v 사이의 각은 각 벡터의 노름(norm)과 내적을 사용하여 계산할 수 있습니다.
\begin{align}u\cdot v & = \Vert{u}\Vert \Vert{v}\Vert \cos(\theta)\\ \cos(\theta) &= \frac{u\cdot v}{\Vert{u}\Vert \Vert{v}\Vert}\end{align} | (식 3) |
그림 1은 두 벡터 a, b의 사이각 θ를 나타낸 것입니다.
x=np.linspace(0.5, 3, 100) fig, ax=plt.subplots(figsize=(2,2)) ax.arrow(0,0, 2,1, lw=4, color="brown", head_width=0.05) ax.arrow(0,0, 4,2, color="b", head_width=0.05) ax.arrow(0,0, 1, 3, lw=2, color="r", head_width=0.05) ax.plot(x, -2*x+5, ls="--") ax.spines['left'].set_position(("data", 0)) ax.spines['bottom'].set_position(("data", 0)) ax.spines['right'].set_visible(False) ax.spines['top'].set_visible(False) ax.set_xticks([1,2,3,4]) ax.set_yticks([1,2,3,4]) plt.text(1.2, 0.9, r"$90^o$", color="g") plt.text(0.2, 0.4, r"$45^o$", color="g") plt.show()
식 3을 적용하여 두 벡터 a와 b의 내적으로 계산하면 다음과 같습니다.
a=np.array([4,2]) b=np.array([1,3]) theta=45 rad=np.radians(theta) rad.round(2)
0.79
위 코드에서 사용한 np.radians() 함수는 각(degree)을 라디안으로 변환합니다. 이 변환은 코사인 값을 계산하는 함수 np.cos()의 인수를 전달하기 위한 것입니다. numpy 패키지는 cos() 뿐만 아니라 모든 삼각함수 기본 형태를 제공하며 이 함수들의 인수는 각도가 아닌 라디안(radian)입니다.
inner=la.norm(a)*la.norm(b)*np.cos(rad) inner.round(2)
10.0
위 결과는 두 벡터의 내적(a·b)과 같은 결과를 반환합니다.
a@b
10
그림 1에서 벡터 aproj는 벡터 a위로 벡터 b의 수직 투영된 부분을 나타낸 것입니다. 이를 정사영(orthogonal projection) 벡터라고 합니다. 다음 코드 결과와 같이 두 벡터 a와 aproj의 노름의 곱은 벡터 a와 b의 내적과 같습니다.
a_proj=np.array([2,1]) inner2=la.norm(a)*la.norm(a_proj) inner2.round(2)
10.0
위 관계는 식 4와 같이 정리할 수 있습니다.
\begin{align}\cos(\theta) & = \frac{\Vert{a_{proj}}\Vert}{\Vert{b} \Vert}\\ a\cdot b& =\Vert{a} \Vert \Vert{b} \Vert \cos(\theta)\\ & = \Vert{a} \Vert\Vert{b} \Vert \frac{\Vert{a_{proj}}\Vert}{\Vert{b} \Vert}\\ &= \Vert{a}\Vert \Vert{a_{proj}\Vert} \end{align} | (식 4) |
결과적으로 벡터의 내적은 벡터 a의 노름과 그 위로 벡터 b의 정사영 벡터 aproj의 놈의 곱을 의미합니다.
벡터의 내적은 벡터의 놈과 그 벡터위로 투영된 정상영 벡터의 놈의 곱
예 3)
벡터 a, b 사이에 사잇각을 계산합니다.
a = [-2, 1], b = [-3, 1]
a=np.array([-2,1]) b=np.array([-3, 1]) ab=a@b cos=ab/(la.norm(a)*la.norm(b)) print(cos.round(3))
0.99
rad=np.arccos(cos) print(rad.round(3))
0.142
deg=np.rad2deg(rad) deg.round(3)
8.13
위 과정에서 θ는 np.arccos(), np.rad2deg() 함수를 사용하여 식 5와 같이 역삼각함수에 의한 결과입니다.
x = cos(θ) ⇒ cos-1(x) = θ | (식 1.3.5) |
cos-1(x)는 numpy 모듈의 arccos()
함수로 계산됩니다. numpy 패키지의 역삼각함수는 삼각함수에 접두어 arc를 결합하여 사용합니다. 그 결과는 라디안 값이므로 다시 각으로 환원해야 합니다. 이 환원 과정은 np.rad2deg()
함수에 의해 실행됩니다.
댓글
댓글 쓰기