6.13. 선형 대수¶
카메라에서의 선형 대수는 작은 행렬 작업입니다: IMU 샘플을 월드 프레임에 융합하는 3x3 회전, 렌즈를 보정하는 보정 행렬, 칼만 필터의 상태-공분산 업데이트, 정규 방정식이 작은 선형 풀이로 나오는 다항식 적합 등입니다. numpy.linalg 와 scipy.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)
참고:
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.T인A @ 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 — 선형 대수 루틴 를 참조하십시오.