Узнайте, как инструменты ИИ могут решать проблемы, не связанные с ИИ

На высоком уровне дифференцируемое программирование - это парадигма написания программного обеспечения, которое можно автоматически дифференцировать. Говоря простым языком, мы можем быстро проверить, как изменения во входных данных программы влияют на выходные данные программы. Например, скажем, мы кодируем автоматически дифференцируемый симулятор автомобиля. Используя эту систему, мы можем смоделировать автомобиль и изучить, как изменение некоторых из его переменных (например, радиуса шины, подвески и т. Д.) Связано с изменениями его выходных характеристик (например, расхода топлива, шума и т. Д.).

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

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

До сих пор решение проблем с использованием производных инструментов было нишевой темой, зарезервированной для более математически ориентированных областей, таких как искусственный интеллект и операционные исследования. Тем не менее, с автоматическим дифференцированием большая часть математической базы, необходимой для запуска и работы этих систем, улучшается, что делает эти методы доступными для гораздо более широкой аудитории.

Если вы все еще не уверены, актуальна ли эта концепция для вас, подумайте о прогрессе, достигнутом в глубоком обучении. Благодаря автоматической дифференциации исследователи начали спрашивать эти системы, как улучшить свои сети, чтобы они лучше работали с конкретными наборами данных, - процесс, известный как обратное распространение. Делая это без особых усилий, в этой области стало легче исследовать различные сети и подходы, что привело к огромному успеху, который мы наблюдаем сегодня с такими моделями, как GPT-3 и StyleGAN.

В этой статье я подробно расскажу, что такое дифференциация и чего она может достичь, а затем сделаю обзор того, как можно использовать TensorFlow для универсального дифференцируемого программирования. Наконец, мы рассмотрим некоторые из его ограничений и в каких случаях его нельзя использовать.

Если эта тема вызвала у вас интерес, продолжайте читать :)

Дифференцируемое программирование

Давайте начнем с некоторых определений того, что такое дифференцируемое программирование, начиная с самого буквального определения до более полезного:

  • Стиль кодирования, при котором программы могут быть автоматически дифференцированы
  • Универсальная парадигма для решения задач с использованием градиентного спуска
  • Обобщение глубокого обучения на более широкий класс проблем

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

Дифференциация

Представьте, что вы владелец растущего бизнеса. У вас одна фабрика, и вы совершенно недовольны ее производительностью. Чтобы улучшить его, вы нанимаете ведущего математика, который внимательно изучает конвейер завода и приходит к выводу, что его производительность определяется следующей формулой:

F (x) = ~ добавьте сюда сумасшедшую математику ~

Это x количество команд, работающих на заводе, и F (x) выпуск продукции, измеренный в тысячах произведенных единиц. И остальные? Остальное ты не знаешь. Итак, все, что мы можем сделать, это попробовать значения и запросить математика.

Сейчас у вас четыре команды, поэтому, согласно формуле, ваша фабричная выработка составляет F (4) = 64 тысячи единиц. Как владелец и стремясь увеличить производство на своей фабрике, вы спрашиваете математика: следует ли мне увеличивать или уменьшать количество команд?

Задержав пример на секунду, вы просто спросили, что математики называют производной функции: как изменение одного из входных данных влияет на результат. В нашем случае, как изменение количества команд влияет на общую производительность фабрики.

Если производная положительная, это означает, что увеличение количества команд повысит производительность. Точно так же отрицательная производная указывает на то, что меньшее количество команд будет более продуктивным. Однако имейте в виду, что производная указывает только правильное направление; он не говорит вам, сколько вам следует изменить.

Визуально мы знаем, что с четырьмя командами фабрика производит 64 тысячи единиц, и, увеличивая количество групп (двигаясь вправо), мы ожидаем увеличения нашей урожайности.

Вернемся к примеру: вам не нужно знать, как вычислить производную, поскольку мы можем вычислить ее автоматически. Под автоматом я имею в виду надежного математика, который говорит вам, что производная положительна.

Градиентный спуск

Поскольку мы знаем, в каком направлении нам нужно двигаться (нанять больше команд), но не знаем, сколько команд нам нужно добавить, делаем предположение: мы нанимаем еще три команды. В итоге завод производит F (7) = 91 тысячу единиц при семи командах, улучшение на 27 тысяч единиц!

Увидев результаты, вы решаете еще раз спросить математика, как лучше поступить - и ответ - нанять больше команд.

Будучи немного жадным, вы решаете сделать еще больший шаг, чем в прошлый раз, поэтому нанимаете пять новых команд, всего 12. Возвращаясь к формуле, F (12) = 96. На этот раз не такое улучшение, особенно в особенности. учитывая, что вам нужно платить гораздо больше зарплат.

