инференс модели что это
Quantization Aware Training. Или как правильно использовать fp16 inference в TensorRT
Если ты читаешь эту статью ради подробного туториала о запуске TensorRT, то его тут нет. Он есть тут. Здесь же про опыт применения и несколько важных моментов, о которых не говорят в официальной документации.
Что за зверь ваш low-precision?
float16
int8 и прочее
Кроме fp16 с простым кастованием есть много идей по более оптимальному использованию бит в 16-битном значении. Просто чтобы напомнить:
Объясню, почему мы не смотрим на 8/4 битные квантизации. Дело в том, что здесь не обойтись без потери точности. Например, тут говорят как оптимально юзать int4 и радуются, что потеряли не 15%, а 8% точности. Или вот красноречивая табличка от Nvidia о западении точности при использовании int8:
TensorRT
InputSize. Мы работаем с изображениями. И размер входного изображения иногда может меняться во время рантайма. Но TRT требует его задавать жестко, что логично. Да, есть возможность задать минимальный и максимальный размеры, а TRT создаст несколько профилей оптимизации. Но всё же это не так гибко, как в TensorFlow, а иногда нужно.
Ну и под конец еще добавлю, что в рантайме эти движки отжирают лишь необходимый минимум GPU RAM и замечательно работают параллельно (если правильно работать с TensorRT Context в вашем коде рантайма).
Контекст задачи
На opentalks.ai2020 мы рассказывали, как, используя Pruning и физичность данных, ускорили обработку в 4 раза и не потеряли в точности. Статью про Pruning я уже выкладывал. Но сегодня давайте поговорим конкретно про low-precision inference.
Как мы запустились и потеряли нежные фичи
13:00. Добрали изображения в основной датасет. Поставили доучиться с низким LR.
Следующее утро началось с мема:
Quntization Aware Training. Учи на том, на чем будет работать
Как в Tensorflow 2.0?
Выводы
Что особенно важно в годы дефицита чипов и дорогих GPU. Я всё же за использование GPU в тех местах, где они приносят пользу людям, автоматизируя что-то. А не там, где они приносят прибыль, делая деньги из подогретого воздуха.
UPD: Отвечая на вопрос DistortNeo про реальную максимальную производительность на практике.
В теории TensorRT, используя tensor cores, может дать реальное ускорение общей производительности до 6 раз. То есть важно говорить как ускорение для 1 потока в 2 раза за счет fp16 вычислений, так и про увеличение пропускной способности памяти и особенности вычислений в tensor cores. Что же на практике?
Наш опыт такой — при работе в 1 потоке мы получаем ускорение 2х. Однако, если говорить про многопоточную обработку, то ситуация интереснее. Во fp32 на TF мы можем обрабатывать параллельно 2.5 потока видео 30 fps, т.е. суммарно 75 кадров в секунду. При переходе на fp16 в TensorRT производительность вырастает до 8 потоков 30 fps = 240 кадров в секунду. Т.е. фактически при полной утилизации tensor cores мы получили увеличение производительности в 3.2 раза — несколько меньше теоретически максимальной. Скорее всего, это связано с тем, что мы обрабатываем потоки с батчем = 1, чтобы не увеличивать latency прихода данных по каждому кадру. Использование большего батча в инференсе может поднять итоговый буст производительности еще ближе к теоретическим 6х.
UPD2: Q&A к вопросам kremnik
1) Почему не решились писать кастомные слои под trt? Легче было написать свой слой на tf, чем на trt?
Мы использовали YOLO-подобную архитектуру и знали, что Upsample там легко заменяется на Conv2DTranspose. Хотя и известно, что Conv2DTranspose здесь менее предпочтителен из-за свойственных ему краевых эффектов. Мы посчитали это более оптимальным по скорости путём, чем создание кастомных слоёв.
2) Встретились ли проблемы с другими слоями при конвертации из tf в uff? Насколько я понимаю, поддержка uff заканчивается, он мало обновляется и все потихоньку переходят на onnx, который поддерживает гораздо больше слоёв.
Других проблем не было. Нормально заходят и Mish-активации, и Separable2DConv. Но да, раз uff становится deprecated, мы будем юзать onnx.
3) Если был BatchNorm, пробовали ли делать BatchNorm Folding или конвертировали напрямую? Насколько я знаю, у uff проблемы с BatchNorm-слоями.
BatchNormalization слои, конечно, были. Проблем при конвертации не возникло и вроде нигде не натыкался на такую информацию.
4) Картинки при инференсе отдаёте батчами или по одной? Есть ли относительное ускорение на одну картину при батче==1 и батче==N?
5) Использовали ли Triton или используете что-то своё?
Оценка эффективности инференса нейронных сетей
Развитие рынка нейронных сетей подразумевает под собой удешевление стоимости железа при постоянном росте производительности. Обычно нейронная сеть проходит три жизненных этапа: обучение, деплой и инференс. Если про обучение и деплой материала в сети предостаточно, то инференс — нераскрытая тема, в которой стоит разобраться.
Инференс
Инференсом называется непрерывная работа какой-либо нейронной сети на конечном устройстве. Инференс выполняется для совершенно разных нейронных сетей, например — для распознавания марок, моделей транспортных средств, лиц, голосов, анализа текстовых материалов и много другого. То есть, это процесс исполнения сети, когда она уже готова к проведению полезной работы.
Для инференса используются процессоры общего назначения CPU, графические процессоры GPU, некоторые другие вычислительные единицы.
Инференс на CPU
В случае использования CPU, инференс выполняется на логических ядрах процессора, число которых равно числу физических ядер или, при использовании технологии Hyper-threading, увеличено вдвое. Решения на современных многоядерных процессорах достаточно дороги, а использование CPU на больших (глубоких) нейросетях неэффективно из-за ограниченного объема кэша процессора и необходимости организации обмена данными с ОЗУ, что существенно влияет на скорость работы. Кроме того, ограничения на производительность накладываются самой архитектурой — в процессе инференса решаются простые задачи сравнения, которые легко переносятся на параллельные вычисления, но количество параллельных потоков обработки всегда будет ограничено количеством логических ядер CPU.
Инференс на GPU
Оценка производительности инференса
Инференс — процесс несложный, в некоторой степени даже примитивный, однако рост сложности нейронной сети приводит к нелинейному росту необходимых для инференса ресурсов. В результате вполне реально упереться в потолок вычислительной производительности системы или канала передачи данных. Из этой проблемы очевидными видятся два выхода — отправлять данные на обработку к серьезным вычислительным мощностям, если канал передачи данных позволяет, или выполнять его на месте, увеличивая локальную производительность. Но и в первом и во втором случае возникает проблема оценки производительности устройств, осуществляющих инференс. Ведь будет очень странным, если нейросеть работает, но для ее работы требуется система стоимостью много мертвых американских президентов.
Производители устройств всегда нацелены на увеличение продаж. Методики тестирования, которые они предлагают, бывают очень своеобразными, как и выводы которые делаются на базе таких тестов. Например, пытались сравнить количество логических ядер разных устройств, заявляя, что количество ядер имеет решающее значение, хотя понятно, что сравнивать десяток Intel 80486 даже с одним Intel i5 мягко говоря неэтично. Пытались сравнить количество обрабатываемых потоков, гордо заявляя, что решение может обрабатывать 100 каналов, тактично замалчивая, что эти потоки – 240p. Каждый измерял в своих попугаях, и каждый из этих попугаев c точки зрения производителя был более правильный и более производительный.
Очевидно, что для практического применения такие методики непригодны. Мы — компания Combox Technology, как разработчики оборудования, нуждались в сравнительных характеристиках, которые позволяли бы объективно оценивать конечную стоимость эксплуатации системы. Мы попытались привязать эффективность к стоимости — той характеристике, которая прямо связана с клиентом. Было проведено исследование скорости сетей различных топологий на различных видах устройств (процессоры и видеокарты) с привязкой к стоимости. Вы можете ознакомиться с ним по ссылке. Основные метрики, которые нас интересовали — это цена за FPS (frame per second) и скорость (relative FPS).
Результаты получились очень интересные. Именно их мы использовали при разработке наших устройств для инференса. Максимальную скорость инференса при оптимальной стоимости FPS можно получить на одноплатном компьютере (SBC) Intel NUC8i5BEK, поскольку на них в полной мере раскрывается потенциал совместного инферена на CPU и GPU. Именно по этой причине мы использовали это устройство в наших устройствах SBC NUC Server. Для другого устройства OutdoorBox NUC используется Intel NUC4i5MYHE.
Эти решения создавались как раз для того, чтобы решить боль, описанную в начале статьи — максимальная эффективность с максимальной объективностью оценки.
Что же получилось в результате?
OutdoorBox NUC — высокопроизводительное решение для инференса на краю, которое используется при необходимости сократить трафик, решать локальные задачи при минимальной стоимости внедрения и эксплуатации. Устройство выполнено во всепогодном исполнении IP66, обладает низким энергопотреблением (30Вт) и обладает великолепной производительностью при низкой стоимости. Одно устройство способно одновременно эффективно работать с 10 потоками FullHD 15fps.
Для корпоративного сегмента при потребностях в высоких вычислительных мощностях мы разработали SBC NUC Server, которое объединяет в одном корпусе до 8 устройств NUC8i5BEK и обеспечивает обработку до 80 потоков FullHD 15fps.
Очевидно, что такие решения разрабатываются не из-за простого интереса к экспериментам. Мы разрабатываем, обучаем и эксплуатируем нейронные сети, например, для целей распознавания марок и моделей транспортных средств, номерных знаков. Для классификации объектов мы используем нейронную сеть на топологии YOLO (You Only Look Once), которая построена на ее упрощенной оптимизированной модели DarkNet19, использующую 19 слоев, взамен оригинальных 25, и нейронную сеть на топологии UNET для детектирования. Решение используется в коммерческом продукте EDGE с MMR (Make and Model Recognition) и LPR (Licence Plate Recognition), который активно эксплуатируется по всему миру – в национальной полиции Колумбии, Мексики, Белоруссии, Грузии, Болгарии, таможенных терминалах Украины, Азербайджана, системах городского видеонаблюдения Аргентины, Омана, Казахстана. Эффект от внедрения SBC NUC Server был очень существенным и осязаемым – в отличие от GPU-серверов на базе Tesla T4, используемых ранее, стоимостью 1 200 000 рублей, наше решение дешевле на 500 000 рублей, энергопотребление вместо 2 кВт на единицу теперь составляет всего 500 Вт. При этом экономится место в стойках – GPU-сервера обычно имеют формат 2U-4U, в отличие от 1U в нашем случае. Что очень важно – мы получили унифицированное решение на стандартной элементной базе, что позволяет в случае необходимости перепрофилировать устройство под другие задачи, кроме инференса (кластерные решения, web-сервер и пр.)
А потребитель теперь может быть уверен в объективности предлагаемых оценок — эффективность измеряется в деньгах — самых универсальных попугаях.
Variational Inference — что это такое и с чем это едят?
Недавно пообщался с коллегами о вариационном автоэнкодере и выяснилось что многие даже работающие в Deep Learning знают о вариационном выводе (Variational Inference) и в частности Нижней вариационной границе только по наслышке и не до конца понимают что это такое.
В этой статье я хочу подробно разобрать эти вопросы. Кому интересено, прошу под кат — будет очень интересно.
Что такое вариационный вывод?
Семейство вариационных методов машинного обучения получили свое название от раздела математического анализа «Вариационного исчисления». В этом разделе изучаются задачи поиска экстремумов функционалов (функционал это функция от функций — т.е мы ищем не значения переменных в которых функция достигает своего максимума (минимума), а такую функцию при которой функционал достигает максимума (минимума).
Но возникает вопрос — в машинном обучении мы всегда ищем точку в пространстве параметров (переменных) в которых функция потерь имеет минимальное значение. Т.е это задача классического математического анализа, причем здесь Вариационное исчисление? Вариационное исчисление появляется в момент когда мы преобразуем функцию потерь в другую функцию потерь (зачастую это нижняя вариационная граница) используя методы вариацонного исчисления.
Зачем же нам это нужно? Разве нельзя напрямую оптимизировать функцию потерь? Эти методы нам необходимы когда напрямую получить несмещенную оценку градиента невозможно (либо эта оценка обладает очень высокой дисперсией). Например наша модель задает и
, а нам необходимо вычислить
. Это как раз, то для чего разрабатывался вариационный автоэнкодер.
Что такое Нижняя вариационная граница (Variational Lower Bound)?
Т.е у любой функции существует бесчетное множество нижних границ. Все ли эти нижние границы одинаковы? Конечно нет. Введем еще одно понятие — невязка (я не нашел устоявшегося термина в русскоязычной литературе, в англоязычных статьях эту величину называют tightness):
Достаточно очевидно, что невязка всегда положительна. Чем меньше невязка — тем лучше.
Вот пример нижней границы с нулевой невязкой:
А вот пример с небольшой, но положительной невязкой:
И наконец, достаточно большая невязка:
Из приведенных выше графиков отчетливо видно что при нулевой невязке максимум функции и максимум нижней границы находятся в одной точке. Т.е если мы хотим найти максимум некоторой функции, мы можем искать максимум нижней границы. В случае если невязка не нулевая, то это не так. И максимум нижней границы может быть очень далеко (по оси х) от искомого максимума. Из графиков видно что чем больше невязка, тем дальше максимумы могут быть друг от друга. В общем случае это неверно, но в большинстве практических случаев эта интуиция работает очень хорошо.
Вариационный автоэнкодер
Сейчас мы разберем пример очень хорошей нижней вариационной границы с потенциально нулевой невязкой (ниже будет понятно почему) — это Вариационный автоэнкодер (Variational Autoencoder).
Наша задача построить генеративную модель и обучить ее методом максимального правдоподобия. Модель будет иметь следующий вид:
где — плотность вероятности генерируемых семплов,
— латентные переменные,
— плотность вероятности латентной переменной (зачастую некая простая — например многомерное гауссовское распределение с нулевым матожиданием и единичной дисперсией — в общем что-то из чего мы можем легко семплировать),
— условная плотность семплов при заданном значении латентных переменных, в вариационном автоэнкодере выбирается гауссовское с мат ожиданием и дисперсией зависящими от z.
Зачем нам может понадобиться представлять плотность данных в таком сложном виде? Ответ прост — данные имеют очень сложную функцию плотности и мы просто технически не можем построить модель такой плотности напрямую. Мы надеемся что эту сложную плотность можно хорошо аппроксимировать с помощью двух более простых плотностей и
.
Мы хотим максимизировать следующую функцию:
где — плотность вероятности данных. Основная проблема в том, что плотность
(при достаточно гибких моделях) не удается представить аналитически, а соответственно обучать модель.
Используем формулу Байеса и перепишем нашу функцию в следующем виде:
К сожалению, все также сложно посчитать (невозможно взять интеграл аналитически). Но во-первых заметим что выражение под логарифмом не зависит от z, значит можно взять математическое ожидание от логарифма по z от любого распределения и это не изменит значения функции и под знаком логарифма умножить и поделить на это же распределение (формально мы имеем лишь одно условие — данное распределение нигде не должно обращаться в нуль). В итоге мы получим:
заметим что во-первых второе слагаемое — KL дивергенция (а значит всегда положительна):
и во-вторых не зависит ни от
ни от
. Отсюда следует что,
= \int
<\phi(z|x)>)>>=VLB$» data-tex=»display»/>
где — нижняя вариационная граница (Variational Lower Bound) и достигает своего максимума когда
— т.е распределения совпадают.
Положительность и равенство нулю тогда и только тогда когда распределения совпадают KL-дивергенции доказываются именно вариационными методами — отсюда и название вариационная граница.
Хочу заметить, что использование вариационной нижней границы дает несколько преимуществ. Во-первых дает нам возможность оптимизировать функцию потерь градиентными методами (попробуйте сделать это когда интеграл аналитически не берется) и во-вторых приближает обратное распределение распределением
— т.е мы можем не только семплировать данные, но и семплировать латентные переменны. К сожалению, главный недостаток — при недостаточно гибкой модели обратного распределения, т.е когда семейство
не содержит
— невязка будет положительной и равной:
а это означает что максимум нижней границы и функции потерь скорее всего не совпадают. Кстати, вариационный автоэнкодер примененный для генерации картинок генерирует слишком смазанные изображения, думаю это как раз из-за выбора слишком бедного семейства .
Пример не очень хорошей нижней границы
Сейчас мы рассмотрим пример, где с одной стороны нижняя граница обладает всеми хорошими свойствами (при достаточно гибкой модели невязка будет нулевой), но в свою очередь не дает никакого преимущества перед использованием оригинальной функции потерь. Я считаю, что этот пример является очень показательным и если не делать теоретический анализ можно потратить очень много времени пытаясь обучать модели которые не имеют никакого смысла. Вернее модели имеют смысл, но если мы можем обучить такую модель, то уже проще выбирать из этого же семейства и использовать принцип максимального правдоподобия напрямую.
Итак, мы будем рассматривать точно такую же генеративную модель как и в случае вариационного автоэнкодера:
обучать будем все тем же методом максимального правдоподобия:
Мы все так же надеемся, что будет намного «проще» чем
.
Только теперь распишем немного по-другому:
используя формулу Дженсена (Jensen) получим:
=\int
= VLB$» data-tex=»display»/>
Вот именно в этом моменте большинство людей отвечают не задумавшись, что это действительно нижняя граница и можно обучать модель. Это действительно так, но давайте посмотрим на невязку:
откуда (применив формулу Ьайеса два раза):
легко заметить, что:
Давайте посмотрим что будет если мы будем увеличивать нижнюю границу — невязка будет уменьшаться. При достаточно гибкой модели:
казалось бы все отлично — нижняя граница обладает потенциально нулевой невязкой и при достаточно гибкой модели все должно работать. Да это действительно так, только внимательные читатели могут заметить что нулевая невязка достигается когда
и
являются независимыми случайными величинами. и для хорошего результата «сложность» распределения
должна быть ничуть не меньше чем
. Т.е нижняя граница не дает нам никаких преимуществ.
Выводы
Нижняя вариационная граница — отличный математический инструмент, позволяющий приближенно оптимизировать «неудобные» для обучения функции. Но как и любой другой инструмент нужно очень хорошо понимать его преимущества и недостатки а также использовать очень аккуратно. Мы рассмотрели очень хороший пример — вариационный автоэнкодер, а также пример не очень хорошей нижней границы, при этом проблемы этой нижней границы сложно увидеть без детального математического анализа.
Надеюсь это было хоть немного полезно и интересно.