Матричные разложения#
В данном курсе нам часто будут встречаться различные матричные разложения. Перечислим основные из них с некоторыми примерами использования.
Сингулярное разложение (SVD)#
\(r=\operatorname{rank}(A)\)
\(U, V\) - унитарные (\(U^{*}U=E\))
\(\sigma_1 \geq \ldots \geq \sigma_r>0\) - ненулевые сингулярные числа. Для эрмитовых матриц равны модулям собственных чисел.
Столбцы \(U, V\) - сингулярные вектора.
Существует всегда.
Note: SVD также может быть определена для \(U \in \mathbb{C}^{m \times p}\), \(\Sigma \in \mathbb{R}^{p \times p}\) и \(V \in \mathbb{C}^{n \times p}, p=\min \{n . m\}\)
Алгоритмы построения#
Спектральное разложение \(A A^*\) или \(A^* A\) совпадает со спектральным разложением (собственное разложение).
Стабильный алгоритм \(\mathcal{O}\left(m n^2\right)\) , \((m>n):\)
Bidiagonalize \(A\) by Householder reflections
\[ A=U_1 B V_1^* \]
Find SVD of \(B=U_2 \Sigma V_2^*\) by spectral decomposition of \(T\) ( 2 options).
\(U=U_1 U_2, \quad V=V_1 V_2\)
Области применения#
Сжатие данных
Эффективное вычисление псевдообратной матрицы
Определение ключевых признаков в Data Science
Спектральное разложение#
\(\lambda_1, \ldots, \lambda_n\) - собственные числа.
Столбцы \(S\) - собственные вектора.
Не всегда существует (в отличие от верхнетреугольного вида)
Алгоритмы построения#
Если \(A=A^*\), Jacobi method: \(\mathcal{O}\left(n^3\right)\)
Если \(A A^*=A^* A\), QR algorithm: \(\mathcal{O}\left(n^3\right)\)
Если \(A A^* \neq A^* A\) ассимптотика также \(\mathcal{O}\left(n^3\right)\)
Области применения#
Полное спектральное разложение используется редко, т.к. для вычисления необходимы все собственные вектора.
Для вычисления собственных чисел лучше использовать верхнетреугольную форму (существует алгоритм за \(\mathcal{O}\left(n^3\right)\))
LU-разложение (Разложение Холецкого)#
Существует только при ненулевых лидирующих минорах.
Уникально.
Если матрица \(A\) - симметрична и положительно определена, LU-разложение можно модифицировать в разложение Холецкого:
Алгоритмы построения#
Different versions of Gaussian elimination, \(\mathcal{O}\left(n^3\right)\) flops. In LU for stability use permutation of rows or columns (LUP).
\(\mathcal{O}\left(n^3\right)\) can be decreased for sparse matrices by appropriate permutations, e.g.
minimum degree ordering
Cuthill-Mckee algorithm
Banded matrix with bandwidth \(b\)
can be decomposed using \(\mathcal{O}\left(n b^2\right)\) flops.
Области применения#
LU и более высокоуровневые модификации LDL, Холецкий используются для
Решения СЛАУ. Пусть \(A=L U\), тогда сложность решения \(A x=b\) есть \(\mathcal{O}\left(n^2\right)\) :
Решим простую систему: \(L y=b\)
Решим обратную систему: \(U x=y\)
Обращения матриц
Вычисления детерминанта
Как часть других разложений.
Пример 1 - решить СЛАУ с помощью LU-разложения#
Пусть СЛАУ:
Тогда хотим:
Для чего последовательно решаем:
Будем использовать следующие обозначения для элементов матриц: \(A=\left(a_{i j}\right), L=\left(l_{i j}\right), U=\left(u_{i j}\right), i, j=1 \ldots n\); причём диагональные элементы матрицы \(L: l_{i i}=1, i=1 \ldots n\).
Найти матрицы \(L\) и \(U\) можно следующим образом (выполнять шаги следует строго по порядку, так как следующие элементы находятся с использованием предыдущих):
Цикл і от 1 до n
Цикл ј от 1 до \(\mathrm{n}\)
\(u_{i j}=0, l_{i j}=0\)
\(l_{i i}=1\)
Цикл і от 1 до \(\mathrm{n}\)
Цикл ј от 1 до \(\mathrm{n}\)
Если \(i \leq j: u_{i j}=a_{i j}-\sum_{k=1}^{i-1} l_{i k} \cdot u_{k j}\)
Если \(i>j: l_{i j}=\left(a_{i j}-\sum_{k=1}^{j-1} l_{i k} \cdot u_{k j}\right) / u_{j j}\)
В итоге мы получим матрицы - \(L\) и \(U\).
import numpy as np
def lu_decomposition(A):
n = A.shape[0]
U = np.zeros((n, n))
L = np.eye(n)
for i in range(n):
U[i, i:] = A[i, i:] - L[i, :i] @ U[:i, i:]
L[i+1:, i] = (A[i+1:, i] - L[i+1:, :i] @ U[:i, i]) / U[i, i]
return L, U
A = np.array([[1, 7, 3], [4, 5, 6], [7, 8, 9.]])
L, U = lu_decomposition(A)
A - L @ U
array([[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, -1.77635684e-15, 0.00000000e+00]])
b = np.array([1., 2., 3.]) # столбец свободных членов
# Решение Ly = b
y = b*0
for i in range(L.shape[0]):
y[i] = b[i] - L[i, :i] @ y[:i]
# Решение Ux = y
x = b*0
for i in range(U.shape[0]-1, -1, -1):
x[i] = (y[i] - U[i, i+1:] @ x[i+1:])/U[i, i]
# Проверка
x - np.linalg.inv(A) @ b
array([ 1.11022302e-16, -8.32667268e-17, 1.66533454e-16])
Пример 2 - решение МНК через Холецкого#
Вспомним что для заданной матрицы объекты-признаки \(X\) наилучшие веса в MSE находились по формуле:
Что эквивалентно решению системы:
С симметричной положительно определенной матрицей \(A=(X^\top X)\) и столбцом свободных членов \(\mathbf{b} = X^\top \cdot \mathbf{y}\). Намечается расзложение Холецкого, для тех кто не понял :).
Его плюс в данном случае - скорость выполнения (т.к. нам не нужно явно считать обратную).
import numpy as np
from numba import njit
def cholesky_solve(A, b):
L = np.linalg.cholesky(A)
# Решение Ly = b
y = np.zeros_like(b)
for i in range(L.shape[0]):
y[i] = (b[i] - L[i, :i] @ y[:i])/L[i, i]
# Решение L.T x = y
x = np.zeros_like(b)
for i in range(L.shape[1]-1, -1, -1):
x[i] = (y[i] - L[i+1:, i] @ x[i+1:])/L[i, i]
return x
A = np.array([[1, 7, 3], [4, 5, 6], [7, 8, 9.]])
try:
L = np.linalg.cholesky(A) # Только для положительно определенных!
except Exception as error:
print(error)
Matrix is not positive definite
# Фит многочленами с прошлого семинара
import numpy as np
# Генерируем выборку для исследования
x = 2*np.random.random(1000000).reshape((-1, 1)) - 1
noise = 0.1*np.random.normal(size = x.shape)
y_label = -4 + 3*x - 2*x**2 + 5*x**3 + noise
# Напишем функцию для создания матрицы объекты-признаки
def create_X(x, m):
X = np.ones(x.shape) # Сразу же добавим intercept
for i in range(1, m + 1):
X = np.concatenate([X, x**i], axis = 1)
return X
X = create_X(x, 5)
A = X.T @ X
L = np.linalg.cholesky(A)
L @ L.T - A
array([[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, 5.82076609e-11, 0.00000000e+00,
-2.91038305e-11, 0.00000000e+00, -2.91038305e-11],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, -2.91038305e-11, 0.00000000e+00,
0.00000000e+00, 2.84217094e-14, 0.00000000e+00],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
2.84217094e-14, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, -2.91038305e-11, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00]])
%%time
w_pinv = np.linalg.inv(A) @ X.T @ y_label # Вычисление обратной O(n^3)
CPU times: user 0 ns, sys: 15.6 ms, total: 15.6 ms
Wall time: 55.1 ms
%%time
w_chol = cholesky_solve(A, X.T @ y_label) # А решение СЛАУ за O(n^2)
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 5.23 ms
w_chol - w_pinv
array([[ 7.63833441e-14],
[-2.41584530e-13],
[-6.11954931e-13],
[ 6.39488462e-13],
[ 7.05184609e-13],
[-5.01479500e-13]])