Озадаченный, вы спрашиваете математика, не следует ли вам нанять больше людей. На этот раз математик качает головой и сообщает, что продуктивность повысится с увольнением (шутка, точные слова, которые она сказала, были «производная отрицательная»).

Печально, но жадно, вы решаете отпустить две команды, оставив в общей сложности десять команд. Используя формулу, F (10) = 100. Действительно, как сказал математик, отпуск некоторых людей сделал фабрику менее переполненной и, следовательно, более производительной.

На данный момент вы не уверены, нужно ли вам распустить больше команд или меньше, поэтому вы спрашиваете математика еще раз. К вашему удивлению, она говорит, что фабрика сейчас в идеальном состоянии. Производная равна нулю. Любое движение навредит продуктивности.

В этом разделе описывается процесс градиентный спуск (или подъем), причем градиент - это просто причудливое название производной. Говоря неформально, мы оптимизируем функцию, перемещаясь по градиенту. Если он говорит об увеличении, вы увеличиваете. Если он говорит об уменьшении, вы уменьшаетесь.

Этот процесс идеален? Нет, это не так. В каком-то смысле это математическая догадка. Он говорит вам направление, но не то, сколько вам нужно идти. Кроме того, вам нужно с чего-то начать, и, в зависимости от того, с чего вы начнете, вам придется идти более или менее, пока не найдете это золотое место.

Глубокое обучение

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

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

Если рассматривать t как текущий месяц, u как количество произведенных единиц, p как цену, которую вы собираетесь продать за каждую единицу, и x как количество команд, математик сформулировал, что прибыль вашего завода представляет собой комбинацию всех этих терминов, как указано в формуле:

