데이터 사이언스 스쿨 수학편을 바탕으로 공부한 내용입니다.
벡터와 행렬의 연산
벡터/행렬의 덧셈과 뺄셈¶
같은 크기를 가진 두 개의 벡터나 행렬은 덧셈과 뺄셈을 할 수 있다. 두 벡 터와 행렬에서 같은 위치에 있는 원소끼리 덧셈과 뺄셈을 하면 된다.
이러한 연산을 요소별(element-wise) 연산이라고 한다.
x=⎣⎢⎡101112⎦⎥⎤,y=⎣⎢⎡012⎦⎥⎤
x−y=⎣⎢⎡101112⎦⎥⎤−⎣⎢⎡012⎦⎥⎤=⎣⎢⎡10−011−112−2⎦⎥⎤=⎣⎢⎡101010⎦⎥⎤
벡터의 덧셈과 뺄셈을 넘파이로 계산하면 다음과 같다.
x = np.array([10, 11, 12, 13, 14])
y = np.array([0, 1, 2, 3, 4])
x + y
array([10, 12, 14, 16, 18])
x - y
array([10, 10, 10, 10, 10])
m1 = np.array([[5, 6], [7, 8]])
m2 = np.array([[10, 20], [30, 40]])
m3 = np.array([[1, 2], [3, 4]])
m1 + m2 - m3
array([[14, 24],
[34, 44]])
스칼라와 벡터/행렬의 곱셈
벡터 x 또는 행렬 A에 스칼라값 c를 곱하는 것은 벡터 x 또는 행렬 A의 모든 원소에 스칼라값 c를 곱하는 것과 같다
c[x1x2]=[cx1cx2]
c[a11a21a12a22]=[ca11ca21ca12ca22]
브로드캐스팅
원래는 덧셈과 뺄셈은 크기(차원)가 같은 두 벡터에 대해서만 가능하지만, 벡터와 스칼라 간에도 연산을 허용하기 위해 관례적으로 1-벡터를 사용하여 스칼라를 벡터로 변환하는데, 이를 브로드캐스팅(broadcasting) 이라고 부른다.
⎣⎢⎡101112⎦⎥⎤−10=⎣⎢⎡101112⎦⎥⎤−10⋅1=⎣⎢⎡101112⎦⎥⎤−⎣⎢⎡101010⎦⎥⎤
데이터 분석에서는 데이터 벡터의 각 원소의 평균값을 뺀 평균제거(mean removed) 벡터 혹은 0-평균(zero-mean) 벡터를 사용하는 경우가 많다.
x=⎣⎢⎢⎢⎢⎡x1x2⋮xN⎦⎥⎥⎥⎥⎤→x−m=⎣⎢⎢⎢⎢⎡x1−mx2−m⋮xN−m⎦⎥⎥⎥⎥⎤
위 식에서 m은 샘플 평균이다.
m=N1i=1∑Nxi
선형조합
벡터/행렬에 다음처럼 스칼라값을 곱한 후 더하거나 뺀 것을 벡터/행렬의 선형조합(linear combination) 이라고 한다.
벡터나 행렬을 선형조합해도 크기는 변하지 않는다.
벡터와 벡터의 곱셈
벡터 x와 벡터 y의 내적은 다음처럼 표기한다.
xTy
내적은 다음처럼 점(dot)으로 표기하는 경우도 있어서 닷 프로덕트(dot product) 라고도 부르고 <x,y> 기호로 나타낼 수도 있다
x⋅y=<x,y>=xTy
두 벡터를 내적하려면 다음과 같은 조건이 만족되어야 한다.
우선 두 벡터의 차원(길이)이 같아야 한다.
앞의 벡터가 행 벡터이고 뒤의 벡터가 열 벡터여야 한다.
xTy=[x1x2⋯xN]⎣⎢⎢⎢⎢⎡y1y2⋮yN⎦⎥⎥⎥⎥⎤=x1y1+⋯+xNyN
x∈RN×1
y∈RN×1
xTy∈R
넘파이에서 벡터와 행렬의 내적은 dot()이라는 명령 또는 @(at이라고 읽는다)이라는 연산자로 계산한다.
x = np.array([[1], [2], [3]])
y = np.array([[4], [5], [6]])
x.T @ y
array([[32]])
벡터의 내적을 사용하여 데이터를 분석하는 몇 가지 예를 살펴보자.
가중합
벡터의 내적은 가중합을 계산할 때 쓰일 수 있다. 가중합(weighted sum) 이란 복수의 데이터를 단순히 합하는 것이 아니라 각각의 수에 어떤 가중치 값을 곱한 후 이 곱셈 결과들을 다시 합한 것을 말한다.
만약 데이터 벡터가 x=[x1,⋯,xN]T 이고 가중치 벡터가 w=[w1,⋯,wN]T 이면 데이터 벡터의 가중합은 다음과 같다.
w1x1+⋯+wNxN=i=1∑Nwixi
이 값을 벡터 x와 w의 곱으로 나타내면 wTx 또는 xTw 라는 간단한 수식으로 표시할 수 있다.
만약 가중치가 모두 1이면 일반적인 합(sum)을 계산한다
w1=w2=⋯=wN=1
w=1N
i=1∑Nxi=1NTx
연습 문제
A, B, C 세 회사의 주식은 각각 100만원, 80만원, 50만원이다. 이 주식을 각각 3주, 4주, 5주를 매수할 때 필요한 금액을 구하고자 한다.
(1) 주식의 가격과 수량을 각각 p 벡터, n 벡터로 표시하고 넘파이로 코딩한다.
(2) 주식을 매수할 때 필요한 금액을 곱셈으로 표시하고 넘파이 연산으로 그 값을 계산한다.
p = np.array([[100], [80], [50]])
n = np.array([[3], [4], [5]])
result = p.T * n
result
array([[300, 240, 150],
[400, 320, 200],
[500, 400, 250]])
가중평균
가중합의 가중치값을 전체 가중치값의 합으로 나누면 가중평균(weighted average) 이 된다. 가중평균은 대학교의 평균 성적 계산 등에 사용할 수 있다.
x 데이터의 평균은 보통 xˉ 라는 기호로 표기하고 “엑스 바(x bar)” 라고 읽는다.
xˉ=N1i=1∑Nxi=N11NTx
x = np.arange(10)
N = len(x)
np.ones(N) @ x /N
4.5
유사도
유사도(similarity)는 두 벡터가 닮은 정도를 정량적으로 나타낸 값으로 두 벡터가 비슷한 경우에는 유사도가 커지고 비슷하지 앟은 경우에는 유사도가 작아진다.
내적을 이용하면 코사인 유사도(cosine similarity) 라는 유사도를 계산할 수 있다.
from sklearn.datasets import load_digits
import matplotlib.gridspec as gridspec
digits = load_digits()
d1 = digits.images[0]
d2 = digits.images[10]
d3 = digits.images[1]
d4 = digits.images[11]
v1 = d1.reshape(64, 1)
v2 = d2.reshape(64, 1)
v3 = d3.reshape(64, 1)
v4 = d4.reshape(64, 1)
plt.figure(figsize=(9, 9))
gs = gridspec.GridSpec(1, 8, height_ratios=[1],
width_ratios=[9, 1, 9, 1, 9, 1, 9, 1])
for i in range(4):
plt.subplot(gs[2 * i])
plt.imshow(eval("d" + str(i + 1)), aspect=1,
interpolation='nearest', cmap=plt.cm.bone_r)
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.title("image {}".format(i + 1))
plt.subplot(gs[2 * i + 1])
plt.imshow(eval("v" + str(i + 1)), aspect=0.25,
interpolation='nearest', cmap=plt.cm.bone_r)
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.title("vector {}".format(i + 1))
plt.tight_layout()
plt.show()

