Нейросети на RISC-V с Movidius Neural Compute Stick

от автора

RISC-V — перспективная открытая архитектура, не требующая royalty от производителей железа на её основе. Нужно отметить, что интерес к архитектуре RISC-V растёт намного быстрее, чем закрепляется её спецификация и идёт процесс принятия новых фич комитетом, а также дальнейшая реализация в железе и софте. Например, сейчас сложно найти предложение по CPU и совместимое с ним Linux ядро с поддержкой векторизации, хотя RVV 0.7.1 уже существует, и RVV 1.0 вот-вот его заменит. В нашем эксперименте трудоемкие вычисления перекладываются на внешнее устройство, поэтому сгодится и самый простой центральный процессор.

В этой статье вы найдете подробные инструкции по использованию библиотек OpenVINO и OpenCV на RISC-V для запуска нейронных сетей с использованием акселератора. Цель материала — продемонстрировать гибкость решений на примере использования RISC-V CPU в качестве хоста для работы с нейросетевым ускорителем Intel Movidius Neural Compute Stick 2 (NCS2). Большая часть статьи состоит из инструкций по сборке под RISC-V. Конечно, это не самый творческий процесс, но мы верим, что со временем все они спрячутся под процессами CI/CD, как когда-то было с ARM-экосистемой.

Тестовые конфигурации

В данной статье поделимся двумя вариантами запуска готового приложения — через эмулятор QEMU и на плате MangoPi MQ-Pro. На QEMU запустим официальный образ Ubuntu 20.04 для RISC-V, а на плате MangoPi — совместимую Ubuntu 22.04 для AllWinner Nezha (ссылка).

Нужно отметить, что MangoPi не богата на способы взаимодействия: WiFi-антенна, пришедшая в комплекте, не хочет помещаться в соответствующий разъем, официально рекомендуемая Tina Linux не бутится, а ядро Ubuntu для AllWinner Nezha не поддерживает WiFi/Bluetooth. Дополнительно потребуется USB хаб или адаптер с Type-C на USB, чтобы подключить Movidius стик.

Сам NCS2 это со-процессор Myriad-X в форм-факторе USB флешки. Архитектура предлагает набор аппаратных реализаций наиболее вычислительно трудоемких операций для исполнения слоев нейронных сетей (например, сверточных).

Подготовка RISC-V тулчейна

Несмотря на то, что в Ubuntu предоставляется готовый компилятор через apt-get install crossbuild-essential-riscv64, в данной статье хочется показать полный путь сборки проекта из исходного кода, чтобы адаптировать под более широкий выбор окружения.

Сам RISC-V компилятор, а также набор полезных инструментов, хранятся и разрабатываются на GitHub. Самую актуальную инструкцию всегда можно найти на странице проекта, ну а здесь просто зафиксируем.

  • Установка зависимостей:

    sudo apt-get update sudo apt-get install -y --no-install-recommends git autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev
  • Клонирование репозитория:

    git clone https://github.com/riscv-collab/riscv-gnu-toolchain --depth 1
  • Важно: для тестирования через QEMU с Ubuntu 20.04 нужно будет откатить GCC и GLIBC на более ранние версии для совместимости:

    cd riscv-gnu-toolchain git submodule init git submodule update glibc && git submodule update gcc cd glibc && git checkout glibc-2.31 cd gcc && git checkout releases/gcc-9.4.0
  • Сборка:

    ./configure --prefix=/opt/riscv sudo make linux -j$(nproc --all)

Кросс-компиляция OpenVINO

OpenVINO — проект от Intel, объединяющий оптимизированные примитивы для запуска нейронных сетей. Кроме Intel Architecture (CPU, GPU, VPU) проект с недавнего времени развивает плагины для ARM и NVIDIA.

Потребуется дополнительный CMake файл, который пока не находится в главном репозитории. Сохраните его с названием riscv64.toolchain.cmake. Внутри ничего особенного, кроме поиска компилятора и настроек, необходимых для кросс-компиляции.

