Полезные алгоритмы, не оптимизированные Jax, PyTorch или TensorFlow

Полезныеалгоритмынеоптимизированныеjaxpytorchилиtensorflow

В некоторых предыдущих сообщениях блога мы подробно описывали, как можно обобщить автоматическое дифференцирование для автоматического повышения стабильности и всевозможных других тонкостей, включив преобразования графов в генерацию кода. Однако одна из вещей, на которую мы не особо вдавались, – это ограничение этих типов алгоритмов. Это ограничение – то, что мы назвали «квазистатическим», то есть свойство, при котором алгоритм может быть интерпретирован как некоторый статический алгоритм. Оказывается, по очень фундаментальным причинам это то же ограничение, которое некоторые основные фреймворки машинного обучения накладывают на код, который они могут полностью оптимизировать, например Jax или Tensorflow. Это привело нас к вопросу: существуют ли алгоритмы, которые нельзя оптимизировать с учетом этого мышления, и почему? Ответ теперь опубликован на ICML 2021, так Давайте углубимся в эту концепцию более высокого уровня.

Пространство квазистатических алгоритмов

Прежде всего, давайте сформулируем конкретное представление о том, что такое квазистатический алгоритм. Это пространство алгоритмов, которое в некотором роде можно выразить как статический алгоритм. Думайте о «статическом алгоритме» как об алгоритме, который имеет простое математическое описание, не требующее полного компьютерного описания, т.е. без циклов, перезаписи в память и т. Д. В качестве примера давайте рассмотрим пример из документация Jax . Следующее – это то, над чем работает Jax JIT:

  @  jit  def  f  ( x ) :  для  i  в диапазоне  (  3  ): Икс  знак равно  2  x  возврат  x  печать   ( f  (  3  )  )  

Обратите внимание, что это представлен чем-то с потоком управления, т.е. это код, представленный циклом, но , но цикл не является необходимым Мы также можем понимать этот метод как 2 2 2 x или 8 x. Продемонстрированный пример того, где JIT завершится ошибкой по умолчанию:

  @  jit  def  f  ( x ) :  if  x  <  3 : возвращение  3 .  x  2  еще: возвращение - 4  x  # Это не удастся ! пытаться: f  (  2  )  Кроме  Исключение   как  e:  печать   ("Исключение {}" .формат ( e )  )  

В этом случае мы видим, что, по сути, существует два вычислительных графа, разделенных на xSymbolics.jl также имеет форму квазистатичности, проявляющуюся в поведении его алгоритмов. И это по той же причине: в символьных алгоритмах вы определяете символьные переменные, такие как «x» и «y», а затем торгуете через программу для построения статического графа вычислений для «2x ^ 2 + 3y», который затем обрабатываете символически. В часто задаваемых вопросов есть вопрос о том, что происходит, когда преобразование функции в символьное не удается . Если вы посмотрите на пример:

  функция  факториал  ( x )  out = x  в то время как  x >   1  x - =  1  вне * = x  конец  out  end  @ переменные x факториал (Икс)  

Как видите, причина в том, что алгоритм не представляет таблицу как одно математическое выражение: факториал не может быть записан как фиксированное количество умножений, потому что количество умножений зависит от того значения x, которое вы пытаетесь вычислить! для! Ошибка, которую выдает символический язык: «ERROR: TypeError: non-boolean (Num) used in boolean context», которая говорит о том, что он не знает, как символически развернуть «while x> 1», чтобы иметь возможность его представить. статически. И это не то, что не обязательно «исправить», это фундаментально для того факта, что этот алгоритм не может быть представлен фиксированным вычислением и обязательно должен изменить вычисление на основе ввода.

Обработка неквазистатических алгоритмов в символике и машинном обучении

«Решение» состоит в том, чтобы определить новый примитив графа через «@register factorial (x)», чтобы эта функция сама была фиксированным узлом, который не пытается быть символически расширенным. Это та же концепция, что и при определении примитива Jax или примитива Tensorflow, где алгоритм просто не является квазистатическим, и поэтому способ получения квазистатического графа вычислений состоит в том, чтобы рассматривать динамический блок как функцию “y = f (x) “который должен существовать. В контексте как символьных языков, так и фреймворков машинного обучения, чтобы это работало в полной мере, вам также необходимо определить производные от указанной функции. Эта последняя часть – уловка. Если вы еще раз взглянете на глубину документации некоторых из этих инструментов , вы заметите, что многие из этих примитивов, представляющих нестатический поток управления, выходят за пределы области, которая полностью обрабатывается.