“0” 이미지와 “0” 이미지, 또는 “1” 이미지와 “1” 이미지의 내적값은 다음과 같다.
(v1.T @ v2)[0][0], (v3.T @ v4)[0][0]
(3064.0, 3661.0)
상대적으로 “0” 이미지와 “1” 이미지, 또는 “1” 이미지와 “0” 이미지의 내적값은 작다.
(v1.T @ v3)[0][0], (v1.T @ v4)[0][0], (v2.T @ v3)[0][0], (v2.T @ v4)[0][0]
(1866.0, 1883.0, 2421.0, 2479.0)
연습 문제
다음 코드를 실행하면 MNIST 숫자 이미지 전체 데이터를 모두 벡터로 변환하여 하나의 넘파이 행렬 X를 만든다. 이 행렬을 이용하여 다음 문제를 풀어라.
(1) 내적을 이용하여 첫 번째 이미지와 10번째 이미지의 유사도를 구하라.
(2) 내적을 이용하여 모든 이미지의 조합에 대해 유사도를 구하라. 어떻게 구현하는 것이 효율적일까? (힌트 : 이 문제는 뒤에서 배울 행렬과 행렬의 곱셈을 이용한다.)
from sklearn.datasets import load_digits
X = load_digits().data
X1 = X[0]
plt.imshow(X1.reshape(8,8), cmap=plt.cm.bone_r)