Прибыль (t, u, p, x) = налоги (Спрос (t, p) ⋅ u⋅ (p − Cost (u)) - Заработная плата (x)

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

Ваше первое впечатление - это то, что вы будете делать с таким уравнением. Однако вы быстро вспоминаете, что вам не нужно много знать; пока у вас есть градиент, вы можете оптимизировать эту формулу медленно, но неуклонно, двигаясь по направлению градиента. Ваша единственная реальная задача - определить функцию прибыли; все остальное сделает система.

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

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

В течение многих десятилетий глубинные модели никогда не становились популярными: (1) они были слишком тяжелыми для использования и (2) их было трудно тренировать. В то время как закон Мура должен был решить первое, неуловимое второе оставалось, в общем, неуловимым.

Что изменилось? Градиентные методы. Вместо прямого и объяснимого алгоритма обучения мы итеративно оптимизируем модели, чтобы получить более высокие баллы по ключевым показателям. Еще лучше, если мы разработаем алгоритмы для вычисления градиентов для нас, мы можем сойти с ума от моделей, не особо заботясь о существовании (или длине) аналитического выражения для всего этого.

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

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

Пример кодирования

В приведенном выше примере F (x) фактически было -x² + 20x. Тогда мы сделали градиентный спуск вручную. В этом разделе мы рассмотрим, как это можно сделать с помощью программного обеспечения и как с этим справиться. Вот код:

В приведенном выше коде TensorFlow определяется функция x и две переменные: x0, инициализированная как переменная TensorFlow, и steps, представляющая количество проходы оптимизации мы выполним. Строки 8–14 определяют фактический цикл оптимизации. На каждом этапе мы используем объект GradientTape для записи градиентов y = function (x0). Другими словами, мы выбираем значение F (x) для текущего x0 и сохраняем его в переменной с именем y.

Настоящая магия описана в строках 12–14. Сначала мы просим ленту вычислить градиент x0 относительно y. Другими словами, мы спрашиваем, как x0 влияет на y - как ввод влияет на вывод. Затем, в строке 14, мы перемещаем наше текущее предположение (x0) по направлению градиента. На этот раз мы используем значение градиента как догадку, на сколько нам нужно двигаться. В частности, мы перемещаем одну четверть градиента. Причина проста: чем больше шаг, тем выше вероятность выхода за рамки оптимизации. Всегда безопаснее идти небольшими шагами, чем гигантскими прыжками. Наконец, в строке 13 отображается наш прогресс.

Запустив сниппет, получаем:

Шаг [1/10]: F (7,00) = 64,00, Градиент: 12,00
Шаг [2/10]: F (8,50) = 91,00, Градиент: 6,00
Шаг [3/10]: F (9,25) = 97,75, Градиент: 3,00
Шаг [4/10]: F (9,62) = 99,44, Градиент: 1,50
Шаг [5/10]: F (9,81) = 99,86, Градиент: 0,75
Шаг [6/10]: F (9,91) = 99,96, Градиент: 0,38
Шаг [7/10]: F (9,95) = 99,99, Градиент: 0,19
Шаг [8 / 10]: F (9,98) = 100,00, Градиент: 0,09
Шаг [9/10]: F (9,99) = 100,00, Градиент: 0,05
Шаг [10/10]: F (9,99) = 100,00, градиент: 0,02
Конечный результат: F (9,99) = 100,00

К восьмому шагу мы уже были так близки к 10, чтобы округлить наш результат до 100. С математической точки зрения мы сошлись примерно на восемь шагов. Конечная цель этого подхода - конвергенция - достичь определенного что любое улучшение сверх этого незначительно.

В образовательных целях я настоятельно рекомендую самостоятельно запустить приведенный выше фрагмент и поиграть с коэффициентом 0,25. Мы называем это «скоростью обучения» или «размером шага». Если вы установите его слишком большим, например, 2, оптимизация взорвется. С 4 будет 28, -44, 172, -476, 1468… Если вы установите слишком маленькое значение, 0,01, потребуется более 100 шагов для достижения x = 10. На практике настройка скорости обучения имеет решающее значение для улучшения результатов.

Приведенный выше пример представляет собой сценарий градиентного подъема. Мы хотим максимизировать функцию. Более популярным алгоритмом является градиентный спуск, который стремится минимизировать функцию. Последний более популярен, поскольку позволяет нам устанавливать цель и постоянно продвигаться в направлении минимизации расстояния до цели. Например, при работе с машинным обучением мы часто хотим, чтобы ошибка нашей модели достигла нуля. Следовательно, это формулировка минимизации.

Следующий фрагмент пытается оптимизировать нашу функцию, чтобы приблизиться к произвольному значению 48, минимизируя расстояние между y и 48.

Мы добавили функцию цель подхода, которая вычисляет квадрат расстояния между y и нашей целью t. В процессе оптимизации мы вычисляем y,, а затем вычисляем t. Более того, градиент x0 теперь берется относительно t. Наконец, мы скорректировали скорость обучения до -0,001. Знак минус указывает на спуск в градиентном спуске. Значение 0,001 было найдено методом проб и ошибок. Значения выше этого не сходились за десять шагов.

Пока что приведенного выше фрагмента достаточно для решения многих задач, требующих от вас поиска подходящего входного значения для функции. Обучение нейронных сетей - это просто поиск оптимальных параметров, соответствующих входам и выходам. Например, рассмотрим официальное руководство по индивидуальным тренировочным циклам. Функция становится нейронной сетью, target_target заменяется функцией потерь, а обновление x0 превращается в оптимизатор (Adam). Затем вся процедура является макетом для работы нескольких пакетов для нескольких эпох.

Ограничения

Поскольку это дифференцируемый программный фреймворк для реализации градиентного спуска, его варианты использования и ограничения практически одинаковы. Общий вариант использования градиентного спуска выглядит следующим образом:

  1. Задача числовая (т.е. ее можно определить как математические выражения)
  2. Он достаточно гладкий (т.е. вы можете вычислить его производные)
  3. Он настолько сложен, что вы не можете решить его более простыми способами.

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

И наоборот, если ваша задача полностью числовая, но слишком проста, следует использовать другие методы. В приведенном выше примере F (x) было -x² + 20x. Мы оптимизировали его до F (10) = 100, используя градиентный спуск четыре раза. Однако, если бы мы знали аналитическое выражение раньше, мы бы вычислили аналитическую форму производной (F '(x) = -2x + 20), нашли бы ее корень (x = 10) и получили бы наш ответ. Кроме того, эти методы не подходят для целых чисел, поскольку они не являются гладкими. Для этого вам нужно использовать некоторые дополнительные приемы для генерации целочисленных решений.

Пока что это случаи, когда вам вообще не следует использовать градиентный спуск. Однако даже в тех случаях, когда вы можете его использовать, вы никогда не должны забывать об одном: вам всегда нужна отправная точка для оптимизации.

Вы, вероятно, сойдетесь при разных значениях, если попробуете несколько оптимизаций градиентного спуска, начиная с других позиций. Это особенно верно для многомерных задач со многими переменными. Нет никакой гарантии, что вы получите наилучший возможный результат. Единственная гарантия - вы окажетесь в лучшем месте, чем начали, или, в худшем случае, останетесь на своем месте.

Вы можете думать об этом как о случайном месте в Гималаях и о возможности видеть только в десяти футах перед собой. Этого достаточно, чтобы подняться на вершину горы, на которой вы находитесь, но вы никогда не узнаете, находитесь ли вы на самой высокой вершине или просто на локальном максимуме. Как показано на картинке ниже.

На этом пока все. Не стесняйтесь комментировать или связаться со мной, если у вас есть какие-либо вопросы по этой статье.

Если вы новичок в Medium, настоятельно рекомендую подписаться. Статьи среднего размера - идеальная пара со StackOverflow для специалистов в области данных и ИТ, а тем более для новичков. Пожалуйста, подумайте об использовании моей партнерской ссылки при регистрации.

Спасибо за чтение :)