기본 콘텐츠로 건너뛰기

[Linear Algebra] 직교적 투영(Orthogonal Projection)

[Linear Algebra] 내적(inner product)

내적(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 행렬곱의 형태
행렬 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

위 결과와 같이 두 행렬 UV의 형태는 같습니다. 벡터의 내적은 스칼라, 즉 크기를 나타냅니다. 행렬곱 역시 하나의 행과 하나의 열로 계산된 두 행렬의 크기를 나타내므로 다음 U·UU·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의 사이각 θ를 나타낸 것입니다.

그림 1. 두 벡터의 사잇각과 내적.
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을 적용하여 두 벡터 ab의 내적으로 계산하면 다음과 같습니다.

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) 벡터라고 합니다. 다음 코드 결과와 같이 두 벡터 aaproj의 노름의 곱은 벡터 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()함수에 의해 실행됩니다.

이 블로그의 인기 게시물

유사변환과 대각화

내용 유사변환 유사행렬의 특성 대각화(Diagonalization) 유사변환(Similarity transformation) 유사변환 n×n 차원의 정방 행렬 A, B 그리고 가역 행렬 P 사이에 식 1의 관계가 성립하면 행렬 A와 B는 유사하다고 하며 이 변환을 유사 변환 (similarity transformation)이라고 합니다. $$\begin{equation}\tag{1} A = PBP^{-1} \Leftrightarrow P^{-1}AP = B \end{equation}$$ 식 1의 유사 변환은 다음과 같이 고유값을 적용하여 특성 방정식 형태로 정리할 수 있습니다. $$\begin{align} B - \lambda I &= P^{-1}AP – \lambda P^{-1}P\\ &= P^{-1}(AP – \lambda P)\\ &= P^{-1}(A - \lambda I)P \end{align}$$ 위 식의 행렬식은 다음과 같이 정리됩니다. $$\begin{align} &\begin{aligned}\textsf{det}(B - \lambda I ) & = \textsf{det}(P^{-1}(AP – \lambda P))\\ &= \textsf{det}(P^{-1}) \textsf{det}((A – \lambda I)) \textsf{det}(P)\\ &= \textsf{det}(P^{-1}) \textsf{det}(P) \textsf{det}((A – \lambda I))\\ &= \textsf{det}(A – \lambda I)\end{aligned}\\ &\begin{aligned}\because \; \textsf{det}(P^{-1}) \textsf{det}(P) &= \textsf{det}(P^{-1}P)\\ &= \t

[matplotlib] 히스토그램(Histogram)

히스토그램(Histogram) 히스토그램은 확률분포의 그래픽적인 표현이며 막대그래프의 종류입니다. 이 그래프가 확률분포와 관계가 있으므로 통계적 요소를 나타내기 위해 많이 사용됩니다. plt.hist(X, bins=10)함수를 사용합니다. x=np.random.randn(1000) plt.hist(x, 10) plt.show() 위 그래프의 y축은 각 구간에 해당하는 갯수이다. 빈도수 대신 확률밀도를 나타내기 위해서는 위 함수의 매개변수 normed=True로 조정하여 나타낼 수 있다. 또한 매개변수 bins의 인수를 숫자로 전달할 수 있지만 리스트 객체로 지정할 수 있다. 막대그래프의 경우와 마찬가지로 각 막대의 폭은 매개변수 width에 의해 조정된다. y=np.linspace(min(x)-1, max(x)+1, 10) y array([-4.48810153, -3.54351935, -2.59893717, -1.65435499, -0.70977282, 0.23480936, 1.17939154, 2.12397372, 3.0685559 , 4.01313807]) plt.hist(x, y, normed=True) plt.show()

R 미분과 적분

내용 expression 미분 2차 미분 mosaic를 사용한 미분 적분 미분과 적분 R에서의 미분과 적분 함수는 expression()함수에 의해 생성된 표현식을 대상으로 합니다. expression expression(문자, 또는 식) 이 표현식의 평가는 eval() 함수에 의해 실행됩니다. > ex1<-expression(1+0:9) > ex1 expression(1 + 0:9) > eval(ex1) [1] 1 2 3 4 5 6 7 8 9 10 > ex2<-expression(u, 2, u+0:9) > ex2 expression(u, 2, u + 0:9) > ex2[1] expression(u) > ex2[2] expression(2) > ex2[3] expression(u + 0:9) > u<-0.9 > eval(ex2[3]) [1] 0.9 1.9 2.9 3.9 4.9 5.9 6.9 7.9 8.9 9.9 미분 D(표현식, 미분 변수) 함수로 미분을 실행합니다. 이 함수의 표현식은 expression() 함수로 생성된 객체이며 미분 변수는 다음 식의 분모의 변수를 의미합니다. $$\frac{d}{d \text{변수}}\text{표현식}$$ 이 함수는 어떤 함수의 미분의 결과를 표현식으로 반환합니다. > D(expression(2*x^3), "x") 2 * (3 * x^2) > eq<-expression(log(x)) > eq expression(log(x)) > D(eq, "x") 1/x > eq2<-expression(a/(1+b*exp(-d*x))); eq2 expression(a/(1 + b * exp(-d * x))) > D(eq2, "x") a * (b * (exp(-d * x) * d))/(1 + b