“>

Прямо в документации отмечается, что вы можете заменить цикл while на lax. while_loop, но это не поддается автоматическому дифференцированию в обратном режиме. Причина в том, что его реализация AD в обратном режиме предполагает, что такой квазистатический алгоритм существует, и использует его для двух целей: во-первых, для генерации обратного прохода, а во-вторых, для генерации XLA («Tensorflow») описания алгоритма для последующей JIT-компиляции. оптимизировать. XLA хочет статический вычислительный граф, который, опять же, не обязательно существует для этого случая, отсюда и фундаментальное ограничение. Способ обойти это, конечно, состоит в том, чтобы определить свой собственный примитив с его собственным быстрым вычислением градиента, и эта проблема решается прочь …

Или нет?

Где мы можем найти предел квазистатических оптимизаторов?

Есть mac Создайте учебные фреймворки, которые не предполагают квазистатичность, но также оптимизируют, и большинство из этих вещей, таких как Diffractor.jl, Zygote.jl и Enzyme.jl на языке программирования Julia (обратите внимание, PyTorch не принимает квазистатические представления , хотя компиляция TorchScript JIT делает). Это заставило меня задуматься: существуют ли реальные алгоритмы машинного обучения, для которых это реальное ограничение? Это хороший вопрос, потому что, если вы откроете свои стандартные методы, такие как сверточные нейронные сети, это вызов ядра фиксированной функции с определенной хорошей производной, или рекуррентная нейронная сеть, которая имеет фиксированный размер цикла. Если вы хотите опровергнуть это предположение, вы должны перейти к разделу, который в основном посвящен алгоритму, где вы не можете знать «объем вычислений», пока не узнаете конкретные значения в задаче, а решатели уравнений являются чем-то в этом роде.

