np.linalg: решение систем, обратная матрица, разложения
Урок — обзор практической линейной алгебры в NumPy: как решать системы, обращать матрицы и применять разложения, понимая, зачем они нужны.
np.linalg — подмодуль NumPy с операциями линейной алгебры (решение систем, обратная матрица, определитель, собственные значения, разложения), реализованными через быстрые библиотеки LAPACK.
Решение систем линейных уравнений: solve
Классическая задача: найти x в системе A·x = b, где A — матрица коэффициентов, b — вектор правых частей. Наивный путь — вычислить обратную матрицу и умножить: x = A⁻¹·b. Но это и медленнее, и менее точно. Правильный инструмент — np.linalg.solve(A, b), который решает систему напрямую (через разложение), не вычисляя обратную матрицу.
import numpy as np
# 2x + y = 5
# x + 3y = 10
A = np.array([[2.0, 1.0],
[1.0, 3.0]])
b = np.array([5.0, 10.0])
x = np.linalg.solve(A, b)
print(x) # [x, y]
print(np.allclose(A @ x, b)) # проверка: подставили обратно
Вывод:
[1. 3.] True
Решение x=1, y=3 подтверждается обратной подстановкой. Запомните правило: почти никогда не нужно явно обращать матрицу для решения системы — используйте solve.
Линейная алгебра — почему это сердце ML
Стоит объяснить, почему линейной алгебре уделяется столько внимания именно в курсе по NumPy. Дело в том, что подавляющее большинство методов анализа данных и машинного обучения формулируются на её языке. Линейная регрессия — это решение системы (или метод наименьших квадратов) для нахождения коэффициентов. Метод главных компонент (PCA) — это SVD или собственное разложение ковариационной матрицы. Рекомендательные системы раскладывают матрицу «пользователи × товары». Нейросети — это каскады матричных умножений с нелинейностями между ними. Даже простое сглаживание, фильтрация сигналов, решение дифференциальных уравнений сводятся к матричным операциям. Поэтому, освоив np.linalg, вы получаете не узкий набор математических функций, а фундаментальный инструментарий, на котором стоит вся прикладная численная математика. И поскольку NumPy реализует эти операции через оптимизированные библиотеки, вы получаете производительность мирового уровня «бесплатно» — нужно лишь правильно сформулировать задачу. Именно эта связка «выразительный высокоуровневый интерфейс + быстрые библиотеки внизу» делает NumPy фундаментом научного Python.
Обратная матрица и определитель: inv, det
np.linalg.inv(A) даёт обратную матрицу (такую, что A·A⁻¹ = E), а np.linalg.det(A) — определитель. Определитель полезен как индикатор: если он близок к нулю, матрица вырождена (необратима), и система либо не имеет решения, либо имеет их бесконечно много. Попытка обратить такую матрицу даёт ошибку или огромные неустойчивые числа.
import numpy as np
A = np.array([[2.0, 1.0],
[1.0, 3.0]])
print(np.linalg.det(A)) # определитель: 2*3 - 1*1 = 5
inv = np.linalg.inv(A)
print(inv)
print(np.allclose(A @ inv, np.eye(2))) # A·A⁻¹ = E
Вывод:
5.000000000000001 [[ 0.6 -0.2] [-0.2 0.4]] True
Определитель 2×2 считается элементарно — это видно на чистом Python (для интуиции):
def det2(m):
return m[0][0] * m[1][1] - m[0][1] * m[1][0]
A = [[2, 1], [1, 3]]
print("det =", det2(A))
print("Вырождена?" , abs(det2(A)) < 1e-12)
Вывод:
det = 5 Вырождена? False
Что значит «обусловленность» и почему она важна
Определитель говорит, обратима ли матрица в принципе, но на практике важнее более тонкое понятие — обусловленность. Матрица может быть формально обратима (определитель не равен нулю), но «почти вырождена»: её определитель крошечный, и обращение усиливает любые погрешности входных данных в огромное число раз. Решение системы с плохо обусловленной матрицей численно неустойчиво — крошечная ошибка в правой части даёт огромную ошибку в ответе. Это коварно, потому что код не падает, а молча возвращает неправильный результат. Признак проблемы — очень большие или очень маленькие числа в обратной матрице, странно большие сингулярные числа, определитель, близкий к машинному нулю. Когда вы решаете системы или обращаете матрицы из реальных данных, полезно проверять обусловленность (через np.linalg.cond) и с осторожностью относиться к результатам, если она велика. Это объясняет, почему solve предпочтительнее inv: он не только быстрее, но и численно устойчивее, так как не вычисляет потенциально неустойчивую обратную матрицу явно.
Собственные значения и векторы: eig
Собственные значения и векторы — фундамент многих методов (PCA, анализ устойчивости, спектральные методы). Собственный вектор v матрицы A — это направление, которое матрица лишь масштабирует, не поворачивая: A·v = λ·v, где λ — собственное значение (коэффициент масштаба). np.linalg.eig возвращает массив собственных значений и матрицу собственных векторов (по столбцам).
import numpy as np
A = np.array([[2.0, 0.0],
[0.0, 3.0]])
values, vectors = np.linalg.eig(A)
print("Собственные значения:", values)
print("Собственные векторы (по столбцам):")
print(vectors)
Вывод:
Собственные значения: [2. 3.] Собственные векторы (по столбцам): [[1. 0.] [0. 1.]]
Для диагональной матрицы собственные значения — это сами диагональные элементы, а собственные векторы — оси координат. В общем случае они нетривиальны и раскрывают «внутреннюю геометрию» преобразования.
Сингулярное разложение: svd
SVD (singular value decomposition) — пожалуй, самое полезное разложение в анализе данных. Любую матрицу A можно представить как A = U·Σ·Vᵀ, где Σ — диагональ из неотрицательных сингулярных чисел, упорядоченных по убыванию. SVD лежит в основе понижения размерности (PCA), сжатия, рекомендательных систем, вычисления псевдообратной матрицы. np.linalg.svd возвращает три части разложения.
import numpy as np
A = np.array([[3.0, 0.0],
[0.0, 2.0],
[0.0, 0.0]])
U, S, Vt = np.linalg.svd(A)
print("Сингулярные числа:", S) # по убыванию
print("U.shape:", U.shape, "Vt.shape:", Vt.shape)
Вывод:
Сингулярные числа: [3. 2.] U.shape: (3, 3) Vt.shape: (2, 2)
Сингулярные числа показывают «важность» каждого направления. Оставив только крупнейшие из них, можно приблизить матрицу меньшим объёмом данных — это и есть идея сжатия и PCA.
Геометрический смысл собственных значений и SVD
Чтобы линейная алгебра не воспринималась как набор магических функций, полезна геометрическая картина. Любую матрицу можно мыслить как преобразование пространства: она растягивает, сжимает, поворачивает и отражает векторы. Собственные векторы — это особые направления, которые преобразование не поворачивает, а только масштабирует, и собственное значение говорит, во сколько раз. SVD идёт дальше и раскладывает любое преобразование на три простых шага: поворот, масштабирование вдоль осей (на сингулярные числа) и ещё один поворот. Поэтому сингулярные числа показывают, насколько сильно преобразование «растягивает» пространство в каждом из главных направлений. Если одно из них близко к нулю, преобразование почти «схлопывает» соответствующее направление — это и есть геометрический смысл вырожденности. А в анализе данных крупнейшие сингулярные числа указывают направления наибольшей изменчивости данных, что и лежит в основе метода главных компонент (PCA): отбросив мелкие сингулярные числа, мы сохраняем главную структуру данных, теряя лишь «шум». Эта геометрическая интуиция делает абстрактные разложения осязаемыми.
Нормы: norm
np.linalg.norm вычисляет «длину» вектора или «величину» матрицы. Для вектора по умолчанию это евклидова норма (корень из суммы квадратов) — геометрическая длина. Нормы повсюду: измерение расстояний, регуляризация, нормировка векторов в единичную длину.
import numpy as np
v = np.array([3.0, 4.0])
print(np.linalg.norm(v)) # 5.0 — евклидова длина (теорема Пифагора)
print(np.linalg.norm(v, 1)) # 7.0 — сумма модулей (L1)
print(v / np.linalg.norm(v)) # нормировка в единичную длину
Вывод:
5.0 7.0 [0.6 0.8]
Евклидова норма [3, 4] равна 5 — это гипотенуза прямоугольного треугольника. Проверим формулу на чистом Python:
import math
def euclid_norm(v):
return math.sqrt(sum(x * x for x in v))
print(euclid_norm([3, 4])) # sqrt(9 + 16) = sqrt(25)
print(euclid_norm([1, 2, 2])) # sqrt(1 + 4 + 4) = 3
Вывод:
5.0 3.0
Когда применять какое разложение
Линейная алгебра богата на инструменты, и новичку трудно понять, что когда брать. Дадим ориентир по задачам. Нужно решить систему уравнений (найти неизвестные по уравнениям) — solve. Нужно понять, обратима ли матрица или насколько она «хороша» — det, matrix_rank, cond. Нужно найти главные направления и понизить размерность данных — svd (основа PCA). Нужно проанализировать устойчивость системы, найти резонансы, диагонализовать преобразование — eig. Нужно измерить длину или расстояние — norm. Нужно решить переопределённую систему методом наименьших квадратов (больше уравнений, чем неизвестных, как в линейной регрессии) — lstsq. Эта карта «задача → инструмент» важнее, чем заучивание сигнатур: понимая, какой вопрос вы задаёте данным, вы быстро находите нужную функцию. А детали вызова всегда можно посмотреть в документации. Линейная алгебра — это язык, на котором формулируются регрессия, понижение размерности, рекомендательные системы и многое в машинном обучении, поэтому понимание «что зачем» окупается во всей дальнейшей работе с данными.
Сводка np.linalg
| Функция | Назначение |
solve(A, b) | решить систему A·x = b (предпочтительно) |
inv(A) | обратная матрица |
det(A) | определитель (индикатор вырожденности) |
eig(A) | собственные значения и векторы |
svd(A) | сингулярное разложение |
norm(x) | норма (длина) вектора или матрицы |
matrix_rank(A) | ранг матрицы |
Подводные камни
- Решать систему через inv.
inv(A) @ bмедленнее и менее точно, чемsolve(A, b). - Обращать вырожденную матрицу. Если
detблизок к нулю,invдаст мусор или ошибку. Проверяйте. - Точность float. Результаты содержат крошечные погрешности (например, det = 5.0000000000001). Сравнивайте через
allclose. - Комплексные собственные значения.
eigдля некоторых матриц возвращает комплексные числа — это нормально, учитывайте dtype результата.
Лучшие практики
- Для решения систем всегда используйте
solve, а не явное обращение матрицы. - Проверяйте вырожденность через
detилиmatrix_rankперед обращением. - Проверяйте корректность через обратную подстановку и
np.allclose. - SVD — ваш универсальный инструмент для понижения размерности и устойчивых вычислений.
Главная мысль этого урока — не запомнить все функции, а понять, что NumPy даёт готовый, надёжный и быстрый инструментарий для всей классической линейной алгебры, опираясь на проверенные библиотеки LAPACK. Ваша роль — правильно сформулировать задачу (решить систему? разложить? измерить?) и выбрать соответствующую функцию, доверив численные тонкости и скорость библиотеке. Понимание геометрического смысла операций и численных подводных камней (обусловленность, точность float) превращает эти функции из «чёрных ящиков» в осмысленные инструменты.
Итог
solveрешает системы напрямую — быстрее и точнее, чем черезinv.detсигнализирует о вырожденности; близкий к нулю определитель означает необратимость.eigиsvdраскрывают внутреннюю структуру матрицы; SVD — основа PCA и сжатия.normизмеряет длину; евклидова норма — геометрическое расстояние.