6.13. 선형 대수#

카메라에서의 선형 대수는 작은 행렬 작업입니다: IMU 샘플을 월드 프레임에 융합하는 3x3 회전, 렌즈를 보정하는 보정 행렬, 칼만 필터의 상태-공분산 업데이트, 정규 방정식이 작은 선형 풀이로 나오는 다항식 적합 등입니다. numpy.linalgscipy.linalg 서브모듈은 바로 그 규모를 다룹니다.

이 함수들은 두 모듈에 들어 있습니다. 행렬 곱 연산은 numpy 최상위 레벨에 있고, 분해와 행렬 역은 numpy.linalg 아래에 있으며, 전용 선형 시스템 풀이는 scipy.linalg 아래에 있습니다. 예를 들어 2x2 행렬 곱은 다음과 같습니다:

from ulab import numpy as np

A = np.array([[1, 2], [3, 4]], dtype=np.float)
B = np.array([[5, 6], [7, 8]], dtype=np.float)
np.dot(A, B)
# array([[19.0, 22.0],
#        [43.0, 50.0]])

6.13.1. 사용 가능한 것#

  • dot() – 행렬 또는 벡터 곱.

  • cross() – 3차원 벡터 외적.

  • trace() – 대각합.

  • inv() – 행렬 역.

  • det() – 행렬식.

  • cholesky() – 촐레스키 분해(대칭 양의 정부호 입력).

  • eig() – 실수 대칭 행렬의 고유값과 고유벡터.

  • norm() – 벡터 또는 행렬의 2-노름.

  • qr()mode='reduced' (기본값) 또는 mode='complete' 를 사용한 QR 분해.

  • solve_triangular()A 가 삼각 행렬일 때 A @ x = b 를 풉니다.

  • cho_solve()A 의 촐레스키 인수가 주어졌을 때 A @ x = b 를 풉니다.

6.13.2. dot, cross, trace#

dot() 은 카메라에서 행렬 곱셈을 작성하는 방법입니다. 데스크톱 numpy 가 동일한 작업을 위해 제공하는 @ 연산자는 ndarray 에 구현되어 있지 않으므로, 모든 행렬-벡터, 행렬-행렬, 벡터 내적은 dot() 호출을 거칩니다:

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

np.dot(a, b)             # 32.0 (scalar product)
np.cross(a, b)           # array([-3.0, 6.0, -3.0])

m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
np.dot(m, a)             # matrix-vector product
np.dot(m, m)             # matrix-matrix product
np.trace(m)              # 1 + 5 + 9 = 15.0

dot() 의 결과는 항상 dtype float 입니다. cross() 는 두 3-벡터의 외적이고, trace() 는 정방 행렬의 주대각의 합입니다.

6.13.3. inv와 det#

m = np.array([[1, 2, 3, 4],
              [4, 5, 6, 4],
              [7, 9, 9, 4],
              [3, 4, 5, 6]])
print(np.linalg.inv(m))
print(np.linalg.det(m))

역은 가우스-조던 소거법으로 계산되므로, 행렬이 특이(singular)할 때 inv()ValueError 를 발생시킵니다(소거 중에 대각 요소가 0이 됨). RAM 비용은 대략 입력 크기의 두 배입니다.

행렬식은 동일한 소거를 재사용합니다 – 실행 시간은 본질적으로 역과 같습니다.

애플리케이션의 목표가 선형 시스템을 푸는 것일 때는, 역을 구해서 곱하지 마십시오 – 아래의 전용 풀이를 선호하십시오. 둘 다 더 빠르고 수치적으로 더 잘 작동합니다.

6.13.4. cholesky#

대칭 양의 정부호 행렬 A 에 대해, cholesky()A = L @ L.T 가 되는 하삼각 L 을 반환합니다:

a = np.array([[25, 15, -5],
              [15, 18,  0],
              [-5,  0, 11]])
L = np.linalg.cholesky(a)

입력이 양의 정부호가 아니거나 대칭이 아니면 ValueError 가 발생합니다.