X10 = X[9]
plt.imshow(X10.reshape(8,8), cmap=plt.cm.bone_r)

X1.T @ X10
2807.0
선형회귀 모형
선형회귀 모형(linear regression model) 이란 독립변수 x에서 종속변수 y를 예측하는 방법의 하나로 독립변수 벡터 x와 가중치 벡터 w와의 가중합으로 y에 대한 예측값 y^를 계산하는 수식을 말한다.
y^=w1x1+⋯+wNxN
선형회귀 모형은 가장 단순하면서도 가장 널리 쓰이는 예측 모형이다.
예를 들어 어떤 아파트 단지의 아파트 가격을 조사하였더니 아파트 가격은 (1)면적, (2)층수, (3)한강이 보이는지의 여부, 즉 이 세 가지 특징에 의해 달라진다는 사실을 알게 되었다. 이때 이 단지 내의 아파트 가격을 예측하는 예측 모형을 다음과 같이 만들 수 있다.
-
면적(m2)을 입력 데이터 x1라고 한다.
-
층수를 입력 데이터 x2라고 한다
-
한강이 보이는지의 여부를 입력 데이터 x3라고 하며 한강이 보이면 x3=1, 보이지 않으면 x3=0이라고 한다.
-
출력 데이터 y^ 는 해당 아파트의 예측 가격이다.
y^=500x1+200x2+1000x3
이 모형은 다음과 같이 해석할 수 있다.
-
면적이 1m2 증가할수록 가격은 500만 원이 증가한다.
-
층수가 1층 높아질수록 가격은 200만 원이 증가한다.
-
한강이 보이는 집은 1,000만 원의 웃돈(프리미엄)이 존재한다.
선형회귀 모형의 단점
선형회귀 모형은 비선형적인 현실 세계의 데이터를 잘 예측하지 못할 수 있다는 단점이 있다.
예를 들어 집값은 면적에 단순 비례하지 않는다. 저층이 보통 고층보다 집값이 싸지만 층수가 올라갈수록 정확히 층수에 비례하여 가격이 증가하지도 않는다.
이러한 현실 세계의 데이터와 선형회귀 모형의 괴리를 줄이기 위해 선형회귀 모형이 아닌 완전히 다른 모형을 쓰기보다는 선형회귀 모형을 기반으로 여러 기법을 사용해 수정한 모형을 사용하는 것이 일반적이다.
제곱합
데이터의 분산(variance)이나 표준 편차(standard deviation) 등을 구하는 경우에는 각각의 데이터를 제곱한 뒤 이 값을 모두 더한 제곱합(sum of squares) 을 계산해야 한다.
xTx=[x1x2⋯xN]⎣⎢⎢⎢⎢⎡x1x2⋮xN⎦⎥⎥⎥⎥⎤=i=1∑Nxi2
행렬과 행렬의 곱셈
A행렬과 B행렬을 곱한 결과가 C행렬이 된다고 하자. C의 i번째 행, j번째 열의 원소 cij의 값은 A행렬의 i번째 행 벡터 aiT와 B행렬의 &j&번째 열 벡터 bj의 곱이다.
C=AB→cij=aiTbj
이 정의가 성립하려면 앞의 행렬 A의 열의 수가 뒤의 행렬 B의 행의 수와 일치해야만 한다.
A∈RN×L,B∈RL×M→AB∈RN×M
⎣⎢⎢⎢⎡a11a21a31a41a12a22a32a42a13a23a33a43⎦⎥⎥⎥⎤⎣⎢⎡b11b21b31b12b22b32⎦⎥⎤=⎣⎢⎢⎢⎡(a11b11+a12b21+a13b31)(a21b11+a22b21+a23b31)(a31b11+a32b21+a33b31)(a41b11+a42b21+a43b31)(a11b12+a12b22+a13b32)(a21b12+a22b22+a23b32)(a31b12+a32b22+a33b32)(a41b12+a42b22+a43b32)⎦⎥⎥⎥⎤
A = np.array([[1, 2, 3], [4, 5, 6]])
B = np.array([[1, 2], [3, 4], [5, 6]])
C= A@B
print(f' AB\n{C}')
AB
[[22 28]
[49 64]]
연습 문제
(1) AB , BA 모두 계산 가능한가?
A=[123]
B=⎣⎢⎡456789⎦⎥⎤
A = np.array([[1, 2, 3]])
B = np.array([[4, 7], [5, 8], [6, 9]])
print(f' AB\n{A@B}')
AB
[[32 50]]
(2) BA 의 결과가 AB와 같은가?
A=[1324]
B=[5768]
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(f' AB\n{A@B}')
print(f' BA\n{B@A}')
AB
[[19 22]
[43 50]]
BA
[[23 34]
[31 46]]
(3) AAT 와 ATA의 크기는 어떠한가? 항상 정방행렬이 되는가?
A=⎣⎢⎡135246⎦⎥⎤
A = np.array([[1, 2], [3, 4], [5,6]])
print("AA^T\n{}".format(A@A.T))
print("A^TA\n{}".format(A.T@A))
AA^T
[[ 5 11 17]
[11 25 39]
[17 39 61]]
A^TA
[[35 44]
[44 56]]
(4) xTx 와 xxT의 크기는 어떠한가? 어떤 것이 스칼라이고 어떤 것이 정방행렬인가?
x=⎣⎢⎡123⎦⎥⎤
x = np.array([[1], [2], [3]])
print("x^Tx\n{}".format(x.T@x))
print("xx^T\n{}".format(x@x.T))
x^Tx
[[14]]
xx^T
[[1 2 3]
[2 4 6]
[3 6 9]]
교환 법칙과 분배 법칙
행렬의 곱셈은 곱하는 행렬의 순서를 바꾸는 교환 법칙이 성립하지 않는다. 그러나 덧셈에 대한 분배 법칙은 성립한다.
A(B+C)=AB+AC
(A+B)C=AC+BC
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
C = np.array([[9, 8], [7, 6]])
AB 와 BA의 값은 다음처럼 다른 값이 나오므로 교환법칙이 성립하지 않음을 알 수 있다.
A@B
array([[19, 22],
[43, 50]])
B@A
array([[23, 34],
[31, 46]])
A@(B+C)
array([[42, 42],
[98, 98]])
A@B + A@C
array([[23, 34],
[31, 46]])
(A+B)@C
array([[110, 96],
[174, 152]])
A@C + B@C
array([[110, 96],
[174, 152]])
전치 연산도 마찬가지로 덧셈/뺄셈에 대해 분배 법칙이 성립한다.
(A+B)T=AT+BT
전치 연산과 곱셈의 경우에는 분배 법칙이 성립하기는 하지만 전치 연산이 분배되면서 곱셈의 순서가 바뀐다
(AB)T=BTAT
(ABC)T=CTBTAT
(A+B).T
array([[ 6, 10],
[ 8, 12]])
A.T+B.T
array([[ 6, 10],
[ 8, 12]])
(A@B).T
array([[19, 43],
[22, 50]])
B.T@A.T
array([[19, 43],
[22, 50]])
연습 문제
(1) 길이가 같은 일벡터 1N∈RN와 행벡터 x∈RN의 곱은 행벡터 x를 반복하여 가지는 행렬과 같음을 보여라
oneV = np.ones((5,1))
oneV
array([[1.],
[1.],
[1.],
[1.],
[1.]])
V = np.array([[1, 2, 3, 4, 5]])
V
array([[1, 2, 3, 4, 5]])
oneV @ V
array([[1., 2., 3., 4., 5.],
[1., 2., 3., 4., 5.],
[1., 2., 3., 4., 5.],
[1., 2., 3., 4., 5.],
[1., 2., 3., 4., 5.]])
(2) 다음 코드를 실행하면 붓꽃 전체 데이터를 모두 벡터로 변환하여 하나의 넘파이 행렬 X 를 만든다.
이 데이터로 행렬 Xˉ의 값을 계산하라. 이 행렬은 첫 번째 열의 값이 모두 같은 값으로 붓꽃의 꽃받침의 길이(sepal length)의 평균이고 두 번째 열의 값이 모두 같은 값으로 붓꽃의 꽃받침의 폭(sepal width)의 평균, 이런 식으로 계산된 행렬이다.
from sklearn.datasets import load_iris
X = load_iris().data
r = np.array([[np.mean(X.T[0])], [np.mean(X.T[1])]])
r
array([[5.84333333],
[3.05733333]])
곱셈의 연결
연속된 행렬의 곱셈은 계산 순서를 임의의 순서로 해도 상관없다.
ABC=(AB)C=A(BC)
ABCD=((AB)C)D=(AB)(CD)=A(BCD)=A(BC)D
연습 문제
다음 행렬의 곱셈을 순서를 바꾸어 두 가지 방법으로 해본다.
[12][1324][56]
a = np.array([1,2])
b = np.array([[1,2], [3, 4]])
c = np.array([[5], [6]])
(a@b)@c
array([95])
a@(b@c)
array([95])
항등행렬의 곱셈
어떤 행렬이든 항등행렬을 곱하면 그 행렬의 값이 변하지 않는다.
A = np.array([[1, 2], [3, 4]])
I = np.eye(2)
A @ I
array([[1., 2.],
[3., 4.]])
I @ A
array([[1., 2.],
[3., 4.]])
행렬과 벡터의 곱
행렬의 곱셈 중 가장 널리 쓰이는 것은 다음과 같은 형태의 행렬 M과 벡터 v의 곱이다.
Mv
열 벡터의 선형조합
행렬 X와 벡터 w의 곱은 행렬 X를 이루는 열벡터 c1,c2,…,cM을 뒤에 곱해지는 벡터 w의 각 성분 w1,w2,…,wM으로 선형조합(linear combination)을 한 결과 벡터와 같다.
Xw=[c1c2⋯cM]⎣⎢⎢⎢⎢⎡w1w2⋮wM⎦⎥⎥⎥⎥⎤=w1c1+w2c2+⋯+wMcM
연습 문제
다음 행렬 X와 벡터 w에 대해 곱 Xw가 열벡터 c1,c2,c3의 선형조합 w1c1+w2c2+w3c3가 됨을 실제 계산으로 증명하라.
X=[142536],w=⎣⎢⎡234⎦⎥⎤
x = np.array([[1, 2, 3], [4, 5, 6]])
w = np.array([2,3,4])
x@w
array([20, 47])
x.T[0]*w[0] + x.T[1]*w[1] + x.T[2]*w[2]
array([20, 47])
벡터의 선형조합은 다양한 분야에 응용된다. 예를 들어 두 이미지 벡터의 선형조합은 두 이미지를 섞어놓은 모핑(morphing) 효과를 얻는 데 사용할 수 있다.
from sklearn.datasets import fetch_olivetti_faces
faces = fetch_olivetti_faces()
f, ax = plt.subplots(1, 3)
ax[0].imshow(faces.images[6], cmap=plt.cm.bone)
ax[0].grid(False)
ax[0].set_xticks([])
ax[0].set_yticks([])
ax[0].set_title("image 1: $x_1$")
ax[1].imshow(faces.images[10], cmap=plt.cm.bone)
ax[1].grid(False)
ax[1].set_xticks([])
ax[1].set_yticks([])
ax[1].set_title("image 2: $x_2$")
new_face = 0.7 * faces.images[6] + 0.3 * faces.images[10]
ax[2].imshow(new_face, cmap=plt.cm.bone)
ax[2].grid(False)
ax[2].set_xticks([])
ax[2].set_yticks([])
ax[2].set_title("image 3: $0.7x_1 + 0.3x_2$")
plt.show()

