Повысьте эффективность своих систем машинного обучения всего в 4 простых шага

Улучшите эффективность ваших систем машинного обучения всего за 4 простых шага

Изображение, сгенерированное с помощью DALL.E-3

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

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

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

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

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

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

Добро пожаловать на эмоциональные американские горки оптимизации ML! Вот простой 4-шаговый процесс, чтобы изменить ситуацию в вашу пользу:

  1. Провести измерения
  2. Упростить
  3. Оптимизировать
  4. Повторять

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

1 – Провести измерения

“Измерь дважды, отрежь один раз” – Смышленый кто-то.

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

Высокий уровень

Такие данные являются предметом еженедельной встречи “На сколько мы f***бы?” и вы бы хотели, чтобы эти показатели были представлены в каждом запуске. Они позволят вам получить общее представление о производительности вашей системы.

Пакеты в секунду – насколько быстро мы обрабатываем каждую партию данных? это должно быть как можно выше

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

Использование GPU – сколько GPU используется во время обучения? Оно должно быть постоянно близким к 100%, если не так, значит, у вас есть простой резерв, который можно оптимизировать.

Использование CPU – насколько используются ваши процессоры во время обучения? Снова, это должно быть как можно ближе к 100%.

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

Низкий уровень

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

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

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

Профилирование моделей – Инструменты, такие как Tensorboard, поставляются с отличными инструментами профилирования, которые помогут вам определить, что замедляет вашу модель.

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

Обязательно ознакомьтесь с этой отличной статьей о профилировании в Python от RealPython для получения дополнительной информации!

2 – Упростите

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

Советы

  • Замените другие компоненты заглушками и фиктивными функциями, которые только предоставляют ожидаемые данные.
  • Симулируйте тяжелые функции с помощью функций sleep или фиктивных вычислений.
  • Используйте фиктивные данные, чтобы убрать накладные расходы на генерацию и обработку данных.
  • Начните с локальных, однопроцессорных версий вашей системы, прежде чем переходить к распределенным.
  • Симулируйте несколько узлов и акторов на одной машине, чтобы устранить сетевые накладные расходы.
  • Найдите теоретическую максимальную производительность для каждой части системы. Если бы все остальные узкие места в системе исчезли, кроме этого компонента, какая будет наша ожидаемая производительность?
  • Снова проведите профилирование! Каждый раз, когда вы упрощаете систему, запускайте профилирование заново.

Вопросы

Когда мы сосредоточились на узком месте, есть несколько ключевых вопросов, на которые мы хотим получить ответы

Какова теоретическая максимальная производительность этого компонента?

Если мы изолировали узкое место, то мы должны быть способны ответить на этот вопрос.

Насколько мы далеко от максимального значения?

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

Есть ли глубинное узкое место?

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

3 – Оптимизируйте

Хорошо, предположим, мы определили самое большое узкое место, и теперь мы переходим к интересной части – как улучшить вещи? Обычно есть 3 области, на которые следует обращать внимание для возможного улучшения

  1. Вычисления
  2. Коммуникация
  3. Память

Вычисления

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

Параллелизация – убедитесь, что выполняете как можно больше работы параллельно. Это первая большая победа в проектировании вашей системы, которая может существенно повлиять на производительность. Рассмотрите такие методы, как векторизация, пакетная обработка, многопоточность и многопроцессорность.

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

Оптимизация – мы все знаем, что Python не славится своей скоростью. К счастью, мы можем передать критические вычисления более низкоуровневым языкам, таким как C/C++.

Масштабирование оборудования – Это некая уловка, но если ничего не помогает, всегда можно просто добавить больше компьютеров для решения проблемы!

Коммуникация

Любой опытный инженер скажет вам, что коммуникация является ключевым моментом для успешной реализации проекта, и мы, конечно же, имеем в виду коммуникацию в пределах нашей системы (небось, чтобы нам никогда не пришлось разговаривать с коллегами). Некоторые хорошие правила, которыми следует руководствоваться:

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

Оставаться в локальной сети – Что бы ни случилось, старайтесь сохранить все на одном компьютере до того, как перейти к распределенной системе. Это упрощает вашу систему и уменьшает накладные расходы связи, характерные для распределенной системы.

Async > Sync – Выявите все, что может быть выполнено асинхронно. Это поможет уменьшить накладные расходы связи, поддерживая работу в ходе перемещения данных.

Избегайте перемещения данных – перемещение данных с ЦП на ГП или из одного процесса в другой требует затрат! Старайтесь сделать это как можно меньше, либо снизьте его влияние, выполнив это асинхронно.

Память

Последнее, но не менее важное – это память. Многие из приведенных выше аспектов могут быть полезны при решении узкого места, однако это может быть невозможно, если нет доступной памяти! Давайте рассмотрим некоторые вещи, о которых следует подумать.

Типы данных – старайтесь сделать их как можно меньше, чтобы уменьшить затраты на связь и память. Кроме того, современные ускорители также уменьшают объем вычислений.

Кэширование – подобно уменьшению объема вычислений, умное кэширование может помочь сэкономить память. Однако убедитесь, что ваши закэшированные данные используются достаточно часто, чтобы оправдать кэширование.

Предварительное выделение – нечто, с чем мы не привыкли иметь дело в Python, но строгое предварительное выделение памяти означает, что вы точно знаете, сколько памяти вам нужно, снижает риск фрагментации и, если вы можете писать в общую память, сокращает связь между вашими процессами!

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

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

4 – Повторение

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

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

Заключение

Строительство масштабных систем машинного обучения И ТАК СЛОЖНО. Это похоже на игру в извращенную версию «Где Уолдо» в сочетании с «Темными душами». Если вы все-таки сумеете обнаружить проблему, вам потребуется несколько попыток, чтобы ее победить, и вы проведете большую часть времени, получая по заднице и задавая себе вопрос: «Зачем я тревожу себя этим в пятницу вечером?». Простой и принципиальный подход может помочь вам пройти через эту последнюю битву с боссом и почувствовать сладкие, теоретические максимальные операции с плавающей запятой.

ML в действии | Донал Бирн | Substack

Рассылка по машинному обучению, которая предоставляет несанкционированные советы, практические идеи и вынесенные уроки в быстро…”

donalbyrne.substack.com