Хочешь сэкономить на облаке? Не игнорируй, утилизацию GPU при тренировке сетей

Источник: https://www.pexels.com/photo/unrecognizable-man-holding-wallet-with-money-4386421/

Современные сети тренируются на GPU пока данные подготавливает CPU.

При этом программисты, обращают внимание на то как уменьшается со временем функция потерь, игнорируя не менее важную метрику — утилизацию GPU, которая про $$$ причем часто не малые.

Рекомендуется стремимся к тому, чтобы утилизация составляла как минимум 80%. Если показатель ниже, стоит искать причины.

История из практики про много денег

На прошлой работе был случай. Наша команда и соседи решали похожие задачи на одних и тех же данных. Но наши модели были заметно точнее.

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

Но и у нас тренировка останавливалась через три дня. Полезли глубже.

Посмотрели на функцию потерь — начало одинаковое, но наша в 4 раза длиннее и этот дополнительный кусок уходит ниже к нулю. То есть через нашу сеть данные шли в в 4 раза быстрее.

Тут догадались проверить утилизацию GPU. У нас 80%, у них 20%. При этом разница между нашими командами во фреймворака мы использовать PyTorch, а они Tensorflow.

А на тот момент TensorFlow очень неэффективно работал в Multi GPU режиме. Тут я без претензий к ребятам, так карта легла, все могло быть ровно наоборот.

Если сильно не вру, годовой бюджет на AWS у нашего подразделения был около $10,000,000 в год, из которых больше половины планировалось потратить на GPU.

Любители арифметики могут пересчитать в бутылках пива чего нам стоило игнорирование важной метрики.

Низкая утилизация бьет сразу по нескольким финансовым болевым точкам. Разберем случай 20% против 80%. Тренируем в 4 раза медленнее => для достижения того же результата надо тренировать в 4 раза дольше, что:

  • стоит в 4 раза больше денег, так как на AWS оплата за GPU / час

  • занимает в 4 раза больше времени, то есть либо сети недотренированные, либо в 4 раза более медленные итерации

  • дает коcвенные затраты на зарплаты программистам. Сеть тренируется — зарплата капает.

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

Варианты:

  • Средняя утилизация за весь период когда сервер с GPU запущен. Позволяет находить ситуации когда когда работник запустил сервер с GPU, не свернул и пошел домой.

  • Низкая утилизация GPU при тренировке говорит, что есть узкие места, которые можно устранить.

А теперь действия, которые помогут определить и улучшить утилизацию GPU, снизив тем самым затраты и ускорив процесс обучения.

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

Первые шаг — посмотреть что происхот сейчас

Как и в любой диагностике надо понять где мы сейчас.

Есть продвинутые инструменты, например, Weights & Biases, там можно собирать данные, агрегировать, экспортировать, создавать всевозможные отчеты для разных команд — круто, масштабируемо, но надо настраивать.

Если хочется быстро и дешево, то можно запустить утилиту nvtop, которая присуствует в любом современном линуксе.

Экран nvtop. Пример хорошей ситуации. На всех четырех GPU утилизация 80%+

Экран nvtop. Пример хорошей ситуации. На всех четырех GPU утилизация 80%+

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

Может получиться, что используются не все GPU по причине что 

  1. В коде закралась строчка которая меняет значения переменной системной CUDA_VISIBLE_DEVICES.

  2. Используются фреймворки для тренировок типа Pytorch Lightning и в конфиге указано неправильное число GPU.

Используются все GPU, но утилизация ниже требуемой

Подозреваемый — CPU.

Надо понять, что происходит с подготовкой данных.

При тренировке сети GPU отвечает за forward и backward pass, а подготовкой этих данных занимается CPU.

Источник: https://towardsdatascience.com/feeding-the-beast-understanding-and-optimizing-the-data-loading-path-for-training-deep-learning-f896548dca89


Как только GPU отработал с батчем, CPU пересылает новый.

Q: А что случается когда CPU не успевает за GPU?
A: GPU простаивает и утилизация падает.

Почему такое случается?

И тут снова два варианта.

Медленный CPU и / или задача по подготовке данных требует больших вычислительных мощностей.

Для диагностики — утилита htop, также доступна во всех современных дистрибутивах линукса.

Если nvtop показывает низкую утилизацию, а htopпоказывает 100% утилизацию CPU, то это оно.

CPU забит на 100% и не вытягивает. Источник: https://medium.com/swlh/running-a-convolutional-neural-network-on-raspberry-pi-4fc5bd80aa4d

Случается при совпадении факторов:

  1. Медленный для данной задачи CPU

  2. Тяжелый препроцессинг или аугментационный пайплайн

С такой ситуацией мы столкнулись в одном из соревнований на Kaggle. Для аугментации использовали библиотеку imgaug. В ней замечательный функционал, но под скорость не оптимизирована.

Почти каждый в команде имел комрьютер с несколькими GPU и CPU не справлялся.

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

Код для benchmark: https://github.com/albumentations-team/albumentations/tree/main/benchmark

Подготовка данных происходит в DataLoader’е и именно там и надо смотреть.

  • Посмотреть на аугментационный пайплайн. Если есть кусок, который уменьшает размер изображения за счет Crop или Resize, лучше это делать первой операцией, а не последней. Изображение 4096×4096 в 64 раза более ресурсоемко обрабатывать чем 512×512.

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

  • Где-то поможет смена библиотеки для аугментаций на более быструю.

