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()
Послесловие
Мы посчитали важным опубликовать эту достаточно подробную инструкцию, чтобы продемонстрировать одну из возможностей платформы на данный момент. Ну а если тема окажется востребованной, продолжим делиться другими материалами по работе с RISC-V.
beho1der
Можно еще взять такую плату на ней есть ethernet для разработки слегка удобнее.
c0mmandor
предположу что MangoPi привлекательна совместимым с RaspberryPi 40-pin разьемом ну и вифи и блюпуп опять же, правда не знал что не заводится, моя плата еще только едет.