Пояснения к опросам я выложу завтра. А пока поговорим про сетки в проде.
В какой прод вы катите ML/DL модельки?
В основном на курсах по ML/DL речь идет про обучение моделек. MVP делается на слегка подправленной кодовой базе с питоном и фласками.
Моделька создается кодом, в нее подгружаются веса из чекпоинта.
Если проект не умер на первых порах, дальше пойдет эволюция.
Люди прибывают. Модельки усложняются.
Появляется легаси (и это нормально!).
Вы начинаете страдать от обратной совместимости, а она от вас, катить становится сложнее, в коде копятся валенки (скажем другой метод ресайза картинок будет стоить вам процентов acc@1 на imagenet).
Разумным решением будет разделить окружение на тренировочное и продовое, а затем разделить кодовую базу.
Как меньше страдать от синхронизации кода моделек и процессингов в разных окружениях?
Давайте просто договоримся сериализовать вычислительный граф с весами и передавать его!
Если речь идет про классификацию картинок произвольного размера, например договариваемся кидать на вход картинки uint8 [1, 3, ?, ?], ресайз и прочий препроцессинг делать в графе...
моделька пусть возвращает два вектора: с названиями классов и вероятностями top5 ответов.
Пример условный, но смысл в том, чтобы коде инференса было минимальное количество вычислений.
Сложность никуда не исчезает: код для экспорта моделек может стать достаточно интересным, но он лежит рядом с трейновой кодовой базой, выполняется редко, возможно даже в ручном режиме.
А вот код для запуска модели максимально простой и может быть отчужден.
Понятное дело, что в реальности у нас могу быть другие потребности: например обрабатывать картинки батчами. Значит их придется ресайзить во время применения и в проде снова появится код с вычислениями.
Если все вычисления делать в вычислительном графе мы сможем отказаться ...
от питона в проде (и гигабайтов библиотек) и использовать специализированные движки для инференса. Сейчас их стало достаточно много, но основных всего ничего:
- tf/torchscript
- tflite для мобилок
- ONNX - (не только для DL-моделек)
- TensorRT (GPU)
- OpenVINO (Intel CPU)
Про скорость.
Переписать все на плюсах и полететь - это очень распространенная фантазия, но обязательно попробуйте!
Так-то питон не сильно замедляет применение моделек по сравнению с С++ API (если вычисления сложнее перемножения пары матричек).
Когда код запуска отлажен и запрофилирован, а скорости мало, приходится делать что-то с модельками.
Обычно говорят про дистилляцию, квантование и прунинг.
Дистилляция модельки - это обучение маленькой модельки на выходах большой.
Может дать больше качества, чем учить маленькую модель на том же датасете.
В чем поинт:
- большая модель может сгладить ошибки и неполноту разметки
- можно учиться на тонне неразмеченных данных
- можно сменить тип модельки авторегрессионную в простую feed-forward
Квантование - классная штука. Нам нужны float point-числа чтобы работал градиентный спуск, часто при переходе к fp16 возникают проблемы со сходимостью обучения.
Но во время инференса точность не особо нужна.
Сейчас популярно квантование в int8, после тренировки:
через сетку прогоняется калибровочный набор данных, для каждого слоя выбирается оптимальный zero-point и scale. Вместо одной fp-операции с fp-входами-выходами получается три операции: quantizing -> q_op -> dequantizing.
Подряд идущие вспомогательные операции склеиваются и в идеале весь граф состоит из квантованных операций.
Подводные камни простые:
- не все получается конвертировать (и выигрыш в одном месте теряется на лишних конвертациях)
- портится качество
Прунинг - зануление весов. Обычно просто по гистограмме значений зануляются маленькие по модулю числа.
Из неочевидного: чтобы неупорядоченный прунинг дал прирост скорости нужны спарсные операции и занулять значительную долю весов.
Так что для скорости нужно делать структурный прунинг: выбрасывать целые каналы и слои.
На прунинг можно смотреть как на регуляризацию, в такой постановке он тесно связан с гипотезой счастливых билетов, но это уже не про оптимизацию инференса)
В добавок к этим трем понятным вариантам есть еще один чуть более глубокий: инференсный граф может отличаться от тренировочного.
Когда речь идет про картиночные сетки мы так часто и делаем не задумываясь: BN в валидационном режиме превращается в простое аффинное преобразование..
Красивую идею предлагают в недавней статье RepVGG: давайте учить ResNet, а инферить VGG.
Пояснение: в резнетах хорошо текут градиенты, это позволяет тренить 100+ слойные слойки.
VGG в свою очередь добры к потреблению памяти:...
отсутствие срезок (skip-connections) позволяет тратить сильно меньше памяти, но тренировать глубокие VGG-like сети тяжело (градиенты и сигналы затухают).
Предлагается сделать математический трюк: изменить ResBlock так чтобы его можно было конвертировать в Conv + Activation с идентичными ответами.
Для этого делаем ResBlock тощим:
- три ветки вычислений
- Identity + BN
- Conv 1x1 + BN
- Conv 3x3 + BN
- ReLU после суммирования
Конвертация в VGG-блок делается так:
- распячиваем Identity и Conv 1x1 в Conv 3x3 (ядра с нулями).
- BN-eval заносим в свертку (ядра умножаем на мультипликативную поправку, аддитивную в bias)
- складываем ядра -> получается одна свертка 3x3
- ...
- ответы сети идентичны исходным!
Так-то подобные работы делали и раньше, привожу эту как наиболее наглядную.
Статья в целом наталкивает на мысль что мы не очень-то хорошо умеем инициализировать и оптимизировать модельки.
Ссылочка на всякий случай: arxiv.org/abs/2101.03697
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Что стоит знать в фреймворке (для определенности pytorch):
- как строится вычислительный граф (у тензоров есть backward-функция, за которую можно дернуть для бекпропа)
- как представлять данные (условно складываем картинки в тензора [bs, channels, height, width])
- как вычисляется лосс (давайте опросом, что должно быть у сети для многоклассовой классификации в голове?)
Пояснения на всякий случай:
- в доку не подглядывать!
- FC=Linear (иначе не лезло)
- если не понимаете о чем речь => reply с вопросом
- как управлять оптимизатором (обход весов, lr scheduling, grad_clip..)
- как мерить скорость и утилизацию железа (tqdm, watch nvidia-smi, profiler..)
- как дебажить наны (forward_hook, backward_hook)
- как сериализовать модель (torch.save, jit.trace, jit.script)
Про инструменты.
Я в основном пишу код на питоне, эпизодически на C++, иногда JS/bash и на чем еще придется по задаче.
Все хоббийное и учебное я пишу на Pytorch, почти все рабочее на TF.
Самый богатый источник граблей - рабочие инструменты.
Очень часто наблюдаю как люди тонну времени тратят на войну с инструментами, а не задачей.
Распространенная история - человек что-то делал, получил результаты, но им нельзя доверять => работу надо переделывать.
Имхо про фреймворки:
- конкуренция это здорово, tf был удобнее theano, pytorch удобнее tf, tf2 удобнее tf1
- не важной какой фреймворк вы используете, кодовую базу и математику сетей нужно заботать, иначе в ваших результатах буду валенки
- ....
На первом курсе универа на колабе МИСиС, MIT и Сколково подружились с Олегом Уржумцевым. Нас преподы из MIT учили делать игрушки на 3D принтерах и лазерных резаках.
Чтобы попасть в эту программу надо было знать английский и уметь в сборку электроники. В этом-то я был хорош.
Потом мне удалось с ними пообщаться вживую (помогли старые EPAM-вские связи)
Оказалось они только стартуют и челленджей непочатый край. Я начал работу над OpenSource проектом с баг-фикса в алгоритме перемножения распределённых блочных матриц и понеслось. 102 коммита, 300k+ строк кода, 15 моделей, PMC проекта.
Чем больше я пилил распределенный ML, читал статьи, писал свои, изучал код scikit-learn/Spark/dlib/tensorflow, тем больше крепло ощущение, что это мое. В какой-то момент случилась магия:
DEThread: Перебравшись в СПб я уже вплотную занялся прокачкой навыков DE, пытаясь устроиться чистым DS. На чистого DS брали только на мало денег, поэтому я раз за разом выбирал DE проекты (presale/прототипы) и рос очень быстро, играя роль FullStackBigData
Одновременно пошел просто огромный поток джавистов, дотнетчиков, database administrators, бегущих из старых рынков в новый сияющий мир "BigData: Hadoop, Hive, Spark". В EPAM мы открыли менторинг (учеба с куратором) по BigData, через который прошло 300+ человек в 16-18 годах
Мне повезло работать с очень крутыми архитекторами и большим количеством досконально изучающих фреймворки людей. Кишки Hadoop - это нечто. Именно тогда и началось мое погружение в недра и уход в библиотекописательство. Ты глядишь - и понимаешь, что можешь говнякать не хуже. 100%
Занимаясь машинным обучением в России, редкий человек избежал искушения обучиться базовым навыкам посредством знаменитого курса от Воронцова. Многие пытались использовать его как первый и единственный доступный материал и убегали в страхе, раздавленные уже первой лекцией.
Те из многих, кто имел достаточную математическую подготовку и небольшой практический опыт боготворили лекции как единственное верное учение и подход к подаче материала.