Модуль V·Статья I·~4 мин чтения

Рандомизированная линейная алгебра

Алгоритмы для Big Data

Превратить статью в подкаст

Выберите голоса, формат и длину — AI запишет аудио

Рандомизированная линейная алгебра

Матрица данных 10 миллионов пользователей × 100 тысяч продуктов. Полное SVD — невозможно: O(mn·min(m,n)) ≈ 10¹⁷ операций. Рандомизированные алгоритмы снижают сложность до O(mnk) при теоретических гарантиях близости к точному решению. Это не «приближение из-за лени» — это математически строгая теория.

Рандомизированное SVD

Задача: вычислить лучшее ранг-k приближение матрицы A ∈ ℝ^{m×n}: min_{rank(B)≤k} ||A − B||_F.

Точный алгоритм: SVD A = UΣVᵀ, Aₖ = U_{1:k}Σ_{1:k}Vᵀ_{1:k}. Сложность O(mn²).

Randomized SVD (Halko, Martinsson, Tropp, 2011):

Шаг 1. Случайная матрица Ω ∈ ℝ^{n×k}: Ωᵢⱼ ~ N(0,1/k). Sketch Y = AΩ ∈ ℝ^{m×k}. Смысл: Y аппроксимирует «пространство столбцов» A — проецируем A на k случайных направлений.

Шаг 2. QR-разложение Y = QR, Q ∈ ℝ^{m×k} — ортонормированный базис пространства столбцов Y.

Шаг 3. B = QᵀA ∈ ℝ^{k×n}. Матрица B намного меньше: k × n.

Шаг 4. SVD(B) = ŪΣVᵀ, U = QŪ.

Итог: U ∈ ℝ^{m×k}, Σ ∈ ℝ^{k×k}, V ∈ ℝ^{n×k} — аппроксимация SVD. Сложность: O(mnk) vs O(mn²).

Теоретические гарантии: с вероятностью ≥ 1−δ:

||A − QQᵀA||F ≤ (1 + O(√(k/δ))) σ{k+1}(A)

Здесь σ_{k+1}(A) — (k+1)-е сингулярное значение, нижняя граница для любого ранг-k приближения. Практически: k_actual = k+p (p=10 «oversampling» итераций) даёт почти точное решение.

Degree-q power iteration: Вместо Y = AΩ используем Y = (AAᵀ)^q AΩ. При q=1–2: ошибка ~σ_{k+1}^{2q+1} — экспоненциально улучшается при q. Стоимость: q дополнительных умножений на A.

Sklearn random SVD по умолчанию: n_iter=4 — 4 power iterations.

Приближённый поиск ближайших соседей (ANNS)

Задача: для запроса q найти x* = argmin_{x∈X} d(q, x). Exact kNN: O(nd) за запрос. При n=10^8, d=768 (BERT embeddings) — невозможно.

LSH (Locality Sensitive Hashing): семейство хэш-функций h такое, что P(h(x)=h(y)) велика при d(x,y) мала и мала при d(x,y) велика.

Для L2 (Дог, 2004): h_{a,b}(x) = ⌊(aᵀx + b)/w⌋, где a ~ N(0,I), b ~ Uniform[0,w]. Близкие точки попадают в один «ведёрко» с высокой вероятностью.

Амплификация: L независимых таблиц × k хэш-функций в каждой. Запрос: смотрим в L таблицах, берём union of buckets. Время: O(d·n^ρ), ρ = 1/(2L²·w² + 1) < 1.

HNSW (Malkov & Yashunin, 2018): иерархический граф навигации. Каждый уровень = граф ближайших соседей с разной плотностью. Поиск: начинаем с верхнего (разреженного) уровня → быстро приближаемся к зоне запроса → спускаемся вниз. Время: O(log n). SOTA для большинства задач ANNS.

FAISS (Facebook AI Similarity Search): библиотека с GPU-ускорением. Product Quantization: сжимаем векторы в коды (4–8 байт вместо 768×4=3072 байт). IVF (Inverted File): кластеризуем пространство, ищем только в ближайших кластерах. Поиск 10^8 векторов за <1 мс на GPU.

Потоковые алгоритмы: большие данные малой памятью

Задача: входит поток из N элементов x₁, x₂,..., xₙ. Память O(N) недопустима. Нужно вычислить статистику потока за один проход.

Count-Min Sketch (Cormode & Muthukrishnan, 2004): оцениваем частоту f(x) при малой памяти.

Структура: матрица счётчиков C[d][w] (d рядов × w столбцов). d независимых хэш-функций h₁,...,h_d: U → {1,...,w}.

Обновление для элемента x: для i=1,...,d: C[i][hᵢ(x)] += 1.

Оценка f̂(x) = min_i C[i][hᵢ(x)] (минимум по рядам).

Гарантии: P(|f̂(x) − f(x)| ≤ εN) ≥ 1−δ при w = ⌈e/ε⌉, d = ⌈ln(1/δ)⌉. Память: O(1/(ε) · log(1/δ)) — не зависит от N!

HyperLogLog (Flajolet et al., 2007): оцениваем число уникальных элементов (cardinality). Идея: хэшируем x → равномерный [0,1]. Максимальный показатель последовательных нулей в начале двоичного представления → оценка log₂(n). Средний по m=2^b подрегистрам. Ошибка: ~1.04/√m. Память: O(log log N) бит. В Redis реализован, используется для Count Distinct в аналитике.

Morris Counter: считать n до ~2^{64} в O(log log n) бит. Храним k = ⌊log₂(n)⌋ + случайная ошибка. Инкремент: k ← k+1 с вероятностью 2^{-k}. Оценка: 2^k. Дисперсия = O(n²).

Численный пример

Рандомизированное SVD Netflix Prize (480K пользователей × 17K фильмов):

  • Истинный ранг матрицы ≈ 100
  • Точное SVD: 480K × 17K × 100 ≈ 816 × 10⁹ операций (~4 часа)
  • Random SVD (k=100, q=2): ~240 × 10⁹ → ~1 час, ошибка <1%

HNSW поиск по 10M BERT-эмбеддингов (768d): индексация 10 минут, поиск top-10 за 0.3 мс (vs 4.5 секунды для exact brute-force).

Задание: (1) Реализуйте рандомизированное SVD без sklearn для матрицы рейтингов MovieLens (100K). Сравните точность (Frobenius error) с truncated SVD для k=10, 50, 100 при q=0, 1, 2 power iterations. (2) Реализуйте Count-Min Sketch и HyperLogLog в Python. Протестируйте на потоке из 10M слов английского текста. Сравните оцениваемое число уникальных слов с истинным.

§ Акт · что дальше