С четырьмя параметрами я могу задать слона, а с пятью я могу заставить его шевелить хоботом.
– John Von Neumann
Идея «дифференцируемого программирования» очень популярна в мире машинного обучения. Для многих не ясно, отражает ли этот термин реальный сдвиг в том, как исследователи понимают машинное обучение, или это просто (еще один) ребрендинг «глубокого обучения». В этом посте разъясняется, что нового дает дифференцируемое программирование (или ∂P) в таблице машинного обучения.
Самое главное, дифференцируемое программирование — это сдвиг, противоположный направлению глубокого обучения; от все более сильно параметризованных моделей к более простым, которые в большей степени используют структуру проблемы.
Далее мы пролистаем полотно неинтересного текста, захотим узнать, что такое автодифференцирование и даже популяем из катапульты!
Brute Force with Benefits
Дифференцируемость — основная идея, которая делает глубокое обучение таким успешным. Там, где поиск методом грубой силы даже по нескольким сотням параметров модели был бы слишком дорогим, градиенты позволяют провести псевдослучайный обход интересных частей пространства параметров и найти хороший набор. Выполняя такой, казалось бы, наивный алгоритм, мы получаем хорошую общность, но далеко не очевидно, что нам нужно дифференцировать, скажем, работая с последовательностями в языковом переводе, но всё оказывается простым, прояви мы немного изобретательности.
А как насчет биологических нейронов и ? В этой формуле нет ничего особенного; это простой и гибкий пример высокопараметрической нелинейной функции. На самом деле, это, вероятно, худшая такая функция в большинстве случаев. Один слой нейронной сети, в принципе, может классифицировать изображения кошек, но только используя относительно неинтересную уловку, действующую как справочная таблица. Работает безотказно! — но мелкий шрифт предупреждает, что вам может понадобиться больше параметров, чем атомов во вселенной. Чтобы на самом деле заставить это дело работать, вам нужно закодировать проблемную структуру в модели — именно здесь она начинает больше походить на традиционное программирование.
Например, ConvNets имеют огромное преимущество по сравнению с персептроном, потому что они работают с ядрами изображений, которые, как известно, используют трансляционную инвариантность. Грань — она и есть грань, независимо от того, отображается она в верхнем левом углу изображения или в центре, но там, где персептрон должен был бы изучить этот случай в каждом конкретном случае, ядро может сразу реагировать на любую часть изображения. Трудно анализировать сверточные сети в статистическом выражении, но гораздо проще рассматривать их как автоматически настроенную версию того, что эксперты по обработке изображений писали от руки. Ядро образа является первой и самой простой дифференцируемой программой.
Encoding Structure, Redux
Наборы инструментов ML все больше поддерживают алгоритмическое дифференцирование (AD), что позволяет нам дифференцировать модели с помощью циклов, ветвлений и рекурсии — или любой программы, построенной на наборе дифференцируемых математических примитивов. Это привело к более сложной архитектуре: модели НЛП все больше похожи на классические грамматические парсеры со stack-augmented моделями, и можно даже дифференцировать аналог машины Тьюринга или интерпретатор языка программирования.
Последний шаг, предпринятый дифференцируемым программированием, состоит в том, чтобы больше не рассматривать умножение матриц, свертки и RNN как фундаментальные строительные блоки глубокого обучения, а лишь как частные случаи. Мы можем применить методы глубокого обучения к любой параметризованной дифференцируемой функции . Функции, столь же сложные, как физические симуляторы или трассировщики лучей, также могут быть дифференцированы и оптимизированы. Даже квантовые вычисления могут вписаться в эту структуру.
Ученые долго использовали механистические модели, которые находятся между явным программированием и машинным обучением. Дифференциальные уравнения со свободными параметрами, используемые в физике, эпидемиологии или фармакодинамике — эквивалентны во всём, кроме терминологии, нейронным сетям. Они просто более ограничены в том, какие функции они могут представлять, что облегчает получение хорошего результата.
Действительно мощный прогресс заключается в следующем: всепроникающая дифференцируемость означает, что все эти методы соединяются вместе, как кирпичи lego. Вместо того, чтобы всегда писать новые программы для ML, мы можем включить существующие программы, приспособив физические движки в моделях робототехники, основанные на глубоком обучении. Там, где современные алгоритмы обучения с подкреплением должны строить детальную модель внешнего мира основываясь только на том, за что дадут вознаграждение (звучит как грубая сила), мы можем вместо этого просто опустить детальное, точное знание физических систем, прежде чем обучение даже начинается.
Даже самые зрелые области глубокого обучения не остаются в стороне; после ядра свертки естественным следующим шагом для моделей изображений является дифференцируемый трассировщик лучей. 3D рендеринг содержит много структурных знаний о том, как сцены отображаются в пикселях, и это тоже может быть в нашем плавильном котле. Скажем, модель принимает решения в моделируемой среде, отображаемой в виде пикселей, которые модель использует в качестве входных данных. В принципе, теперь мы можем сделать весь цикл дифференцируемым, что позволит нам непосредственно увидеть влияние среды на решения модели и наоборот. Это может значительно увеличить мощность реалистичной моделируемой среды для тренировочных моделей, таких как автомобили с автоматическим вождением.
Как и в науке, гибридные модели могут быть более эффективными и разрешать некоторые из компромиссов между глубоким обучением и явным программированием. Например, планировщик траектории полета беспилотника может иметь компонент нейронной сети, который может вносить лишь незначительно корректирующие изменения в надежную явную программу, делая ее общее поведение анализируемым, в то же время адаптируясь к эмпирическим данным. Это также хорошо для интерпретируемости: параметры механистических моделей и симуляций обычно имеют четкие физические интерпретации, поэтому, если модель оценивает параметры внутри, она делает четкое утверждение о том, что, по ее мнению, происходит снаружи.
Если это все так замечательно, почему же все всё не побросали и не кинулись учиться дифференцирвать? К сожалению, ограничения существующих фреймворков затрудняют построение моделей такой сложности, и невозможно повторно использовать богатство знаний, встроенных в существующий научный код. Необходимость заново реализовать физические движки с нуля на весьма ограниченном языке моделирования превращает сценарий из десяти строк в многолетний исследовательский проект. Но достижения в области языка и технологии компиляции, особенно автоматическое дифференцирование, приближают нас к святому Граалю: «просто дифференцируйте мой игровой движок, пожалуйста».
Итак, что такое дифференцируемое программирование?
Дифференцируемое программирование применяет методы глубокого обучения к сложным существующим программам, используя в своих интересах огромное количество знаний, встроенных в них: глубокое обучение, статистика, программирование и науки — все, что моделирует мир вокруг нас, и что можно сунуть в ускоритель частиц. Это улучшит текущие модели и позволит применять ML в тех областях, где его текущие ограничения — либо интерпретируемость, либо требования к вычислениям и данным — делают его неосуществимым в одиночку.
Дифференцируемые проблемы управления
Далее покажем, что ∂P может привести к некоторым простым, но классическим задачам управления, в которых мы обычно используем «черный ящик» для обучения с подкреплением (RL). ∂P-модели не только учат гораздо более эффективные стратегии управления, но и обучают на несколько порядков быстрее. Код доступен для изучения — в большинстве случаев он обучается за несколько секунд на любом ноутбуке.
Следуйте за Градиентом
Дифференцирование — это практически каждый шаг глубокого обучения; для данной функции мы используем градиент , чтобы выяснить, как изменение x
повлияет на y
. Несмотря на математическую сущность, градиенты на самом деле являются очень общей и интуитивной концепцией. Забудьте формулы, на которые вам приходилось смотреть в школе; давайте делать что-то более веселое, например, пулять вещи.
Когда мы бросаем вещи с помощью требушета, наш x
(входные данные) представляет собой настройку (скажем, размер противовеса или угол выброса), а y
— это расстояние, которое снаряд проходит до приземления. Если вы пытаетесь прицелиться, градиент говорит вам кое-что очень полезное — увеличить или уменьшить определенный параметр. Чтобы максимизировать расстояние, просто следуйте градиенту.
Хорошо, но как нам получить правильный параметр? А вот с помощью хитрой штуки, называемой алгоритмическим дифференцированием, которое позволяет дифференцировать не только простые формулы, которые вы вымучили в школе, но и программы любой сложности — например, наш симулятор Требушета. В результате мы можем взять простой симулятор, написанный на Julia и пакет диффуров DiffEq без глубокого изучения, и получить градиенты для него в одном вызове функции.
# what you did in school
gradient(x -> 3x^2 + 2x + 1, 5) # (32,)
# something a little more advanced
gradient((wind, angle, weight) -> Trebuchet.shoot(wind, angle, weight),
-2, 45, 200) # (4.02, -0.99, 0.051)
Throwing Stuff
Нам нужно нацелить требушет на цель, используя градиенты для точной настройки угла выброса; Подобные вещи распространены под названием оценки параметров, и мы уже рассматривали подобные примеры. Мы можем сделать задачу интересней, перейдя к мета-методу: вместо того, чтобы нацеливать trebuchet на одну цель, мы оптимизируем нейронную сеть, которая может нацеливать его на любую цель. Вот как это работает: нейронная сеть принимает два входа, целевое расстояние в метрах и текущую скорость ветра. В сети выкладываются настройки требушета (масса противовеса и угол расцепления), которые подаются в симулятор, который рассчитывает пройденное расстояние. Затем мы сравниваем с нашей целью и продвигаемся по всей цепочке, чтобы скорректировать вес сети. Наш «набор данных» — это случайно выбранный набор целей и скоростей ветра.
Хорошим свойством этой простой модели является то, что обучение проходит быстро, потому что мы выразили именно то, что мы хотим от модели, полностью дифференцируемым способом. Изначально это выглядит так:
Примерно после пяти минут обучения (на одном ядре процессора моего ноутбука) это выглядит так:
Если вы хотите повлиять на траекторию, увеличьте скорость ветра:
Отклонилось на 16 см, или около 0,3%. А как насчет того, чтобы нацелиться требушетом напрямую? Это легко сделать градиентным спуском, учитывая, что у нас есть градиенты. Однако это медленный итеративный процесс, который каждый раз занимает около 100 мс. Напротив, работа нейронной сети занимает 5 мкс (в двадцать тысяч раз быстрее) с небольшой потерей точности. Этот трюк «приблизительная инверсия функций через градиенты» является очень общим, и его можно использовать не только с динамическими системами, но и с алгоритмом быстрой передачи стиля.
Речь идет о самой простой из возможных проблем управления, которые мы используем в основном в иллюстративных целях. Но мы можем применить те же методы, более продвинутыми способами, и к классическим задачам RL.
Cart, meet Pole
Более узнаваемой проблемой управления является CartPole, «Hello world» для обучения с подкреплением. Задача состоит в том, чтобы научиться балансировать вертикальный столб, подталкивая его основание влево или вправо. Наша установка в целом похожа на случай с Trebuchet: реализация Julia позволяет напрямую рассматривать вознаграждение, полученное средой, как потери. ∂P даёт нам возможность беспрепятственно переключаться с простой модели на RL-модель.
Проницательный читатель может заметить загвоздку. Пространство действия для картпола — смещение влево или вправо — является дискретным и, следовательно, не дифференцируемым. Мы решаем эту проблему, вводя дифференцируемую дискретизацию, определяемую следующим образом:
Другими словами, мы заставляем градиент вести себя так, как если бы была тождественной функцией. Учитывая, насколько математическая идея дифференцируемости уже используется в ML, возможно, не удивительно, что мы можем просто здесь схитрить; для обучения все, что нам нужно, это сигнал для информирования нашего псевдослучайного обхода пространства параметров, а остальное — детали. Результаты говорят сами за себя. В тех случаях, когда методам RL необходимо обучаться сотнями эпизодов, прежде чем решать проблему, модели ∂P нужно всего лишь около 5 эпизодов, чтобы окончательно победить.
The Pendulum & Backprop through Time
Важной целью для RL (обучение с подкреплением) является обработка отсроченного вознаграждения, когда действие не помогает нам улучшать результаты несколько шагов подряд. Когда среда дифференцируема, ∂P позволяет обучать агента обратным распространением во времени, как в рекурентной сети! В этом случае состояние окружающей среды становится «скрытым состоянием», которое изменяется между временными шагами.
Чтобы продемонстрировать эту технику, рассмотрим модель маятника, где задача состоит в том, чтобы качать маятник до тех пор, пока он не встанет вертикально, и удерживать его в неустойчивом равновесии. Это сложно для моделей RL; после примерно 20 эпизодов обучения проблема решается, но зачастую путь к решению является явно неоптимальным. В отличие от этого, BPTT может побить рейтинг лидеров RL в одном эпизоде обучения. Поучительно наблюдать, как разворачивается этот эпизод; в начале записи стратегия является случайной, и модель со временем улучшается. Темпы обучения почти тревожны.
Модель хорошо подходит для обработки любого начального угла и имеет нечто, близкое к оптимальной стратегии. При перезапуске модель выглядит примерно так.
Это только начало; мы добьемся реальных успехов, применяя DP к средам, с которыми RL вообще слишком сложно работать, где уже существуют богатые симуляции и модели (как в большинстве инженерных и естественных наук), и где интерпретируемость является важным фактором (как в медицине).
The Map Is Not The Territory
Ограничением этих игрушечных моделей является то, что они приравнивают моделируемую среду обучения к среде тестирования; конечно, реальный мир не дифференцируем. В более реалистичной модели симуляция дает нам грубую схему поведения, которая уточняется данными. Эти данные информируют, скажем, о моделируемом воздействии ветра, что, в свою очередь, улучшает качество градиентов, которые симулятор передает контроллеру. Модели могут даже составлять часть прямого прохода контроллера, позволяя ему уточнить свои прогнозы без необходимости изучать динамику системы с нуля. Изучение этих новых архитектур сделает захватывающей будущую работу.
Coda
Основная идея заключается в том, что дифференцируемое программирование, в котором мы просто пишем произвольную числовую программу и оптимизируем ее с помощью градиентов, является мощным способом создания более совершенных моделей и архитектур, похожих на глубокое обучение, особенно когда у нас под рукой большая библиотека дифференцируемых программ. Описанные модели — это всего лишь предварительные просмотры, но мы надеемся, что они дадут представление о том, как эти идеи могут быть реализованы более реалистичным способом.
Точно так же, как функциональное программирование предполагает рассуждение и выражение алгоритмов с использованием функциональных шаблонов, дифференцируемое программирование включает в себя выражение алгоритмов с использованием дифференцируемых шаблонов. Сообщество глубокого обучения уже разработало множество таких шаблонов проектирования, например, для обработки проблем управления или последовательной и древовидной структуры данных. По мере взросления области будет изобретено гораздо больше, и в результате на фоне этих программ, вероятно, даже самые передовые из существующих архитектур глубокого обучения будут выглядеть грубыми и отсталыми.
Ссылки
- Источники
- Википедия
- Автодифференцирование; статья на хабре
- Автодифференцирование; готовый пакет
- Автодифференцирование; своими руками
Автор: Yermack