촐레스키 인수는 LU 분해의 절반에 해당하는 작업량이며, 행렬이 대칭 양의 정부호임이 알려진 모든 문제(공분산 업데이트, 최소제곱 적합에서 나오는 정규 방정식)에 대한 올바른 출발점입니다.

6.13.5. eig#

eig()실수 대칭 행렬에서만 작동합니다. 비대칭 행렬은 ValueError 를 발생시킵니다. 이것은 2-튜플 (eigenvalues, eigenvectors) 를 반환합니다:

a = np.array([[1, 2, 1, 4],
              [2, 5, 3, 5],
              [1, 3, 6, 1],
              [4, 5, 1, 7]], dtype=np.uint8)
x, y = np.linalg.eig(a)

참고:

  • 고유값은 특정 순서 없이 반환됩니다. 정렬된 순서가 중요할 때는 sort() 를 적용하십시오(그리고 argsort() 를 통해 고유벡터에도 동일한 순열을 적용하십시오).

  • 고유벡터는 0이 아닌 스칼라 배율까지만 유일하므로, 개별 고유벡터의 부호 는 유일하게 정의되지 않습니다. 두 번의 올바른 실행이 부호가 반대인 벡터를 만들어낼 수 있으나, 이는 무해합니다.

6.13.6. norm#

벡터 또는 행렬의 유클리드(프로베니우스) 노름:

v = np.array([1, 2, 3, 4, 5])
m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

np.linalg.norm(v)        # 7.416...
np.linalg.norm(m)        # 16.881...

선택적 axis= 키워드는 전체 배열이 아니라 단일 축을 따라 노름을 취합니다.

6.13.7. qr#

qr() 는 직사각 행렬 A (형태 (M, N))를 A == Q @ R 가 되는 정규 직교 Q 와 상삼각 R 로 분해합니다:

A = np.arange(6).reshape((3, 2))

q, r = np.linalg.qr(A)
# mode='reduced' (default): q is (3, 2), r is (2, 2)

q, r = np.linalg.qr(A, mode='complete')
# q is (3, 3), r is (3, 2)

이 분해는 연속적인 기븐스 회전을 통해 구현됩니다. 행렬이 대칭이 아닌 최소제곱 문제에 적합한 선택입니다.

6.13.8. 시스템 풀이#

ulab.scipy.linalg 아래의 두 전용 풀이는 np.dot(np.linalg.inv(A), b) 보다 둘 다 더 빠르고 더 정확합니다:

  • solve_triangular(a, b, lower=False)()a 가 삼각 행렬이라고 가정하고 a @ x = b 를 풉니다:

    A = np.array([[3, 0, 0, 0],
                  [2, 1, 0, 0],
                  [1, 0, 1, 0],
                  [1, 2, 1, 8]])
    b = np.array([4, 2, 4, 2])
    x = sp.linalg.solve_triangular(A, b, lower=True)
    
  • cho_solve(L, b)() – 촐레스키 인수 L 이 주어졌을 때 A = L @ L.TA @ x = b 를 풉니다:

    L = np.linalg.cholesky(A)
    x = sp.linalg.cho_solve(L, b)
    

A 의 구조가 허용할 때마다 역을 구하는 대신 이것들을 활용하십시오 – 소거 작업 그리고 명시적 역 계산을 절약합니다.

6.13.9. 작은 선형 시스템 풀기#

A = np.array([[3, 0, 1, 1],
              [0, 1, 0, 2],
              [1, 0, 1, 1],
              [1, 2, 1, 8]])
b = np.array([4, 2, 4, 2])

x = np.dot(np.linalg.inv(A), b)
print(x)
print(np.dot(A, x))         # should equal b

동일한 문제를 A 를 분해하고 적절한 풀이를 호출하여 표현할 수도 있습니다 – A 가 적절한 구조를 가질 때 더 빠르고 더 정확합니다.

완전한 인수 수준 참조는 numpy.linalg — 선형대수 루틴scipy.linalg — 선형 대수 루틴 를 참조하십시오.