Сколько-то знакомых пожалели когда купили компьютеры для тренирвки сетей и взяли с несколькими мощными GPU, но не очень мощным CPU, ибо «самое важное — это GPU, так как тренировка проходит на них» и потом долго пытались придумать как повышать утилизацию и понижать простаивание.

Не вытягивает I/O

Если nvtop и htop не на максимуме, то подозреваемым становится чтение данных.

Под этот случай есть утилита iotop, но я сам ей толком не пользовался.

Данные надо откуда-то брать.

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

Данные на лету считываются с облачного хранилища, скажем S3

Пример:
знакомые тренировали разные модели на одних и тех же данных, которые хранились на S3.

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

Скорости сети не хватало, GPU простаивали, пришлось менять.

Надо:

  1. Либо перед тренировкой копировать средствами bash данные локально.

  2. Либо Data Loader с кэшированием, который при первом запросе к изображению считывает с S3 и сохраняеть локально, а при последующих запросах подгражает из кэша.

Если данные уже хранятся л

Локально, но скорости считывания не хватает.

Поместить данные в оперативную память.
Оперативная память дешевая, докупить в свой компьютер или выбрать машину в облаке где ее много — это вариант.

Случайный доступ к данным в оперативной памяти очень быстрый. Проблема может решиться без изменений в кодовой базе.

HDD => SSD

Тот же аргумент, что и выше. SSD недорогие, что при покупке в личное пользоваение, что при выборе машины в облаке.

Уменьшение размера изображений

Обработать заранее. Как упоминалось выше — изображение размером 4096×4096 в 64 раза тяжелее чем 512×512.

Это, кстати, не всегда приемлимо.

  • Криминалистика — определяют это исходное изображение или был монтаж и внего был вставлен кусок другого, там важны не видимые глазу микроартефакты, характерной для модели телефона. При масштабировании могут пропаcть.

  • По selfie определять глюкозу в крови, пульс, уровень увлажненности кожи.

Но это, скорее, узкие ниши и в большинстве случаев менять масштаб можно.

При тренировке ImageNet картинки считываются с диска, после чего им на лету меняют масштаб с сохранением отношения сторон так, чтобы минимальная сторона была 256, и после этого при тренировке случайным образом выделяют регионы 224×224.

Можно это сделать заранее.

Смена уровня компрессии

В JPEG переход от 90 качества до 70 уменьшает размер изображений в два раза.

Источник: https://code.flickr.net/2015/09/25/perceptual-image-compression-at-flickr/

Группировка изображений в мини батчи

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

Можно собирать изображения в небольшие группы с помощью tfrecords или просто tar файлов. А сам тренировочный батч собирать из таких мини групп. На практике работает неотличимо от полностью случайных батчей.

Смена библиотеки для считывани JPEG файлов с диска
[Честно говоря, изначально текст я писал под эту секцию, но вступление что-то затянулось]

Изображения хранятся в сжатом формате, соответственно их надо не только считать, но и декодировать. И разные библиотеки делают это с разной скоростью.

Я написал код для benchmark [ссылка на Github], где сравнил с какой скоростью библиотеки:

  • skimage: 0.22.0

  • imageio: 2.34.0

  • opencv: 4.9.0.80

  • PIL (pillow): 10.2.0

  • jpeg4py: 0.1.4

  • torchvision: 0.17.1

  • tensorflow: 2.15.0.post1

считывают 2000 файлов с валидационного сета ImageNet, делая 1 разогревочный прогон и 5 боевых, значения по которым усреднялись.

с трех HDD и трех SSD, которые стоят в моем десктопе

  • QVO 870: SAMSUNG 870 QVO SATA III 2.5» SSD 8TB (MZ-77Q8T0B)

  • Samsung 960Pro: Samsung 960 PRO Series — 512GB PCIe NVMe — M.2 (MZ-V6P512BW)

  • WDC 8TB: WD Red Pro 8TB NAS Internal Hard Drive — 7200 RPM, SATA 6 Gb/s (WD8003FFBX)

  • WDC 6TB: WD Red Pro 6TB 3.5-Inch SATA III 7200rpm NAS Internal Hard Drive (WD6002FFWX)

  • Toshiba 8TB: Toshiba X300 8TB Performance Desktop Gaming Hard Drive 7200 RPM 128MB Cache SATA 6.0Gb/s 3.5 inch (HDWF180XZSTA)

  • EVO 850: Samsung 850 EVO 1 TB 2.5-Inch SATA III Internal SSD (MZ-75E1T0B/AM)

на CPU: AMD Ryzen Threadripper 3970×32-Core Processor. В коде я постарался для вычислений выделить только один поток, так как это масксимально приближено к сетапу в котором подгатавливается батч для сети.

da01d15f475087202a945a3d16ea5c1d.png

Что вообще неожиданно, так как opencv быстрая библиотека и я использую как раз ее для чтения файлов. А в Tensorflow и Torchvision мы дополнительно конвертируем тензоры в массивы numpy.

Заключение

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

© Habrahabr.ru