Выпуклая оптимизация
Дифференцируемое программирование
Google в 2017 году обучил оптимизатор-нейросеть, которая обогнала вручную настроенный Adam на 80+ задачах. Airbus оптимизирует геометрию крыла A350 через градиент по уравнениям аэродинамики, экономя 4% топлива. **Дифференцируемое программирование** превращает любую программу - симулятор, рендер, физику - в функцию, по которой можно делать обратный проход и оптимизировать.
- **MAML и мета-обучение**: Apple и Google используют похожие подходы для персонализации моделей - быстрая адаптация к пользователю за несколько шагов, без переобучения с нуля
- **Дифференцируемые симуляторы**: Tesla FSD обучается частично через дифференцируемые модели дорожной среды - градиенты из симуляции заменяют дорогостоящие реальные испытания
- **Neural ODE**: оборачивает ODE-solver в дифференцируемый слой - используется в моделировании молекулярной динамики и временных рядов
Дифференцируемое программирование: программы = вычислительные графы
**Дифференцируемое программирование (DP)** - это парадигма, в которой вся программа рассматривается как вычислительный граф с дифференцируемыми операциями. Если каждая операция имеет определённую производную, то через chain rule (autograd) мы можем взять градиент потерь по любому параметру программы - даже по структуре алгоритма, гиперпараметрам или шагам оптимизатора.
В классическом ML мы дифференцируем по весам нейросети. В DP мы идём дальше: дифференцируем по **гиперпараметрам** (скорость обучения, архитектура), по **алгоритмам** (шаги физической симуляции), по **программам** (итерации внутреннего оптимизатора). Это открывает возможность обучать не только «что», но и «как».
Любая операция, для которой определено правило обратного распространения (VJP - vector-Jacobian product), встраивается в дифференцируемый граф. JAX, PyTorch и TensorFlow поддерживают сотни операций «из коробки». Нестандартные решатели (LP, QP, оптимизация на многообразиях) оборачиваются через `custom_vjp` / `register_hook` с аналитически выведенными градиентами.
Что принципиально нового даёт дифференцируемое программирование по сравнению с обычным autograd нейросетей?
Неявное дифференцирование и bi-level оптимизация
Многие задачи имеют вид: **найти θ, минимизирующее внешнюю задачу, где внутренняя задача - оптимизация по x(θ)**. Это **bi-level оптимизация** (двухуровневая). Для вычисления градиента нужно дифференцировать через argmin - это непросто, потому что argmin не имеет явного выражения.
Неявное дифференцирование использует **теорему о неявной функции**: если условие первого порядка выполнено и гессиан невырожден, то x*(θ) - дифференцируемая функция θ. Ключевое вычислительное преимущество: не нужно хранить граф всех шагов внутреннего оптимизатора - достаточно решить одну линейную систему H⁻¹v в конце.
**higher** (PyTorch): разворачивает оптимизатор и сохраняет граф - для точного unrolling. **jaxopt**: реализует неявное дифференцирование через `implicit_diff=True` для iterative solvers. **cvxpylayers**: оборачивает CVXPY задачи (LP, QP, SDP) в дифференцируемый слой через KKT-условия - те же уравнения первого порядка, что и неявное дифференцирование.
Главное преимущество неявного дифференцирования перед развёртыванием (unrolling) оптимизатора:
Развёртывание оптимизатора vs неявное дифференцирование
Два основных подхода к дифференцированию через внутреннюю оптимизацию имеют разный trade-off по памяти, точности и сложности реализации. Выбор зависит от числа итераций, размера модели и требований к точности градиента.
Компромиссный подход: развернуть только последние K итераций вместо всех T. Это ограничивает память O(K) и уменьшает vanishing gradients, но вводит bias в градиент. Используется в MAML с небольшим числом inner steps (обычно 5-10) и в обучении score функций в диффузионных моделях.
При bi-level оптимизации, где внутренняя задача - это LP (линейное программирование) с тысячами итераций simplex-метода. Какой подход единственно применим?
Приложения: MAML, физика, NAS
Дифференцируемое программирование стало основой ряда прорывных направлений ML последних лет - от meta-learning до дифференцируемой физики и автоматического поиска архитектур.
Зачем в MAML используется `create_graph=True` при вычислении inner loop градиентов?
Ключевые идеи
- **Дифференцируемое программирование** - парадигма, где весь pipeline (симулятор, оптимизатор, архитектура) рассматривается как дифференцируемый вычислительный граф
- **Bi-level оптимизация**: внешняя задача оптимизирует по решению внутренней; требует дифференцирования через argmin
- **Неявное дифференцирование**: градиент через оптимальность условий (KKT/стационарность) - O(1) памяти, точен в оптимуме
- **Unrolling**: развёртывание T шагов оптимизатора в граф - просто, но O(T) памяти и проблема vanishing/exploding gradients
Связанные темы
Дифференцируемое программирование связывает классическую оптимизацию с современным ML:
- Оптимизация на многообразиях — Riemannian Autodiff - частный случай DP, где граф включает операции на многообразиях
- Федеративная оптимизация — Персонализованный FL использует MAML-подобные bi-level схемы для адаптации к клиентам
- Оптимизация в задачах RL — Policy gradient - специальный случай DP: дифференцируем через стохастическое выполнение программы-политики
Вопросы для размышления
- Какие задачи в данной области могут быть сформулированы как bi-level оптимизация?
- В чём принципиальная разница между MAML (unrolling) и методами meta-learning через неявное дифференцирование?
- Как дифференцируемое программирование меняет границу между «моделью» и «алгоритмом» в ML?