AI 기초다지기 (2)

·2025년 7월 15일

just공부

목록 보기
26/47

행렬은 뭔가요?

행렬(matrix)은 벡터를 원소로 가지는 2차원 배열이다.

  • 열 벡터를 원소로 가지는 2차원 배열
  • numpy에서는 열벡터가 아니라 행벡터를 원소로 가져간다고 생각하시면 조금 더 이해가 쉬울 것 같다.
    • numpy에서는 행(row)이 기본 단위

예시

  • 3개의 행과 3개의 열
  • 3차원 배열
import numpy as np

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

# 실행결과 
[[ 1 -2  3]
 [ 7  5  0]
 [-2 -1  2]]

  • n개의 행과 m개의 열로 이뤄진 행렬
X=[x1x2xn]=[x11x12x1mx21x22x2mxn1xn2xnm]\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}

아래와 같이 표기하기도 한다. - i번째 행의 j번째 원소
X=(xij)\mathbf{X} = (x_{ij})
  • 행렬은 행(row)과 열(column)이라는 인덱스(index)를 가짐
  • 행렬의 특정 행(열)을 고정하면 행(열)벡터라고 부른다.

전치행렬(transpose matrix)은 행과 열의 인덱스가 바뀐 행렬을 의미한다.

  • 아래와 같이 T 기호로 표시해주면 된다.
XT=(xji)\mathbf{X}^T = (x_{ji})
  • 행은 열로 열은 행으로 바꾸게 되는 것이다.

** 벡터도 transpose 연산을 할 수 있다.

행렬을 이해하자

벡터가 공간에서 한 점을 의미한다면 행렬은 여러 점들을 나타낸다.
행렬의 행벡터 xix_i는 i번째 데이터를 의미한다.

  • 행렬의 xijx_{ij} 는 i번째 데이터의 j번째 변수의 값을 말한다.

행렬의 덧셈, 뺄셈, 성분곱, 스칼라곱

행렬끼리 같은 모양을 가지면 덧셈, 뺄셈을 계산할 수 있다.

X±Y=(xij±yij)\mathbf{X} \pm \mathbf{Y} = (x_{ij} \pm y_{ij})

위와 같으므로 아래의 예시로 이해해볼 수 있다.

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} \qquad \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}

이렇게 X와 Y가 있다고 가정한다면, 아래처럼 연산을 할 수 있게 된다.

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}
  • 보면 알다시피 벡터의 덧뺄셈과 동일하다.

행렬의 성분곱 또한 벡터와 같다.

XY=(xijyij)\mathbf{X} \circ \mathbf{Y} = (x_{ij} y_{ij})

위와 같은 정의를 가졌기 때문에 아래와 같은 결과가 나올 수 있게 된다.

XY=[x11y11x12y12x1my1mx21y21x22y22x2my2mxn1yn1xn2yn2xnmynm]\mathbf{X} \circ \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}

행렬의 스칼라곱 또한 같다.

αX=(αxij)\alpha\mathbf{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}

행렬 곱셈

행렬의 곱셈은 벡터의 곱셈과 조금 다르다.
행렬 곱셈(matrix multiplication)은 i번째 행벡터와 j번째 열벡터 사이의 내적을 성분으로 가지는 행렬을 계산한다.

  • 행렬곱은 X의 열의 개수와, Y의 행의 개수가 같아야 한다.

어떤 행렬을 앞에 놓을지, 뒤에 놓을지에 따라 결과가 달라지기 때문에 중요하다.

X=[x11x12x1mx21x22x2mxn1xn2xnm]Y=[y11y12y1y21y22y2ym1ym2ym]\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} \qquad \mathbf{Y} = \begin{bmatrix} y_{11} & y_{12} & \cdots & y_{1\ell} \\ y_{21} & y_{22} & \cdots & y_{2\ell} \\ \vdots & \vdots & \ddots & \vdots \\ y_{m1} & y_{m2} & \cdots & y_{m\ell} \end{bmatrix}
XY=(kxikykj)\mathbf{XY} = \left( \sum_k x_{ik} y_{kj} \right)
  • numpy 에선 @ 연산을 사용할 수 있다.

예시

import numpy as np

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

y = np.array([[0, 1],
              [1, -1],
              [-2, 1]])

x @ y

# 실행 결과
array([[-8,  6],
       [ 5,  2],
       [-5,  1]])

연산 결과는 아래와 같이 계산은 해보면 된다.

  • 첫 번째 행의 첫 번째 열의 결과인 -8이 어떻게 나오게 된 건지 알아보자.
    -8 = (1 * 0) + (-2 * 1) + (3 * -2) = 0 + (-2) + (-6)

  • 첫 번째 행의 두 번째 열의 결과인 6은 어떻게 나왔을까
    6 = (1 * 1) + (-2 * -1) + (3 * 1) = 1 + 2 + 3

  • 그렇다면 세 번째 행의 두 번째 열인 1의 결과는 어떨까?
    1 = (-2 * 1) + (-1 * -1) + (2 * 1) = (-2) + 1 + 2