여러 개의 벡터에 대한 가중합 동시 계산
벡터 하나의 가중합은 wTx 또는 xTw로 표시할 수 있다는 것을 배웠다. 여러 데이터 x1,x2,x3,⋯,xN 개의 데이터 모두에 대해 예측값 y1,y2,y3,⋯,yN을 한꺼번에 계산하고 싶다면 다음처럼 데이터 행렬 X를 사용하여 y^=Xw라는 수식으로 간단하게 표시할 수 있다.
y^=⎣⎢⎢⎢⎢⎡y^1y^2⋮y^M⎦⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎡w1x1,1+w2x1,2+⋯+wNx1,Nw1x2,1+w2x2,2+⋯+wNx2,N⋮w1xM,1+w2xM,2+⋯+wNxM,N⎦⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎡x1,1x2,1⋮xM,1x1,2x2,2⋮xM,2⋯⋯⋮⋯x1,Nx2,N⋮xM,N⎦⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎡w1w2⋮wN⎦⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎡x1Tx2T⋮xMT⎦⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎡w1w2⋮wN⎦⎥⎥⎥⎥⎤=Xw
잔차
선형 회귀분석(linear regression)을 한 결과는 가중치 벡터 w라는 형태로 나타나고 예측치는 이 가중치 벡터를 사용한 독립변수 데이터 레코드 즉, 벡터 xi의 가중합 wTxi
이 된다고 했다.
예측치와 실젯값(target) yi의 차이를 오차(error) 혹은 잔차(residual) ei라고 한다. 이러한 잔찻값을 모든 독립변수 벡터에 대해 구하면 잔차 벡터 e가 된다.
ei=yi−y^i=yi−wTxi
잔차 벡터는 다음처럼 y−Xw로 간단하게 표기할 수 있다.
e=⎣⎢⎢⎢⎢⎡e1e2⋮eM⎦⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎡y1y2⋮yM⎦⎥⎥⎥⎥⎤−⎣⎢⎢⎢⎢⎡x1Twx2Tw⋮xMTw⎦⎥⎥⎥⎥⎤=y−Xw
e=y−Xw
잔차 제곱합
잔차의 크기는 잔차 벡터의 각 원소를 제곱한 후 더한 잔차 제곱합(RSS: Residual Sum of Squares) 을 이용하여 구한다. 이 값은 eTe 로 간단하게 쓸 수 있으며 그 값은 다음처럼 계산한다.
i=1∑Nei2=i=1∑N(yi−wTxi)2=eTe=(y−Xw)T(y−Xw)
연습 문제
분배 법칙을 사용하여 위 식 (y−Xw)T(y−Xw)을 풀어쓰면 다음과 같아짐을 보여라.
(y−Xw)T(y−Xw)=yTy−wTXTy−yTXw+wTXTXw
(yT−XTwT)(y−Xw)
yTy−wTXTy−yTXw+wTXTXw
이차형식
위의 연습 문제에서 마지막 항은 wTXTXw라는 형태다. 이 식에서 XTX는 정방행렬이 되므로 이 정방행렬을 A라고 이름 붙이면 마지막 항은 wTAw와 같은 형태가 된다.
벡터의 이차형식(Quadratic Form) 이란 이처럼 어떤 벡터와 정방행렬이 ‘행벡터 × 정방행렬 × 열벡터’의 형식으로 되어 있는 것을 말한다.
이 수식을 풀면 i=1,…,N,j=1,…,N에 대해 가능한 모든 i,j 쌍의 조합을 구한 다음 i, j에 해당하는 원소 xi, xj를 가중치 aij와 같이 곱한 값 aijxixj의 총합이 된다.
xTAx=[x1x2⋯xN]⎣⎢⎢⎢⎢⎡a1,1a2,1⋮aN,1a1,2a2,2⋮aN,2⋯⋯⋱⋯a1,Na2,N⋮aN,N⎦⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎡x1x2⋮xN⎦⎥⎥⎥⎥⎤=i=1∑Nj=1∑Nai,jxixj
연습 문제
다음 3차원 벡터와 행렬에 대해 이차형식을 쓰고 값을 계산하라.
x=⎣⎢⎡x1x2x3⎦⎥⎤
A=⎣⎢⎡a11a21a31a12a22a32a13a23a33⎦⎥⎤
예를 들어 x=[1,2,3]T 이고 A가 다음과 같다면
A=⎣⎢⎡147258369⎦⎥⎤
넘파이 에서 벡터의 이차형식은 다음처럼 계산한다.
x = np.array([1, 2, 3])
x
array([1, 2, 3])
A = np.arange(1, 10).reshape(3, 3)
A
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
x.T
array([1, 2, 3])
x.T @ A @ x
228
다음 식이 성립함을 증명하라.
xTAx=21xT(A+AT)x
부분행렬
다음과 같은 2차원 정방행렬 A,B가 있다.
A=[a11a21a12a22],B=[b11b21b12b22]
두 행렬의 곱 AB는 A,B의 부분행렬(submatrix) 을 이용하여 여러 방법으로 계산할 수 있다
(1) 우선 앞에 곱해지는 행렬을 행벡터로 나누어 계산해도 된다.
A=⎣⎢⎡a1Ta2T⎦⎥⎤
a1T=[a11a12],a2T=[a21a22]
AB=⎣⎢⎡a1Ta2T⎦⎥⎤B=⎣⎢⎡a1TBa2TB⎦⎥⎤
여기에서는 2차원 행렬의 예를 들었지만 일반적인 N차원 행렬에서도 이 관계는 성립한다.
A = np.arange(1, 10).reshape(3, 3)
B = np.arange(11, 20).reshape(3, 3)
print(A)
print(B)
[[1 2 3]
[4 5 6]
[7 8 9]]
[[11 12 13]
[14 15 16]
[17 18 19]]
A@B
array([[ 90, 96, 102],
[216, 231, 246],
[342, 366, 390]])
np.array([A[0]@B, A[1]@B, A[2]@B])
array([[ 90, 96, 102],
[216, 231, 246],
[342, 366, 390]])
(2) 아니면 뒤에 곱해지는 행렬을 열벡터로 나누어 계산해도 된다.
B=⎣⎢⎢⎡\mathstrutb1\mathstrut\mathstrutb2\mathstrut⎦⎥⎥⎤
b1=[b11b21],b2=[b21b22]
AB=A⎣⎢⎢⎡\mathstrutb1\mathstrut\mathstrutb2\mathstrut⎦⎥⎥⎤=⎣⎢⎢⎡\mathstrutAb1\mathstrut\mathstrutAb2\mathstrut⎦⎥⎥⎤
np.array([A@B.T[0], A@B.T[1], A@B.T[2]]).T
array([[ 90, 96, 102],
[216, 231, 246],
[342, 366, 390]])
(3) 앞에 곱해지는 행렬을 열벡터로, 뒤에 곱해지는 행렬을 행벡터로 나누어 스칼라처럼 계산해도 된다.
AB=[a1a2][b1Tb2T]=a1b1T+a2b2T
AB=⎣⎢⎢⎡\mathstruta1\mathstrut\mathstruta2\mathstrut⎦⎥⎥⎤⎣⎢⎡b1Tb2T⎦⎥⎤=\mathstruta1\mathstrutb1T+\mathstrutb1\mathstrutb2T
np.array([[A[0]@B.T[0], A[0]@B.T[1], A[0]@B.T[2]],
[A[1]@B.T[0], A[1]@B.T[1], A[1]@B.T[2]],
[A[2]@B.T[0], A[2]@B.T[1], A[2]@B.T[2]]])
array([[ 90, 96, 102],
[216, 231, 246],
[342, 366, 390]])