riscv64.toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR riscv64)  if(NOT DEFINED CMAKE_C_COMPILER)   find_program(CMAKE_C_COMPILER NAMES riscv64-unknown-linux-gnu-gcc                                 PATHS /opt/riscv/bin ENV PATH) endif()  if(NOT DEFINED CMAKE_CXX_COMPILER)   find_program(CMAKE_CXX_COMPILER NAMES riscv64-unknown-linux-gnu-g++                                   PATHS /opt/riscv/bin ENV PATH) endif()  set(CMAKE_SYSROOT /opt/riscv/sysroot CACHE PATH "RISC-V sysroot")  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

  • Клонирование проекта OpenVINO:

    git clone --recurse-submodules -j$(nproc --all) https://github.com/openvinotoolkit/openvino --depth 1
  • При использовании этих инструкций на Ubuntu 22.04 потребуется поставить несколько дополнительных пакетов: libusb под RISC-V, которая используется OpenVINO для обмена данными с Movidius стиком, а также зависимостей, необходимых для Python оберток. Их можно скачать с помощью apt-get, но перед этим добавить новую архитектуру:

    sudo dpkg --add-architecture riscv64

    Затем почему-то возникает ошибка при вызове update, которая решается корректировкой определенного файла. Такая команда дублирует каждую строку для amd64 и riscv64 архитектур:

    sudo sed -i -E 's|^deb ([^ ]+) (.*)$|deb [arch=amd64] \1 \2\ndeb [arch=riscv64] http://ports.ubuntu.com/ubuntu-ports/ \2|' /etc/apt/sources.list sudo apt-get update
  • Установка пакетов:

    sudo apt-get install -y --no-install-recommends \   libusb-1.0-0-dev:riscv64 \   libpython3-dev:riscv64 \   cython3
  • Ну и что-то пойдёт не так с поиском header файлов по относительному пути для кросс-компиляции Python оберток, поэтому можно решить эту проблему грубо:

    sudo sed -i -E 's|riscv64-linux-gnu/python3.10/pyconfig.h|/usr/include/riscv64-linux-gnu/python3.10/pyconfig.h|' /usr/include/python3.10/pyconfig.h
  • Сборка с помощью CMake с указанием расположения библиотеки libusb:

    cmake \   -DCMAKE_TOOLCHAIN_FILE=/path/to/riscv64.toolchain.cmake \   -DLIBUSB_LIBRARY=/usr/lib/riscv64-linux-gnu/libusb-1.0.so \   -DLIBUSB_INCLUDE_DIR=/usr/include/libusb-1.0 \   -DTHREADING=SEQ \   -DENABLE_OPENCV=OFF \   -DENABLE_PYTHON=ON \   -DENABLE_SAMPLES=OFF \   -DPYTHON_INCLUDE_DIR=/usr/include/python3.10 \   -DPYTHON_MODULE_EXTENSION=".so" \   -DPYTHON_LIBRARY=/usr/lib/riscv64-linux-gnu/libpython3.10.so \   -S openvino -B openvino_build       cmake --build openvino_build -j$(nproc --all) cmake --install openvino_build --prefix openvino_install

Кросс-компиляция OpenCV

OpenCV не является обязательной зависимостью OpenVINO, а потребуется нам в приложении для работы над данными. Так же собираем вместе с Python обертками.

Команды для сборки OpenCV
git clone --depth 1 https://github.com/opencv/opencv/  python3 -m pip install numpy==1.21.5  cmake \   -DCMAKE_TOOLCHAIN_FILE=$(realpath opencv/platforms/linux/riscv64-gcc.toolchain.cmake) \   -DCMAKE_INSTALL_PREFIX=$(realpath opencv_install) \   -DCMAKE_BUILD_TYPE=Release \   -DBUILD_LIST=core,imgcodecs,python3 \   -DPYTHON3_NUMPY_INCLUDE_DIRS=$HOME/.local/lib/python3.10/site-packages/numpy/core/include/ \   -DPYTHON3_INCLUDE_PATH=/usr/include/python3.10 \   -DPYTHON3_LIBRARIES=/usr/lib/riscv64-linux-gnu/libpython3.10.so \   -DPYTHON3_EXECUTABLE=/usr/bin/python3.10  \   -DPYTHON3_CVPY_SUFFIX=".cpython-310-riscv64-linux-gnu.so" \   -S opencv -B opencv_build  cmake --build opencv_build -j$(nproc --all) cmake --install opencv_build --prefix opencv_install

Запуск через QEMU

