Линейная алгебра
Системы линейных уравнений: от школы до TensorFlow
Линейная регрессия, фильтр Калмана, метод наименьших квадратов - все они в итоге сводятся к решению системы линейных уравнений. Понять, когда система совместна, когда имеет единственное решение, а когда множество - значит понять фундамент половины методов ML.
- Линейная регрессия: нормальное уравнение (XᵀX)w = Xᵀy - система линейных уравнений
- Компьютерная графика: нахождение точки пересечения лучей - система 3 уравнений
- Электротехника: законы Кирхгофа для сети с n узлами - система n уравнений
- Экономика: равновесие рынков в модели Вальраса - большая линейная система
- GPS: определение координат из 4 уравнений дальности до спутников
Системы линейных уравнений: от школы до TensorFlow
**Каждую секунду numpy.linalg.solve вызывается миллионы раз по всему миру** - в физических симуляторах, финансовых моделях, нейросетях, компьютерном зрении. За этим вызовом стоит ровно одно уравнение: **Ax = b**. Трансформация A известна, результат b известен, нужен исходный вектор x. Школьная система уравнений - это то же самое, просто записанное построчно. Матричная запись позволяет решать её за O(n²) вместо O(n³) и использовать GPU.
**О чём этот урок на самом деле**: не "как решать задачи в столбик", а три практических вопроса. Первый: как работает np.linalg.solve и почему inv(A) @ b медленнее. Второй: что делать с переопределёнными системами (больше уравнений чем неизвестных) - это 90% задач в ML. Третий: conjugate gradient и зачем он нужен когда матрица гигантская.
Что главное в концепте «Системы линейных уравнений: от школы до TensorFlow»?
Проверка усвоения материала концепта.
Три формы одного уравнения
Три формы одного уравнения
1. КЛАССИЧЕСКАЯ (школьная): 2x + 3y - z = 5 x - y + 2z = 1 3x + y + z = 4 2. МАТРИЧНАЯ: A · x = b ┌ ┐ ┌ ┐ ┌ ┐ │ 2 3 -1 │ │ x │ │ 5 │ │ 1 -1 2 │ · │ y │ = │ 1 │ │ 3 1 1 │ │ z │ │ 4 │ └ ┘ └ ┘ └ ┘ 3. СТОЛБЦОВАЯ (комбинация столбцов): ┌ 2 ┐ ┌ 3 ┐ ┌ -1 ┐ ┌ 5 ┐ x · │ 1 │ + y · │ -1 │ + z · │ 2 │ = │ 1 │ └ 3 ┘ └ 1 ┘ └ 1 ┘ └ 4 ┘ Вопрос столбцовой формы: «Как представить вектор b в виде линейной комбинации столбцов A?» Коэффициенты x, y, z - это и есть решение.
**Столбцовая интерпретация мощнее**: она напрямую связана с рангом матрицы и понятием линейной оболочки. Если b лежит в пространстве, натянутом на столбцы A - решение есть. Не лежит - решения нет. Именно отсюда вырастает теория совместности систем.
Что главное в концепте «Три формы одного уравнения»?
Проверка усвоения материала концепта.
Сколько решений: три случая
Сколько решений: три случая
| Случай | Геометрия (3D) | Условие | В ML |
|---|---|---|---|
| Одно решение | Три плоскости пересекаются в точке | det(A) ≠ 0, ранг = n | Точное обучение (редко) |
| Бесконечно много | Плоскости пересекаются в прямой | det(A) = 0, b в образе A | Underdetermined (n < d в признаках) |
| Нет решений | Плоскости не имеют общей точки | b вне образа A | Overdetermined - lstsq! |
Что главное в концепте «Сколько решений: три случая»?
Проверка усвоения материала концепта.
np.linalg.solve vs inv: почему inv медленнее
np.linalg.solve vs inv: почему inv медленнее
Наивный способ решить Ax = b - вычислить A⁻¹ и умножить: x = A⁻¹ · b. Это **неправильный** подход в production-коде. np.linalg.solve(A, b) быстрее и точнее, потому что использует LU-разложение напрямую, без вычисления обратной матрицы.
**Почему solve быстрее**: np.linalg.inv строит полную обратную матрицу O(n³), потом умножает A⁻¹ · b за O(n²). np.linalg.solve сразу решает через LU-разложение с пивотингом - один проход вместо двух, меньше округлений. В LAPACK это функция dgesv. При 10 правых частях b₁...b₁₀ solve вызывается один раз с матрицей B - снова выигрыш.
Что главное в концепте «np.linalg.solve vs inv: почему inv медленнее»?
Проверка усвоения материала концепта.
Переопределённые системы: lstsq и нормальное уравнение
Переопределённые системы: lstsq и нормальное уравнение
В ML почти всегда уравнений **больше** чем неизвестных: 10000 примеров, 100 признаков. Точного решения нет - b не лежит в образе A. Выход: найти x*, который **минимизирует** ||Ax - b||². Это задача ~наименьших квадратов~{least squares: min ||Ax - b||²}.
Задача: min ||Ax - b||² при A размера (m x n), m > n Теорема: минимум достигается при AᵀA · x* = Aᵀb Если AᵀA обратима (что бывает при полном ранге A): x* = (AᵀA)⁻¹ Aᵀ b ЭТО И ЕСТЬ линейная регрессия! X = матрица признаков (n_samples x n_features) y = вектор меток w* = (XᵀX)⁻¹ Xᵀy ПРОБЛЕМА нормального уравнения: XᵀX может быть плохо обусловлена (κ >> 1) → использовать scipy.linalg.lstsq или sklearn LinearRegression (внутри они используют SVD, а не явное обращение)
**Когда lstsq лучше нормального уравнения**: всегда, когда данные потенциально мультиколлинеарны или матрица признаков плохо обусловлена. lstsq использует SVD внутри - даже при ранг-дефицитной матрице выдаёт минимальную по норме ||x*|| псевдорешение. sklearn LinearRegression делает ровно то же самое.
Что главное в концепте «Переопределённые системы: lstsq и нормальное уравнение»?
Проверка усвоения материала концепта.
LU, Cholesky, QR: выбор алгоритма по ситуации
LU, Cholesky, QR: выбор алгоритма по ситуации
np.linalg.solve умеет выбирать алгоритм автоматически, но понимание того, какой алгоритм используется внутри, помогает диагностировать проблемы и выбирать scipy-функции явно.
| Алгоритм | Когда применять | Сложность | Пример |
|---|---|---|---|
| LU (DGESV) | Общая квадратная матрица | O(n³/3) | np.linalg.solve по умолчанию |
| Cholesky | Симметричная positive definite | O(n³/6) | scipy.linalg.cho_solve - Ridge, GP |
| QR | Переопределённые системы | O(mn²) | scipy.linalg.lstsq при m >> n |
| SVD | Ранг-дефицитные, псевдообратная | O(mn²) | np.linalg.lstsq внутри, LoRA |
Что главное в концепте «LU, Cholesky, QR: выбор алгоритма по ситуации»?
Проверка усвоения материала концепта.
Conjugate Gradient: когда матрица не помещается в память
Conjugate Gradient: когда матрица не помещается в память
LU-разложение требует O(n³) операций и O(n²) памяти. При n = 10⁶ (типично для задач на графах, PDE, больших sparse систем) это невозможно. ~Метод сопряжённых градиентов~{conjugate gradient: итеративный метод для SPD систем} (CG) решает Ax = b итерационно: каждый шаг O(n) умножений, сходится за ≤ n шагов.
Задача: min f(x) = (1/2) xᵀAx - bᵀx Градиент: ∇f(x) = Ax - b = 0 → это и есть Ax = b ЭТО ТА ЖЕ ЗАДАЧА что и решение системы! CG решает её как задачу оптимизации. КАЖДЫЙ ШАГ: r_k = b - A·x_k (невязка, residual) p_k = -r_k + β·p_{k-1} (сопряжённое направление) x_{k+1} = x_k + α_k·p_k (шаг вдоль p_k) ГДЕ ИСПОЛЬЗУЕТСЯ: scipy.sparse.linalg.cg(A, b) - для sparse SPD матриц PyTorch L-BFGS оптимайзер - NN обучение с точным CG Gaussian Process posterior - GPyTorch использует CG
**Preconditioned CG в оптимизации нейросетей**: чистый градиентный спуск - это CG с нулевой памятью о прошлых направлениях. L-BFGS и Hessian-free optimization используют приближения сопряжённых направлений. Именно поэтому L-BFGS в sklearn.LogisticRegression быстрее SGD на табличных данных - он учитывает кривизну поверхности потерь.
Что главное в концепте «Conjugate Gradient: когда матрица не помещается в память»?
Проверка усвоения материала концепта.
Однородные системы: ядро матрицы
Однородные системы: ядро матрицы
~Однородная система~{homogeneous system: Ax = 0} - это вопрос «какие векторы A обнуляет?». Множество таких векторов называется ~ядром~{null space / kernel} матрицы A.
Ax = 0 всегда имеет нулевое решение x = 0. ЕСЛИ det(A) ≠ 0 → только x = 0 (ядро нулевое) ЕСЛИ det(A) = 0 → ядро ненулевое (бесконечно векторов) ГЕОМЕТРИЯ: Проекция на ось X: ker = вся ось Y Поворот: ker = только {0} ML-применение: null space матрицы весов в нейросети = направления, которые слой «не видит». Именно из-за ненулевого ядра в underdetermined системах решений бесконечно - нужна регуляризация (L2 / L1 / dropout) чтобы выбрать одно конкретное.
Ax = b в ML-инфраструктуре
Где решение систем работает прямо сейчас
| Компонент | Роль | Детали |
|---|---|---|
| sklearn LinearRegression | lstsq через SVD под капотом | Normal equation XᵀX w = Xᵀy, решается через scipy.linalg.lstsq с rcond threshold |
| Ridge / Lasso Regression | (XᵀX + λI)w = Xᵀy - всегда SPD, Cholesky | Ridge: аналитическое, Lasso: ADMM / coordinate descent (нельзя решить как СЛАУ напрямую) |
| Gaussian Process posterior | μ* = K(X*,X)(K(X,X)+σ²I)⁻¹y - SPD, CG в GPyTorch | GPyTorch использует CG с preconditioning вместо Cholesky - O(n) памяти против O(n²) |
| Калман фильтр | Предсказание состояния: P = A P Aᵀ + Q (SPD, Cholesky) | Беспилотники, трекинг, SLAM - сотни решений Ax=b в секунду в реальном времени |
Что главное в концепте «Однородные системы: ядро матрицы»?
Проверка усвоения материала концепта.
Практика: решение линейной системы
Практика: решение линейной системы
Вопросы для собеседования
Почему np.linalg.solve(A, b) лучше np.linalg.inv(A) @ b? Когда inv всё же нужен?
- solve использует LU напрямую - O(n³/3), не строит обратную матрицу - inv(A) @ b = два шага: O(n³) + O(n²) - лишние умножения и округления - При 100 разных b: solve(A, B) один раз делает LU, потом 100 forward-back substitutions O(n²) - inv нужен только когда A⁻¹ сама по себе нужна как объект - редкость в практике
Датасет: 10000 объектов, 500 признаков. Матрица XᵀX плохо обусловлена. Как решить задачу линейной регрессии надёжно?
- np.linalg.lstsq(X, y) внутри использует SVD - устойчив при любом ранге - Ridge добавляет λI: (XᵀX + λI)w = Xᵀy - матрица становится SPD с κ = (σ_max²+λ)/(σ_min²+λ) - Маленький λ сильно улучшает обусловленность: σ_min²=0.001, λ=0.1 → κ снижается в 100x - sklearn Ridge внутри вызывает scipy.linalg.solve_triangular после Cholesky
Когда conjugate gradient предпочтительнее прямых методов (LU/Cholesky)?
- При n > 10^4 и sparse матрице: LU требует O(n²) памяти, CG - O(n) - Sparse матрицы (графы, PDE): умножение A·v дёшево, CG сходится за √κ итераций - В оптимизации нейросетей: L-BFGS = CG с аппроксимацией Гессиана - Exact solver нужен при κ < 10^6 и нет памяти для хранения разложения
Что главное в концепте «Практика: решение линейной системы»?
Проверка усвоения материала концепта.
Что унести из урока
- **Ax = b** - одно уравнение за тремя видами записи; матричная форма открывает GPU и библиотечные solver-ы
- **np.linalg.solve** быстрее и точнее чем inv(A) @ b - использовать всегда, когда нужно решить систему
- **Переопределённые системы** (m > n) - нет точного решения; lstsq минимизирует ||Ax - b||² через SVD
- **Нормальное уравнение** AᵀA·x = Aᵀb - это вся линейная регрессия в одной формуле
- **Выбор алгоритма**: LU - общий, Cholesky - SPD (в 2x быстрее), QR/SVD - переопределённые, CG - sparse/огромные
- **Ядро матрицы** (null space) - направления которые система «не видит»; именно из-за него underdetermined задачи имеют бесконечно решений
Куда дальше
Системы уравнений - пересечение всего линейной алгебры
- Свойства матриц — Обусловленность и SPD-ность определяют выбор solver-а
- Обратная матрица — A⁻¹ существует ↔ det(A) ≠ 0 ↔ Ax=b имеет единственное решение
- SVD и псевдообратная — lstsq = A⁺b через SVD; псевдообратная обобщает решение на ранг-дефицитный случай