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 флешки. Архитектура предлагает набор аппаратных реализаций наиболее вычислительно трудоемких операций для исполнения слоев нейронных сетей (например, сверточных).

Кросс-компиляция 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)

set(CMAKE_C_COMPILER riscv64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER riscv64-linux-gnu-g++)

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
  • Установка кросс-компилятора:

    sudo apt-get install -y crossbuild-essential-riscv64
  • При использовании этих инструкций на 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
  • Сборка с помощью 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-gnu.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.

  • Установите 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 стиком. Время обработки изображения 640x640 — менее 2 секунд.

Послесловие

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

Комментарии (2)


  1. beho1der
    27.10.2022 16:38
    +1

    Можно еще взять такую плату на ней есть ethernet для разработки слегка удобнее.


    1. c0mmandor
      28.10.2022 14:56

      предположу что MangoPi привлекательна совместимым с RaspberryPi 40-pin разьемом ну и вифи и блюпуп опять же, правда не знал что не заводится, моя плата еще только едет.