Если вы пока ждёте свою RISC-V борду, но уже заинтересованы в экспериментах, попробуйте вариант с эмулятором QEMU. Сам RISC-V тулчейн тоже умеет собирать бинарник qemu-riscv64, но в него не получилось пробросить USB-устройство, в отличие от следующего способа:

  • Установите QEMU и необходимые зависимости:

    sudo apt-get install qemu-system-misc opensbi u-boot-qemu qemu-utils
  • Скачайте официальный образ Ubuntu:

    wget https://cdimage.ubuntu.com/releases/20.04.2/release/ubuntu-20.04.2-preinstalled-server-riscv64.img.xz  xz -dk ubuntu-20.04.2-preinstalled-server-riscv64.img.xz
  • Установите udev правила на хостовой машине, чтобы можно было работать с NCS. Данный шаг придется повторить и внутри QEMU:

    sudo usermod -a -G users "$(whoami)"  (выполните logout)  sudo cp openvino_install/install_dependencies/97-myriad-usbboot.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger sudo ldconfig
  • Запуск системы. Важным является добавление двух productId, чтобы пробросить USB-устройство внутрь эмулятора: 2485, соответствующий Movidius стику в режиме ожидания и f63b, который присваивается занятому вычислениями стику в процессе работы приложения.

    qemu-system-riscv64 \     -machine virt -nographic -m 2048 -smp 2 \     -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf \     -kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \     -device virtio-net-device,netdev=eth0 -netdev user,id=eth0 \     -usb -device usb-ehci,id=ehci \     -device usb-host,vendorid=0x03e7,productid=0x2485 \     -device usb-host,vendorid=0x03e7,productid=0xf63b \     -drive file=ubuntu-20.04.2-preinstalled-server-riscv64.img,format=raw,if=virtio
  • Повторите установку udev правил на USB стик, но уже внутри QEMU. Возможно, потребуется перезапуск.

  • Эмулятор готов, и на нём можно использовать приложение, описанное ниже.

Запуск на плате

Необходимо переместить собранные OpenVINO и OpenCV на плату MangoPi MQ-Pro с помощью SSH (или скопировать на SD карту). Затем, уже на девайсе, доустановить зависимости. В качестве end2end примера можно сделать Telegram бота для стилизации фотографий.

python3 -m pip install --upgrade pip python3 -m pip install pytelegrambotapi

Готовые файлы модели можно скачать по ссылкам: the_scream_style_transfer.xml и the_scream_style_transfer.bin. Остается выставить переменные окружения и запустить Python скрипт с использованием токена созданного бота (инструкцию можно легко найти).

source opencv_install/bin/setup_vars_opencv4.sh source openvino_install/setupvars.sh export PYTHONPATH=/home/ubuntu/openvino_install/python/python3.10/:$PYTHONPATH  python3 bot.py --token XXX
Код приложения
import os import numpy as np import cv2 as cv import telebot import argparse import io from openvino.runtime import Core  WORK_WIDTH = 640 WORK_HEIGHT = 640  class StyleTransfer:     def __init__(self, xml_path):         core = Core()         model = core.read_model(xml_path)         model = core.compile_model(model, "MYRIAD")         self.ireq = model.create_infer_request()          def process(self, img):         h, w = img.shape[0], img.shape[1]          img = cv.resize(img, (WORK_WIDTH, WORK_HEIGHT))  # resize to fixed input resolution         img = img.reshape(1, WORK_HEIGHT, WORK_WIDTH, 3).transpose(0, 3, 1, 2).astype(np.float32)         img -= np.array([103.939, 116.779, 123.68]).reshape(1, 3, 1, 1)         img /= 255          out = self.ireq.infer([img])         out = next(iter(out.values()))          out += np.array([103.939, 116.779, 123.68]).reshape(1, 3, 1, 1)         out = np.clip(out, 0, 255).astype(np.uint8)          out = out.transpose(0, 2, 3, 1).reshape(WORK_HEIGHT, WORK_WIDTH, 3)         out = cv.resize(out, (w, h))          return out   parser = argparse.ArgumentParser() parser.add_argument('--token', help='Telegram bot token', required=True) args = parser.parse_args()  model = StyleTransfer("the_scream_style_transfer.xml")  bot = telebot.TeleBot(args.token)  def get_image(message):     fileID = message.photo[-1].file_id     file = bot.get_file(fileID)     data = bot.download_file(file.file_path)     buf = np.frombuffer(data, dtype=np.uint8)     return cv.imdecode(buf, cv.IMREAD_COLOR)  def send_image(message, img):     _, buf = cv.imencode(".jpg", img, [cv.IMWRITE_JPEG_QUALITY, 90])     outputbuf = io.BytesIO(buf)     bot.send_photo(message.chat.id, outputbuf)  @bot.message_handler(content_types=['photo']) def process_image(message):      img = get_image(message)     stylized = model.process(img)     send_image(message, stylized)  bot.polling()

Как работает Telegram Bot на MangoPi с Movidius стиком. Время обработки изображения 640x640 — менее 2 секунд.
Как работает Telegram Bot на MangoPi с Movidius стиком. Время обработки изображения 640×640 — менее 2 секунд.

Послесловие

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


ссылка на оригинал статьи https://habr.com/ru/company/yadro/blog/694024/