Как стать автором
Обновить

Комментарии 16

Зашел почитать про кофе, а тут про какие-то нейронки :)

Спасибо за статью. Если не брать в расчет мобильные платформы, то я бы OpenCV и ONNX поставил в один ряд, т.к. OpenCV поддерживает ONNX сетки, как подмножество.
ONNX это лишь формат записи сети. ONNX может исполняться и через OpenCV и через OpenVino и через TensorRT. Сконвертировать из ONNX в любой другой формат — обычно не сложно.

ONNX runtime собственно и инферит на оптимальном оборудовании (то что там используется базовый формат ONNX опять же не проблема). Там автоматически подключен и TensorRT и OpenVino и другие ускорители. В этом плане — он более всеобъемлющ чем чистый OpenCV. Так как в OpenCV нет и RockChip и AMD-шного инференса, и других приблуд именно для инференса.
Но мне OpenCV ближе — так как я его понимаю лучше.
Сконвертировать из ONNX в любой другой формат — обычно не сложно.

До тех пор, пока это сети, состоящие из тривиальных слоев)


В первом приближении, в кейсах, выходящих за рамки стандартных ONNX опсетов все становится совсем плохо в плане совместимости. Например, если хочется использовать поквантованные сети, то там и на уровне формата слоев не будет совместимости, ни на уровне соответствующих ядер для инференса. Та же проблема со специфическими топологиями, типа всевозможных детекторов (например что-то вроде Faster-RCNN), когда какой-нибудь ROIPooling в каждом фреймворке реализован по своему и привязан к конкретному train pipeline etc.


А дальше начинается специфика гнрафовых оптимизаций: если у вас что-то смогло сконвертироваться из ONNX в конкретный фреймворк, то не факт, что будет работать быстро. Например, в трансформерах часто используется gelu активация, которая может быть реализована многими разными способами (torch.nn.gelu, куча вариантов собрать ее из разных примитивов и тд и тп), и если фреймворк не умеет парсить паттерн gelu, используемый в конкретной имплементации трансформера, то все может быть печально.


ONNX, конечно, сильно упрощает жизнь, но вендор-специфичных штук в нем оочень много.

«У мене внутре… гм… не… нейронка»
Отличная статья, спасибо!
Есть какие-то рекомендации по конвертации нейросетей с кастомными слоями? Ну, кроме короткого ответа «это больно». В частности вопрос про ковертацию детекторов, в которых есть NMS или Deformable Convolution.
Артур, привет! Спасибо!
Это очень от фреймворка будет зависеть все же. И тут каждый раз надо будет разбираться заново. Для того же OpenVino (а значит всего того что на его базе) большинство известных слоёв конвертируются без проблем. Например вот или вот.
Так же у них есть большой набор готовых моделей, которые автоматически конвертируются.
Мне кажется, что это обусловлено тем, что в OpenVino не нужно лишний раз данные гонять в память видюхи и назад => они просто берут процессорную реализацию слоя и в целом она работает из коробки. NMS, Deformable Convolution, например, есть. Но будет ли это у них работать и на проце и на их GPU — не знаю. Вроде SSD мы как-то запускали на IntelNuc с GPU и там работало, значит NMS по крайней мере был.

Если мы перейдём к какому-нибудь TensorRT, то там все ещё более-менее норм, но многое уже не из коробки. Но для того кто куду и c++ знает это вроде как не сложно. Я сам весьма поверхностно на ней умею писать. Коллега который хорошо разбирается — OpenPose'овскую обработку поз года три назад где-то за неделю перенёс на куду. Сетка инферелась на TensorRT, а потом напрямую кудой данные обрабатывались.
Формальный список слоёв там достаточно уныл. Но для большинства распространённых есть уже готовые «плагины». Например NMS, или гайд по добавлению Deformable Convolution.
Но с тем же tensorRT существует такая штука как tf-trt слои которые можно он исполняет на TensorRT, а то для чего реализации нет — на tensorflow. В целом, какой-нибудь SSD на JetsonNano давал усадку процентов на 20, если его через TfTRT было делать, но работал из коробки.

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