Сколько шагов нужно, чтобы метод Ньютона сходился? Сколько шагов делает адаптивный решатель ODE? Это не вопросы, на которые можно ответить априори: это, по сути, вопросы, требующие знания:

  1. Какое уравнение мы решаем?
    1. Каково начальное состояние?

    2. В течение какого периода времени?
      1. С каким допуском решателя?

      По этой причине люди, работающие в среде Python, искали «правильный» способ решения уравнений (решение ОДУ, поиск корней f (x) = 0 и т. Д.) ) как представление черного ящика. Если вы еще раз взглянете на статью по нейронным обыкновенным дифференциальным уравнениям , одним из важных моментов, которые он предлагал, было обращение с нейронными ОДУ как с черным ящиком с производной, определяемой сопряженным ОДУ. Причина, конечно, в том, что адаптивные решатели ODE обязательно выполняют итерацию в соответствии с допуском, поэтому обязательно есть что-то вроде «while t Следует ли рассматривать решатели уравнений как квазистатический черный ящик?»

      Нет, не обязательно рассматривать такие алгоритмы как черный ящик. Фактически, мы несколько лет назад опубликовал довольно популярную статью, показывающую, что нейронные стохастические дифференциальные уравнения можно обучать с помощью прямого и обратного автоматического дифференцирования напрямую с помощью некоторых инструментов Julia AD . Причина в том, что эти инструменты AD (Zygote, Diffractor, Enzyme и т. Д.) Не обязательно принимают квазистатические формы из-за того, как они выполняют прямые преобразования источника в источник, и поэтому они могут напрямую различать адаптивные решатели и выдавать правильные градиенты. Таким образом, вам не обязательно делать это в стиле «определить операцию Tensorflow», но что лучше?

      Оказывается, «лучше» действительно трудно определить, потому что два алгоритма не обязательно одинаковы и могут вычислять разные значения. Вы можете свести это к следующему: вы хотите дифференцировать решатель уравнения или вы хотите дифференцировать уравнение и применить к нему решатель? Первый, который эквивалентен автоматическому дифференцированию алгоритма, известен как анализ дискретной чувствительности или дискретная оптимизация. Последний – это непрерывный анализ чувствительности или подходы «оптимизация, затем дискретизация». Машинное обучение – не первая область, в которой встречается эта проблема, поэтому статья на универсальные дифференциальные уравнения и экосистема научного машинного обучения имеет довольно длинное описание, которое я процитирую:

      “” “
      Предыдущее исследование показало, что дискретный сопряженный подход более устойчив, чем непрерывные сопряжения в некоторых случаях [41, 37, 42, 43, 44, 45], в то время как непрерывные сопряженные соединения более устойчивы. стабильна в других [46, 43] и может уменьшить паразитные колебания [47, 48, 49]. Этот компромисс между дискретным и непрерывным сопряженным подходами был продемонстрирован на некоторых уравнениях как компромисс между стабильностью и вычислительной эффективностью [50, 51, 52, 53, 54, 55, 56, 57, 58]. Следует проявлять осторожность, так как стабильность сопряженного подхода может зависеть от выбранного метода дискретизации [59, 60, 61, 62, 63], а наше программное обеспечение помогает исследователям переключаться между всеми этими подходами оптимизации в сочетании с сотнями методов решения дифференциальных уравнений с изменением одной строчки кода.

      “” “

      Или, tl; dr: есть масса предыдущих исследований, которые обычно показывают, что непрерывные присоединения менее стабильны, чем дискретные присоединения, но они могут быть быстрее. Мы провели недавние исследования, которые показывают, что эти утверждения верны в отношении современных проблем с современным программным обеспечением. В частности,
      эта статья о жестких нейронных ODE показывает, почему дискретные сопряжения более стабильны, чем непрерывные сопряжения при обучении на многомасштабных данных
      , но мы также недавно показали, что непрерывные сопряжения могут быть намного быстрее при вычислениях градиентов, чем (некоторые) текущие методы AD для дискретных сопряжений .

      Итак, хорошо, в использовании дискретных сопряженных методов есть настоящая выгода, если вы работают с этими жесткими дифференциальными уравнениями, дифференцируют уравнения в частных производных и т. д., и это было известно со времен 170 в области теории управления. Но кроме этого, это промывка, и поэтому неясно, лучше ли различать такие алгоритмы в машинном обучении, верно?

      Доработка по существу неквазистатического алгоритма, ускоряющего машинное обучение

      Теперь это подводит нас к тому, как недавняя статья ICML вписывается в этот рассказ. Есть ли неквазистатический алгоритм, который действительно полезен для стандартного машинного обучения? Ответ оказался положительным, но для того, чтобы добраться туда, потребуется несколько хитрых уловок. Во-первых, настройка. Нейронные ODE могут быть интересным методом для машинного обучения, потому что они используют адаптивный решатель ODE, чтобы по существу выбрать количество слоев для вас, так что это похоже на повторяющуюся нейронную сеть (или, более конкретно, как остаточную нейронную сеть), которая автоматически находит ” правильное “количество слоев”, где количество слоев – это количество шагов, которые решающая программа ODE решает сделать. Другими словами, нейронные ODE для обработки изображений – это алгоритм, который автоматически выполняет оптимизацию гиперпараметров. Аккуратно!

      Но … каково «правильное» количество слоев? Для оптимизации гиперпараметров можно предположить, что это «наименьшее количество слоев для точного прогнозирования». Однако по умолчанию нейронные ОДУ не дадут вам такое количество слоев: они дадут вам все, что захотят. Фактически, если вы посмотрите на исходный документ нейронного ODE, по мере того, как нейронное ODE обучается, оно продолжает увеличивать количество используемых слоев:

      “>

      Итак, есть ли способ изменить нейронную ODE, чтобы она определяла “правильный” количество слоев »как« наименьшее количество слоев »? В работе Изучая легко решаемые дифференциальные уравнения , они именно это и сделали. нейронное ОДУ. Они рассмотрели решение и отметили, что ОДУ, в которых происходит больше изменений, обязательно труднее решить, поэтому вы можете преобразовать процесс обучения в оптимизацию гиперпараметров, добавив член регуляризации, который говорит: «Сделайте производные более высокого порядка малыми как это возможно ». Остальная часть статьи посвящена тому, как реализовать эту идею. Как это было сделано? Что ж, если вам нужно рассматривать алгоритм как черный ящик, вам нужно определить некоторый способ черного ящика для определения производных высокого порядка, который затем приводит к До Джесси tty классная формулировка автоматического дифференцирования в режиме Тейлора. Но как бы вы это ни выразились, это будет дорогостоящий объект для вычисления: вычисление градиента дороже, чем прямой проход, а вторая производная больше, чем градиент, а третья и т. Д., Поэтому алгоритм, которому нужны 6-е производные будет противно тренироваться. Проделав довольно героическую работу, они получили формулировку этой операции черного ящика, обучение которой занимает в два раза больше времени, но успешно выполняет оптимизацию гиперпараметров.

      Конец истории? Это далеко не так!

      Лучший способ превратить нейронные ОДУ в алгоритм автоматической оптимизации гиперпараметров

      Есть ли способ ускорить автоматическую оптимизацию гиперпараметров с помощью нейронных ODE? Да, , и наша статья заставляет их не только тренироваться быстрее, чем этот другой метод, но и заставляет его тренироваться быстрее, чем обычное нейронное ODE . Мы можем сделать оптимизацию гиперпараметров слоя менее чем бесплатной: мы можем сделать это дешевле, чем не проводить оптимизацию! Но как? Хитрость заключается в том, чтобы открыть черный ящик. Позвольте мне показать вам, как выглядит шаг адаптивного решателя ODE:

      “>

      Обратите внимание, что адаптивный решатель ODE выбирает, подходит ли временной шаг с использованием оценки ошибок. Алгоритм ODE фактически построен так, что оценка ошибки, оценка того, «насколько сложно решить это ODE», вычисляется бесплатно . Что, если мы используем эту бесплатную оценку ошибок в качестве метода регуляризации? из этого 42 x обучаться быстрее, чем раньше, при этом автоматически выполняется оптимизация гиперпараметров.

      “>

      Обратите внимание, где мы закончили: в результате Алгоритм обязательно не является квазистатическим.Эта оценка ошибки вычисляется фактическими шагами адаптивного решателя ODE: для вычисления этой оценки ошибки вы должны выполнить те же вычисления, тот же цикл while, что и решатель ODE. , вы не можете избежать прямой дифференциации решателя ODE, потому что части внутренних вычислений решателя ODE теперь являются частью регуляризации. Это то, что принципиально не оптимизируется методами, требующими квазистатических графов вычислений (Jax, Tensorflow и т. д.), и это то, что делает оптимизацию гиперпараметров дешевле, чем отсутствие оптимизации гиперпараметров, поскольку регуляризатор вычисляется бесплатно. Я просто нахожу этот результат таким классным!

      Заключение: обнаружение ограничений наших инструментов

      Итак, да, статья представляет собой документ по машинному обучению о том, как бесплатно выполнять оптимизацию гиперпараметров, используя трюк с нейронными ОДУ, но я думаю, что общий контекст программного обеспечения здесь выделяется истинное открытие бумаги. Это первый алгоритм, который я знаю, где есть явный стимул для его использования в современном машинном обучении, но также есть фундаментальная причина, по которой общие фреймворки машинного обучения, такие как Jax и Tensorflow, не смогут их лечить. оптимально. Даже TorchScript PyTorch принципиально не будет работать с этим алгоритмом из-за допущений процесса компиляции. Эти предположения были выбраны с умом, потому что большинство алгоритмов могут им удовлетворить, а этот – нет. Это значит машинное обучение алгоритмически застряло в колее? Возможно, потому что я полностью уверен, что кто-то, работающий с набором инструментов, который не оптимизирует этот алгоритм, никогда бы не нашел это заставляет меня задуматься.

      Какие есть другие алгоритмы, которые просто лучше наших текущие подходы, но хуже только из-за текущих фреймворков машинного обучения? Я не могу дождаться выпуска Diffractor.jl, чтобы глубже изучить этот вопрос.

Leave a comment

Your email address will not be published. Required fields are marked *