Определитель, обратная матрица, ранг и системы уравнений
Определитель: во сколько раз меняется площадь
Линейное преобразование превращает единичный квадрат (построенный на e_1 и e_2) в параллелограмм. Определитель \det A — площадь этого параллелограмма со знаком. А раз сетка равномерная, любая фигура меняет площадь в те же |\det A| раз.
Для матрицы 2×2:
- |\det A| > 1 — площади растут, < 1 — уменьшаются;
- \det A < 0 — ориентация перевернулась (как в зеркале);
- \det A = 0 — плоскость схлопнулась в прямую или точку.
Вернитесь к виджету и посмотрите на закрашенный квадрат — его площадь равна |\det|:
import numpy as np
import vis
for M in ([[2, 0], [0, 1.5]], [[1, 1], [0, 1]], [[0, 1], [1, 0]], [[1, 2], [0.5, 1]]):
M = np.array(M)
print(M.tolist(), "det =", round(float(np.linalg.det(M)), 3))
vis.transform(M, title=f"det = {np.linalg.det(M):.2f}")
Для матриц 3×3 определитель — изменение объёма, для n×n — n-мерного объёма. Считать его руками больших матриц не нужно: np.linalg.det.
Важное свойство: \det(AB) = \det A \cdot \det B — если одно преобразование удваивает площади, а другое утраивает, то их композиция увеличит в 6 раз.
Обратная матрица
Обратная матрица A^{-1} отменяет преобразование: A^{-1}A = AA^{-1} = I. Если A повернула плоскость на 30°, то A^{-1} повернёт на −30°.
Обратная существует тогда и только тогда, когда \det A \ne 0. Если пространство схлопнулось в прямую, разные точки попали в одну — по результату уже не восстановить, откуда они пришли. Такие матрицы называют вырожденными (singular).
import numpy as np
A = np.array([[4.0, 7], [2, 6]])
A_inv = np.linalg.inv(A)
print(A_inv)
print(A_inv @ A) # единичная матрица (с точностью до округления)
x = np.array([1.0, 2])
print("A·x =", A @ x, " → A⁻¹·(A·x) =", A_inv @ (A @ x))
import numpy as np
S = np.array([[2.0, 4], [1, 2]]) # первая строка = 2 × вторая
print("det =", np.linalg.det(S))
try:
print(np.linalg.inv(S))
except np.linalg.LinAlgError as e:
print("ошибка:", e)
На обычном компьютере NumPy бросит LinAlgError: Singular matrix. Браузерная сборка NumPy такую проверку не делает и молча вернёт nan («не число»). Поэтому перед обращением матрицы полезно самим проверить определитель или ранг.
Для 2×2 есть явная формула: \begin{pmatrix} a & b \\ c & d \end{pmatrix}^{-1} = \frac{1}{ad - bc}\begin{pmatrix} d & -b \\ -c & a \end{pmatrix} — деление на определитель сразу показывает, почему при \det = 0 ничего не выйдет.
Ранг
Ранг матрицы — размерность пространства, которое получается после преобразования (размерность «образа»). Это же — число линейно независимых столбцов (или строк).
| Матрица 3×3 сжимает пространство | Ранг | det |
|---|---|---|
| никак (объём сохраняет «толщину») | 3 | ≠ 0 |
| на плоскость | 2 | 0 |
| на прямую | 1 | 0 |
| в точку (нулевая матрица) | 0 | 0 |
import numpy as np
A = np.array([[1, 2, 3], [4, 5, 6], [5, 7, 9]]) # третья строка = первая + вторая
print("ранг:", np.linalg.matrix_rank(A), " det ≈", round(float(np.linalg.det(A)), 10))
B = np.outer([1, 2, 3], [1, 0, -1]) # все строки пропорциональны
print(B)
print("ранг:", np.linalg.matrix_rank(B))
Линейно зависимые признаки в данных (например, цена в рублях и та же цена в долларах) делают матрицу X^TX вырожденной — так в ML появляется проблема мультиколлинеарности.
Системы линейных уравнений
Система
записывается как A\mathbf{x} = \mathbf{b}, где A = \begin{pmatrix} 2 & 1 \\ 1 & -3 \end{pmatrix}, \mathbf{b} = \begin{pmatrix} 5 \\ -1 \end{pmatrix}. Геометрически: какой вектор A переводит в \mathbf{b}? Если A обратима, ответ единственный: \mathbf{x} = A^{-1}\mathbf{b}.
import numpy as np
A = np.array([[2.0, 1], [1, -3]])
b = np.array([5.0, -1])
x = np.linalg.solve(A, b) # решает систему, не вычисляя обратную явно
print("решение:", x)
print("проверка A·x =", A @ x)
solve, а не inv
np.linalg.solve(A, b) быстрее и точнее, чем np.linalg.inv(A) @ b. Явно обращать матрицу почти никогда не нужно.
Метод наименьших квадратов: первая обученная модель
Вернёмся к квартирам. Точек много (10), а параметров два (w и b). Уравнений больше, чем неизвестных: прямая не пройдёт через все точки, точного решения нет. Найдём наилучшее — с минимальной суммой квадратов ошибок.
Запишем модель матрично. Матрица X имеет два столбца: площади и единицы (единица умножается на b). Вектор параметров \theta = (w, b). Тогда предсказания — X\theta, и мы хотим минимизировать \|X\theta - y\|^2.
Ответ даёт нормальное уравнение (его вывод — через производные, которые будут в следующем модуле):
import numpy as np
import vis
areas = np.array([32, 38, 45, 50, 56, 63, 70, 78, 85, 95], dtype=float)
prices = np.array([6.8, 7.9, 9.1, 10.4, 11.0, 12.9, 13.8, 15.6, 16.5, 19.2])
X = np.c_[areas, np.ones(len(areas))] # столбцы: площадь, 1
theta = np.linalg.solve(X.T @ X, X.T @ prices)
w, b = theta
mse = ((X @ theta - prices) ** 2).mean()
print(f"w = {w:.4f}, b = {b:.4f}, MSE = {mse:.4f}")
print(f"квартира 60 м² ≈ {w * 60 + b:.2f} млн ₽")
vis.line(areas, {"данные": prices, "наилучшая прямая": X @ theta},
title="Метод наименьших квадратов", xlabel="площадь, м²", ylabel="цена, млн ₽")
Это настоящая линейная регрессия — модель, обученная на данных. Почему же нейросети учат не так, а градиентным спуском?
- Формула работает только для линейной модели с квадратичной ошибкой. Для нейросетей замкнутой формулы нет.
- Обращение матрицы d \times d стоит порядка d^3 операций: при миллионе признаков это невозможно.
Градиентный спуск лишён обоих ограничений — к нему мы и идём.
Итог
- \det A — во сколько раз меняется площадь (объём); знак — ориентация; \det(AB) = \det A \det B.
- \det A = 0 — пространство схлопнулось, матрица вырождена, обратной нет.
- A^{-1} отменяет преобразование; системы Ax = b решают
np.linalg.solve. - Ранг — размерность образа = число независимых столбцов.
- Наименьшие квадраты: \theta = (X^TX)^{-1}X^Ty — точное решение линейной регрессии; для нейросетей нужен градиентный спуск.
Практика решено 0 из 6
Чему равен определитель матрицы [[3, 1], [4, 2]]?
Есть ли у матрицы обратная?
| есть | нет | |
|---|---|---|
[[1, 2], [2, 4]] |
||
[[0, -1], [1, 0]] (поворот) |
||
[[1, 0], [0, 0]] (проекция) |
||
[[2, 0], [0, 0.5]] |
||
[[1, 2, 3], [4, 5, 6], [5, 7, 9]] |
Чему равен ранг матрицы 3×3, которая сжимает всё пространство на одну прямую?
Что вернёт np.linalg.det(A @ B), если det(A) = 2, det(B) = -3?
Напишите на чистом Python (без NumPy):
det2(M)— определитель матрицы 2×2 (список списков);inv2(M)— обратную матрицу 2×2 по формуле
Если определитель равен 0, inv2 должна бросить ValueError.
Какие тесты проверяют код (3)
assert det2([[3, 1], [4, 2]]) == 2 and det2([[1, 2], [2, 4]]) == 0
Mi = inv2([[4, 7], [2, 6]])
expected = [[0.6, -0.7], [-0.2, 0.4]]
assert all(abs(Mi[i][j] - expected[i][j]) < 1e-12 for i in range(2) for j in range(2)), f"Получено {Mi}"
try:
inv2([[1, 2], [2, 4]])
assert False, "Для вырожденной матрицы нужен ValueError"
except ValueError:
pass
def det2(M):
(a, b), (c, d) = M
return a * d - b * c
def inv2(M):
(a, b), (c, d) = M
det = det2(M)
if det == 0:
raise ValueError("матрица вырождена")
return [[d / det, -b / det], [-c / det, a / det]]
Метод наименьших квадратов. Даны площади x и цены y (векторы длины N).
Напишите fit_line(x, y), которая возвращает (w, b) — параметры прямой y ≈ w·x + b
с минимальной суммой квадратов ошибок. Используйте нормальное уравнение
\theta = (X^T X)^{-1} X^T y, где у матрицы X два столбца: x и столбец единиц
(решайте систему через np.linalg.solve). Верните обычные float.
Какие тесты проверяют код (2)
x = np.array([0.0, 1, 2, 3])
w, b = fit_line(x, 2 * x + 1)
assert abs(w - 2) < 1e-9 and abs(b - 1) < 1e-9, f"Для точек на прямой y = 2x + 1 получено w={w}, b={b}"
rng = np.random.default_rng(0)
x = rng.uniform(20, 100, 50)
y = 0.2 * x + 0.5 + rng.normal(0, 0.5, 50)
w, b = fit_line(x, y)
w2, b2 = np.polyfit(x, y, 1)
assert abs(w - w2) < 1e-8 and abs(b - b2) < 1e-6
assert isinstance(w, float) and isinstance(b, float), "Верните обычные float"
import numpy as np
def fit_line(x, y):
X = np.c_[x, np.ones(len(x))]
w, b = np.linalg.solve(X.T @ X, X.T @ y)
return float(w), float(b)
Войдите, чтобы отмечать пройденные уроки.