А вот чем дальше — тем будет хуже. Однажды мы переносили какую-то сетку на SnapDragon-овский сопроцессор для нейронок (ещё до того как TFlite был адекватен), и там не было даже слоя ADD. При этом его нельзя было добавить.
И каждый раз для каждого фреймворка/каждой железки приходилось заново разбираться, если что-то редкое есть.

В целом, обычно логика такая:
  1. Иногда слой можно выполнить на основном процессоре. Особенно это просто сделать если есть разделяемая память для процессора и GPU|TPU модуля
  2. Иногда готовые слои можно найти в интернете, может кто-то их уже написал
  3. Иногда можно подменить слой на что-нибудь ещё. Чаще всего это для ReLu работает хорошо
  4. Иногда можно что-нибудь подтьюнить в сетке и дообучить её уже в более простой конфигурации, где нет плохого слоя, но работает не всегда


Но, реально, из того что я перечислил в статье — у большинства фреймворков я не знаю как слой добавить:) Для тех же TensorFlow.JS или ONNX.JS. Так что однажды когда я попал в ситуацию что что-то не сконвертилось, просто взял другую сетку ¯\_(ツ)_/¯
Спасибо, крутой гайд!
Добавлю дата поинт насчет onnx-runtime: с простыми сетями из коробки завелось на серверном CPU и на CUDA, все хорошо; быстрее, чем traced модель из Pytorch, а процедура выкатывания в прод аналогичная, все очень просто.
Ого. Прикольно. А используете через с++ или через python?
Python
На неё даже Yolov4 спортировано. И в целом, AlexeyAB очень позитивно отзывался.


Да, Tencent-NCNN хороша тем, что в нее конвертятся модели из ONNX и запускать можно везде где есть Vulkan. В TFlite есть много проблем при конвертации из ONNX. Но нативные-TensorFlow модели работают на TFlite быстрее, чем на NCNN.

YOLOv4 где только нету, на TensorFlow / TensorFlow-Lite устанавливается просто pip install yolov4 и поддерживает обучение, инференс и любые конвертации.

— yolov4-tiny на TensorFlow-Lite где-то в 1.25 раза быстрее, чем на NCNN на CPU RaspberryPi4
— TensorFlow-Lite (2 threads) где-то в 2 раза быстрее, чем Pytorch-mobile на CPU iPhone 11.
— TensorFlow-Lite умеет ускорять на GPU в 2 раза и на NPU в 4 раза на iPhone11, Pytorch-mobile пока этого не умеет.
Но это конечно сильно зависит от модели, есть которые совсем плохо ускоряются на GPU/NPU.
— в Pytorch-е есть Eager-mode-квантование только в бетте, а Graph-mode-квантование пока вообще не реализовано.
— а в TensorFlow-lite не все модели из TF могут быть сконвертированы в TFlite, например, в TFlite не поддерживаются Grouped-Conv — известная воспроизводимая проблема: github.com/tensorflow/tensorflow/issues/40044 и приходится городить массив из кучи conv-слоев и итоговая модель очень медленная. Но DepthWise-conv (частный случай GroupedConv когда input_channels==groups) поддерживается в TFlite.

Я пока что выбрал TFLite для инференса на мобильных устройствах.
Но проблема в том, что большинство разработчиков обучают модель на Pytorch, а хотят inference на TFlite и есть проблемы в конвертации из Pytorch в TFLite. Есть 3 пути конвертации:

1. Pytorch->ONNX->TensorFlow(pb)->TensorFlowLite(tflite) но в этом случае наши полученные модели основанные на обычном resnet50
— (iPhone11) либо вообще не запускаются на GPU/NPU
— (SnapDragon 865) либо работают на GPU/NNAPI даже медленнее, чем на CPU

2. Pytorch->ONNX->OpenVINO->TensorFlow(pb) / TensorFlow-lite После того, как 1-й путь оказался плохим, решено было сделать конвертер OpenVINO-> TensorFlow-NHWC(tflite/tfjs/tf-trt/tf-edge) github.com/PINTO0309/openvino2tensorflow полученные модели на скорость на смартфонах ещё не тестировал, но на PC работает: github.com/PINTO0309/PINTO_model_zoo/issues/15#issuecomment-714441709