이렇게 나올 수 있다.
아마 직접 세 번 정도 해보면 쉽게 풀 수 있을 것이라고 생각한다.

행렬도 내적이 있을까?

numpynp.inner는 i번째 행벡터와 j번째 행벡터 사이의 내적을 성분으로 가지는 행렬을 계산한다.
수학에서 말하는 내적과는 다르므로 주의하자.

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} \qquad \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}
XY=(kxikyjk)\mathbf{X} \mathbf{Y}^{\top} = \left( \sum_k x_{ik} y_{jk} \right)

예시

import numpy as np

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

Y = np.array([[0, 1, -1],
              [1, -1, 0]])

np.inner(X, Y)

# 실행 결과
array([[-5,  3],
       [ 5,  2],
       [-3, -1]])

위의 계산 결과가 어떻게 나오게 된 것인지 알아보자
계산을 하기 위해서는 두 행렬의 행벡터의 크기가 같아야 계산이 가능하다.

  • 첫 번째 행의 첫 번째 원소인 -5
    -5 = 1 · 0 + (-2) · 1 + 3 · (-1) = 0 + -2 + -3
  • 두 번째 행의 두 번째 원소인 2의 경우
    2 = 7 · 1 + 5 · -1 + 0 · 0 = 7 + (-5) + 0

행렬을 이해하자 (2)

행렬은 벡터공간에서 사용되는 연산자(operator)로 이해한다.
이번에는 데이터를 저장하는 것이 아니라 서로 다른 데이터들을 연결시켜주는 연산자 역할로 알아보자.

  • 행렬곱을 통해 벡터를 다른 차원의 공간으로 보낼 수 있다.
zi=jaijxjz_i = \sum_j a_{ij} x_j
[z1z2zn]=[a11a12a1ma21a22a2man1an2anm][x1x2xm]\begin{bmatrix} z_1\\ z_2\\ \vdots\\ z_n \end{bmatrix} = \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} \begin{bmatrix} x_1\\ x_2\\ \vdots\\ x_m \end{bmatrix}
z=Ax\mathbf{z} = \mathbf{A} \mathbf{x}

A는 n개의 행과 m개의 열로 이뤄져 있다.
결과물인 Z는 n개의 행으로 이뤄져 있는 열벡터가 된다.

  • Z의 i번째 원소는
    X의 j번째 원소(xjx_j)와 행렬 A의 각 성분(AijA_{ij})을 곱해서
    모든 j에 대해 더한 값으로 만들어진다.
zi=j=1maijxjz=Axz_i = \sum_{j=1}^m a_{ij} x_j \quad \Longleftrightarrow \quad \mathbf{z} = \mathbf{A}\mathbf{x}

행렬 A와 벡터 X 사이의 곱으로 표현되는 연산과 같다.

  • 행렬곱을 통해 벡터를 다른 차원의 공간으로 보낼 수 있다.
  • 행렬곱을 통해 패턴을 추출할 수 있고 데이터를 압축할 수도 있다.

모든 선형변환(linear transform)은 행렬곱으로 계산할 수 있다.

딥러닝은 선형변환과 비선형변환으로 이루어져 있기 때문에 연산이 어떻게 이뤄지는지 정확하게 이해를 해야 한다.

역행렬 이해하기

어떤 행렬 A의 연산을 거꾸로 되돌리는 행렬을 역행렬(inverse matrix)이라 부르고 A1A^{-1} 라 표기한다.
역행렬은 행과 열 숫자가 같고 행렬식(determinant)이 0이 아닌 경우에만 계산할 수 있다.

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

numpy에서는 numpy.linalg.inv로 구할 수 있다.
주어진 행렬의 역행렬을 이용할 수 있다.

역행렬과 원래 주어진 행렬을 곱해주게 되면,
대각선에 오는 원소들은 거의 1이고, 나머지는 0에 가까운 숫자가 나오게 된다.

만일 역행렬을 계산할 수 없다면, 유사역행렬(pseudo-inverse) 또는 무어-펜로즈(Moore-Penrose) 역행렬 A+A^{+}을 이용한다.

  • n >= m 인 경우

    A+=(AA)1A\mathbf{A}^+ = (\mathbf{A}^\top \mathbf{A})^{-1} \mathbf{A}^\top
  • n <= m인 경우

A+=A(AA)1\mathbf{A}^+ = \mathbf{A}^\top (\mathbf{A} \mathbf{A}^\top)^{-1}
  • n >= m 이면 A+A=IA^{+}A=I가 성립하고
    n <= m 이면 AA+=IAA^{+}=I만 성립한다.

응용 부분은.. 이어서 수정해놓겠습니다.
너무너무어려웠고요,. 망한재수시절에 기하 안했으면 진짜 영영 못 알아들었을 것만 같은 내용이었어요..

Reference

profile
Whatever I want | Interested in DFIR, Security, Infra, Cloud

0개의 댓글