[BoostCamp Pre-course] 행렬(Metric) 개념 정리

김윤경·2025년 7월 3일
0

AI👾

목록 보기
2/3
post-thumbnail

행렬(Metric) 이란?

행렬(Metric)

X=[123750212]\mathbf{X} =\begin{bmatrix}1 & -2 & 3 \\7 & 5 & 0 \\-2 & -1 & 2\end{bmatrix}

소문자 볼드체(x\mathbf{x})는 벡터, 대문자 볼드체(X\mathbf{X})는 행렬을 의미한다.
: 벡터를 원소로 갖는 2차원 배열(array)을 의미한다.

[코드]

X = np.array([[ 1, -2,  3],   # 행(row) 기준
              [ 7,  5,  0],
              [-2, -1,  2]])
  • numpy는 행(row)을 기본 단위로 처리한다.

X=[x1x2xn]=[x11x12x1mx21x22x2mxn1xn2xnm]\quad \mathbf{X} =\begin{bmatrix}\mathbf{x}_1 \\\mathbf{x}_2 \\\vdots \\\mathbf{x}_n\end{bmatrix}=\begin{bmatrix}x_{11} & x_{12} & \cdots & x_{1m} \\x_{21} & x_{22} & \cdots & x_{2m} \\\vdots & \vdots & \ddots & \vdots \\x_{n1} & x_{n2} & \cdots & x_{nm}\end{bmatrix}


  • 크기가 n×mn \times m 이면, 원소는 xijx_{ij}로 표기한다. (ii : 행, jj : 열)

  • 공간상에서의 벡터는 한 점을 나타낸다면, 행렬은 여러 점을 나타낸다.

  • 행렬은 벡터 공간에서 사용되는 연산자 (operator)로도 작동한다.

zi=j=1maijxj,z=[z1z2zn]=[a11a12a1ma21a22a2man1an2anm]A[x1x2xm]\quad z_i = \sum_{j=1}^{m} a_{ij}x_j,\qquad\mathbf{z} =\begin{bmatrix}z_1 \\ z_2 \\ \vdots \\ z_n\end{bmatrix}=\underbrace{\begin{bmatrix}a_{11} & a_{12} & \cdots&a_{1m} \\a_{21} & a_{22} & \cdots & a_{2m} \\\vdots & \vdots & \ddots & \vdots \\a_{n1} & a_{n2} & \cdots & a_{nm}\end{bmatrix}}_{\displaystyle \mathbf{A}}\begin{bmatrix}x_1 \\ x_2 \\ \vdots \\ x_m\end{bmatrix}

행렬곱을 통해 벡터를 다른 차원의 공간으로 보낼 수 있다. (패턴 추출, 데이터 압축 등 가능)

  • 전치 행렬(Transpose Matrix) : 행과 열의 인덱스가 바뀐 행렬

    • 벡터의 Transpose : 행벡터 ↔ 열벡터
    • 행렬의 Transpose : n×mn \times m 행렬 ↔ m×nm \times n 행렬


행렬의 기본 연산

행렬 X\mathbf{X}, Y\mathbf{Y}가 다음과 같다고 하자

X=[x11x12x1mx21x22x2mxn1xn2xnm]Y=[y11y12y1my21y22y2myn1yn2ynm]\mathbf{X} =\begin{bmatrix}x_{11} & x_{12} & \cdots & x_{1m} \\x_{21} & x_{22} & \cdots & x_{2m} \\\vdots & \vdots & \ddots & \vdots \\x_{n1} & x_{n2} & \cdots & x_{nm}\end{bmatrix} \quad \mathbf{Y} =\begin{bmatrix}y_{11} & y_{12} & \cdots & y_{1m} \\y_{21} & y_{22} & \cdots & y_{2m} \\\vdots & \vdots & \ddots & \vdots \\y_{n1} & y_{n2} & \cdots & y_{nm}\end{bmatrix}

: 이때 행렬끼리 동일한 모양이라면 연산이 가능하다.

  1. 스칼라곱 (Scalar Multiplication) : αX=(αxij)\alpha X = (\alpha x_{ij})

    : 모든 성분에 똑같이 숫자를 곱하는 것을 의미한다.

    αX=[αx11αx12αx1mαx21αx22αx2mαxn1αxn2αxnm]\alpha \mathbf{X} =\begin{bmatrix}\alpha x_{11} & \alpha x_{12} & \cdots & \alpha x_{1m} \\\alpha x_{21} & \alpha x_{22} & \cdots & \alpha x_{2m} \\\vdots & \vdots & \ddots & \vdots \\\alpha x_{n1} & \alpha x_{n2} & \cdots & \alpha x_{nm}\end{bmatrix}


  1. 덧셈·뺄셈 (Addition·Subtraction) : X±Y=(xij±yij)X \pm Y = (x_{ij} \pm y_{ij})

    X±Y=[x11±y11x12±y12x1m±y1mx21±y21x22±y22x2m±y2mxn1±yn1xn2±yn2xnm±ynm]\mathbf{X} \pm \mathbf{Y} =\begin{bmatrix}x_{11} \pm y_{11} & x_{12} \pm y_{12} & \cdots & x_{1m} \pm y_{1m} \\x_{21} \pm y_{21} & x_{22} \pm y_{22} & \cdots & x_{2m} \pm y_{2m} \\\vdots & \vdots & \ddots & \vdots \\x_{n1} \pm y_{n1} & x_{n2} \pm y_{n2} & \cdots & x_{nm} \pm y_{nm}\end{bmatrix}


  1. 성분곱 (Hadamard Product) : XY=(xijyij)X \odot Y = (x_{ij} y_{ij})

    Hadamard 곱, 또는 성분곱은 동일한 위치의 성분을 각각 곱하는 것을 의미한다.

    XY=[x11y11x12y12x1my1mx21y21x22y22x2my2mxn1yn1xn2yn2xnmynm]\mathbf{X} \odot \mathbf{Y} =\begin{bmatrix}x_{11} y_{11} & x_{12} y_{12} & \cdots & x_{1m} y_{1m} \\x_{21} y_{21} & x_{22} y_{22} & \cdots & x_{2m} y_{2m} \\\vdots & \vdots & \ddots & \vdots \\x_{n1} y_{n1} & x_{n2} y_{n2} & \cdots & x_{nm} y_{nm}\end{bmatrix}


※ 행렬의 스칼라곱, 덧셈, 뺄셈, 성분곱은 벡터와 다를 게 없다.

  1. 행렬곱 (Matrix Multiplication) : ii번째 행벡터와 jj번째 벡터 사이의 내적

    행렬곱의 특징

    • X\mathbf{X}의 열 수와 Y\mathbf{Y}의 행 수가 같아야 한다.

    • 행렬곱은 교환법칙이 성립하지 않기 때문에, 곱하는 순서가 바뀌면 결과도 달라진다. (XYYX\mathbf{X}\mathbf{Y} \ne \mathbf{Y}\mathbf{X})

    • 모든 선형변환(linear transform)은 결국 행렬곱 형태로 표현 가능하다.

    행렬곱의 일반적인 수식은 아래와 같다:

    XY=(kxikykj)\quad \mathbf{X}\mathbf{Y} = \left( \sum_{k} x_{ik} y_{kj} \right)


    [코드]

    X = np.array([[1, -2, 3],  
                [7, 5, 0],
                [-2, -1, 2]])  # 3 x 3 행렬
    
    Y = np.array([[0, 1],   
                [1, -1],
                [-2, 1]])      # 3 x 2 행렬
    
    X @ Y
    # array([[-8, 6],   # -8 = 1*0 + (-2)*1 + 3*(-2)
    #        [5, 2],
    #        [-5, 1]])

    ⚠️np.inner 함수와의 차이⚠️

    한편 np.inner(X, Y)는 행렬곱과는 다르게 작동한다.
    이는 ii번째 행벡터와 jj번째 벡터 사이의 내적(inner product)을 계산하며,
    결과적으로 tr(XY)tr(\mathbf{X}\mathbf{Y}^\top)와 같은 동작을 수행하게 된다.

    즉, XX의 행의 개수와 YY의 행의 개수가 같아야 연산이 가능하다.
    이 때문에 행렬곱과는 차원 조건도 다르고 결과도 완전히 다르다.

    아래는 np.inner의 예시이다:

    X = np.array([[1, -2, 3],  
    			   [7, 5, 0],
    			   [-2, -1, 2]]) # 3 x 3
    Y = np.array([[0, 1, -1],  
    			  [1, -1, 0]])   # 2 x 3
    np.inner(X, Y) 
    # array([[-5, 3],            # -5 = 1*0 + (-2)*1 + 3*(-1)
    #		 [5, 2],
    #		 [-3, -1]])


역행렬 (Inverse Matrix)

: 어떤 행렬 A\mathbf{A}에 대해, 그 연산을 거꾸로 되돌리는 역할을 하는 행렬을 의미하며,
  \space\space보통 A1\mathbf{A}^{-1}로 표기한다.

역행렬은 행과 열의 개수가 같고(즉, 정방행렬) 행렬식(determinant)이 0이 아닌 경우에만 계산할 수 있다.

AA1=A1A=I,I=[100010001]\quad \mathbf{A} \mathbf{A}^{-1} = \mathbf{A}^{-1}\mathbf{A} = \mathbf{I}, \quad \quad \mathbf{I} = \begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix}

NumPy에서는 np.linalg.inv()를 사용하여 역행렬을 구할 수 있다.

X = np.array([[1, -2, 3],  
            [7, 5, 0],
            [-2, -1, 2]])

np.linalg.inv(X)
# array([[ 0.21276596,  0.0212766 , -0.31914894],
#        [-0.29787234,  0.17021277,  0.44680851],
#        [ 0.06382979,  0.10638298,  0.40425532]])

X @ np.linalg.inv(X)
# array([[ 1.00000000e+00, -1.38777878e-17,  0.00000000e+00],
#        [-2.22044605e-16,  1.00000000e+00, -5.55111512e-17],
#        [-2.77555756e-17,  0.00000000e+00,  1.00000000e+00]])

⚠️ 역행렬을 구할 수 없는 경우 ⚠️

: 만약 행렬의 행과 열 수가 다르거나, 행렬식이 0인 경우에는 역행렬이 존재하지 않는다.
이러한 경우에는 유사역행렬(pseudo-inverse) 또는 무어-펜로즈 역행렬(Moore-Penrose pseudoinverse)을 사용한다.

무어-펜로즈 유사역행렬 (Moore-Penrose Pseudoinverse)

: 행렬 A\mathbf{A}n×mn\times m 행렬이고, 전치행렬 AA\mathbf{A}A^\topm×nm\times n일 때,
다음 두 가지 경우로 나뉜다.

  1. nm\mathbf{n \ge m} 인 경우 :

    A+=(AA)1A\mathbf{A}^+ = (\mathbf{A}^\top \mathbf{A})^{-1} \mathbf{A}^\top

    이 경우에는 A+A=I\mathbf{A}^+ \mathbf{A} = \mathbf{I}가 성립한다.
    AA\mathbf{A}^\top \mathbf{A}m×mm \times m 행렬이므로 역행렬 계산이 가능하다.

  1. nm\mathbf{n \le m} 인 경우 :

    A+=A(AA)1\mathbf{A}^+ = \mathbf{A}^\top(\mathbf{A}\mathbf{A}^\top )^{-1}

    이 경우에는 AA+=I\mathbf{A} \mathbf{A}^+ = \mathbf{I}가 성립한다.
    AA\mathbf{A}^\top \mathbf{A}m×mm \times m 행렬이므로 역행렬 계산이 가능하다.

따라서, 유사역행렬을 구할 때는 행의 개수와 열의 개수를 비교하여 적절한 공식을 선택해야 한다.

NumPy에서는 np.linalg.pinv()를 사용하여 유사역행렬을 쉽게 구할 수 있다.

Y = np.array([[0, 1],   
            [1, -1],
            [-2, 1]])  # 행의 개수 != 열의 개수 → 유사역행렬 사용

np.linalg.pinv(Y)
# array([[ 5.00000000e-01,  4.09730229e-17, -5.00000000e-01],
#        [ 8.33333333e-01, -3.33333333e-01, -1.66666667e-01]])

np.linalg.pinv(Y) @ Y
# array([[ 1.00000000e+00, -2.22044605e-16],
#        [ 0.00000000e+00,  1.00000000e+00]])


✅ 예제 1: 연립방정식 풀기

변수의 개수가 식의 개수보다 많을 때 (nmn \le m)의 예제를 생각해보자.
이 경우는 해가 유일하지 않거나 무한히 존재할 수 있는 상황으로, 다음과 같이 정리된다.


a11x1+a12x2++a1mxm=b1a21x1+a22x2++a2mxm=b2        an1x1+an2x2++anmxm=bnAx=b\quad \begin{aligned}a_{11}x_1 + a_{12}x_2 + \cdots + a_{1m}x_m &= b_1\\a_{21}x_1 + a_{22}x_2 + \cdots + a_{2m}x_m &= b_2 \\\vdots\qquad\qquad\quad\;\;\;\; &\vdots \\a_{n1}x_1 + a_{n2}x_2 +\cdots + a_{nm}x_m &= b_n\end{aligned} \quad \rightarrow \quad\mathbf{A} \mathbf{x} = \mathbf{b}

여기서 aa는 계수, x1, x2,,xmx_1, \space x_2, \cdots, x_m는 구하고자 하는 해를 의미한다.


이 식은 행렬 방정식 형태로 표현할 수 있으며,
해를 구하기 위해 유사역행렬(Moore-Penrose Pseudo-Inverse)을 이용한다.

x=A+b=A(AA)1b\quad \mathbf{x} = \mathbf{A}^+\mathbf{b} = \mathbf{A}^\top (\mathbf{A}\mathbf{A}^\top)^{-1} \mathbf{b}

이처럼 유사역행렬을 이용하면 과잉 결정된 시스템에서도 최적의 해(근사해)를 구할 수 있다.



✅ 예제 2: 선형회귀(Linear Regression) 분석

이번에는 반대 상황, 즉 식의 개수 nn가 변수의 개수 mm보다 많은 경우(nmn \le m) 를 살펴보자.

이는 다음과 같은 선형 모델 식으로 표현할 수 있다.

[x1x2xn][β1β2βm]=[y1y2yn] Xβ =y\quad \begin{bmatrix}\mathbf{x}_1 \\\mathbf{x}_2 \\\vdots\\\mathbf{x}_n\end{bmatrix}\begin{bmatrix}\beta_1 \\\beta_2\\\vdots \\\beta_m\end{bmatrix}=\begin{bmatrix}y_1 \\y_2\\\vdots \\y_n\end{bmatrix} \\\ \quad \quad \mathbf{X} \quad \boldsymbol{\beta} \quad \space = \quad \mathbf{y}

여기서 X\mathbf{X}는 설명 변수(feature), yy는 타깃 값(target), β\beta는 추정해야 할 계수이다.

그러나 일반적으로 Xβ=y\mathbf{X} \boldsymbol{\beta} = \mathbf{y}를 정확히 만족하는 해는 존재하지 않으며,
가장 적합한 해를 구하기 위해 다음과 같은 근사식을 사용한다.

Xβ=y^y\quad \mathbf{X} \boldsymbol{\beta} = \mathbf{\hat y} \approx \mathbf{y}

이때, 다음 목적식을 최소화한다.

minyy^2min ||\mathbf{y} - \hat{\mathbf{y}} ||_2

즉, L2 노름을 최소화하는 계수 β\beta를 찾는 것이 목표이다.
이때 유사역행렬을 이용한 해는 다음과 같이 계산된다.

β=X+y=(XX)1Xy\beta = \mathbf{X}^+\mathbf{y} = (\mathbf{X}^\top\mathbf{X})^{-1}\mathbf{X}^\top \mathbf{y}

[코드]
다음은 동일한 선형회귀 문제를 두 가지 방식으로 해결하는 코드 예제이다:

# scikit-learn을 활용한 선형회귀분석
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X, y)
y_test = model.predict(x_test)
# Moore-Penrose 역행렬을 활용한 선형회귀분석 
X_ = np.array([np.append(x, [1]) for x in X]) # y절편(intercept)항을 고려해야 함 
beta = np.linalg.pinv(X_) @ y       # beta = X^+ @ y 
y_test = np.append(x_test) @ beta   # X@beta = y_hat

np.linalg.pinv()를 활용하면 LinearRegression()과 동일한 결과를 수학적으로 직접 구현할 수 있다.
이처럼 선형 방정식과 회귀 문제 모두 유사역행렬로 해결이 가능하다는 점은 수치해석과 머신러닝의 핵심 개념 중 하나이다.


위 내용은 (([부스트캠프 AI Tech 프리코스] 인공지능 기초 다지기 (2)) 2. 행렬이 뭐에요?) 강의 내용을 바탕으로 작성하였습니다.
더 자세한 내용은 강의를 통해 확인하시길 바랍니다.

profile
AI / Recsys

0개의 댓글