3. Pytorch->TensorFlow требует использовать идентичные нативные модели в обоих фреймворках и конвертировать только веса. Полученная модель очевидно будет самая быстрая и без мусора, который добавляют конвертеры и этот мусор видимо не поддерживается GPU/NPU. Я реализовал и использую этот метод.

В итоге удалось ускорить модель на смартфоне в 100 раз на GPU/NPU (на Snapdragon 865 GPU было 0.2 FPS, стало 22 FPS), при сохранении точности:
— в 10х раз ускорить за счет использования моего конвертера (3) вместо (1)
— в 10х раз ускорить за счет оптимизации/изменения модели и параметров обучения
Монументально!
Вообще вопрос конвертации я даже не пробовал затрагивать, там всегда какой-то ад творится. И на эту тему очень мало информации опубликовано, особенно по потерям производительности при конвертации.

Любопытно. А не проще ли было сделать обучение сразу на TensorFlow? Мне в таких ситуациях кажется, что перенос обучения зачастую проще чем перенос модели…
Много разработчиков знают только Pytorch и много проектов, кода и кастомных слоев написано на Pytorch. Поэтому исследования проще проводить на Pytorch.
Поэтому пока что оптимальный вариант — это реализовать только inference-часть на TensorFlow, обучать на Pytorch и переносить только веса из Pytorch в TensorFlow.
А когда Facebook в Pytorch доделает: Graph-mode-quantization, Pytorch-mobile, Pytorch-TRT и поддержку Intel GPU/NPU, то можно будет не выходить за пределы Pytorch.

И свою статью про то как устроены embedding системы в последнее время.
И то и то уже немного неактуально, ведь всё быстро-быстро меняется;)

Я бы даже сказал: DeepLearning меняется непредсказуемо, где-то очень быстро, а где-то очень медленно:

— Например ResNext вышла в 2016 году arxiv.org/abs/1611.05431 а в Keras Grouped-Convolutional, которые используются в ResNext были добавлены только в начале 2020 года github.com/tensorflow/tensorflow/pull/36773 при том, что ResNext в 2019 года была топ4 по точности на Imagenet, и до сих пор в 2020 году в топ10: paperswithcode.com/sota/image-classification-on-imagenet

— В Pytorch в 2020 году до сих пор нет Padding=SAME: github.com/pytorch/pytorch/issues/3867 из-за этого приходится создавать костыли github.com/rwightman/gen-efficientnet-pytorch/blob/master/geffnet/conv2d_layers.py которые потом косвенно мешают конвертации и квантизации. Не релизнута обычная int8 квантизация: в eager только в бете, а в graph вообще её нет и т.д.
AlexeyAB спасибо за коммент! особенно интересно для RPi4!

Q1. а этот третий конвертер «3. Pytorch->TensorFlow» есть в открытом доступе?

Q2. можете ли порекомендовать ссылки на примеры предобученных TF-Lite моделек (EfficientDet, YoloV4, MobileNet, ResNet) для object detection, показывающих на Rpi4 FPS >= 10?
1. Он приватный.

2. Много TFlite маленьких моделей для детекторов у Katsuya Hyodo PINTO0309. Но какие из них 10 FPS на RpI4 уже не помню (смотрите quantized). Чтобы скачать в 3-й колонке Link на 3 синих квадратика жмите: github.com/PINTO0309/PINTO_model_zoo#2-2d-object-detection

А почему именно RPi4 хочется использовать?
Rpi4 — 5 — 10тр
За те же 14тр можно уже JetsonNano купить.
Или смартфон использовать, там уже встроенные GPU/DSP/NPU: nplus1.ru/news/2020/08/27/openbot
Xiaomi mi6 GPU (TFlite) за 14тр примерно такой же по скорости как JetsonNano GPU (TensorRT) за 14тр. И раз в 5 быстрее, чем RPi4.
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий