1. Проверяем окружение CVAT перед подключением модели

Перед развёртыванием модели необходимо проверить, как установлен CVAT, какая версия платформы используется и доступен ли на сервере Docker Compose.

В self-hosted CVAT модели для автоматической разметки могут запускаться как отдельные serverless-функции под управлением Nuclio. CVAT передаёт функции изображение, получает от неё координаты объектов и добавляет полученные аннотации в задачу.

Схема взаимодействия выглядит следующим образом:

Разметчик

Интерфейс CVAT

CVAT Server / Worker

Nuclio

Контейнер с моделью

Предсказания модели

Аннотации в задаче CVAT

 

Подключение к серверу

Подключитесь по SSH к серверу, на котором работает CVAT:

ssh <пользователь>@<адрес-сервера>

Например:

ssh root@192.168.1.50

Если CVAT установлен не от пользователя root, после подключения перейдите к пользователю, который управляет Docker-контейнерами.

Находим каталог с CVAT

Если путь к CVAT известен, сразу перейдите в него:

cd /opt/cvat

Вместо /opt/cvat укажите фактический путь к репозиторию.

Проверить текущий каталог можно командой:

pwd

В корневом каталоге CVAT должны находиться как минимум следующие файлы и директории:

ls -la

Ожидаемые элементы:

docker-compose.yml

components/

serverless/

cvat/

Если путь к установке неизвестен, попробуйте найти docker-compose.yml:

sudo find /opt /srv /home /root \

    -maxdepth 4 \

    -type f \

    -name docker-compose.yml \

    2>/dev/null

После этого перейдите в найденный каталог:

cd <путь-к-CVAT>

Проверяем версию CVAT

Если CVAT был установлен из Git-репозитория, зафиксируем ветку, тег и текущий commit:

git remote -v

git branch --show-current

git describe --tags --always --dirty

git rev-parse --short HEAD

Дополнительно запросим информацию через API работающего CVAT:

curl -ksS https://%yor_domain%.com/api/server/about | python3 -m json.tool

В ответе должны появиться сведения о серверной версии CVAT.

Если команда python3 недоступна, выполните запрос без форматирования:

curl -ksS https:// ://%yor_domain%.com/api/server/about

Проверяем Docker и Docker Compose

Выведем установленные версии:

docker --version

docker compose version

Проверим состояние контейнеров CVAT:

docker compose ps

Если установка запускалась с нестандартным именем Compose-проекта или команда не показывает контейнеры, используйте:

docker ps \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' \

    | grep -Ei 'NAME|cvat|traefik|nuclio'

Основные контейнеры CVAT должны иметь состояние Up или healthy. Названия сервисов могут отличаться в зависимости от версии, но обычно среди них присутствуют:

cvat_server

cvat_ui

cvat_db

cvat_redis_inmem

cvat_redis_ondisk

cvat_worker_*

traefik

Получить список сервисов, описанных в основной конфигурации, можно командой:

docker compose config --services | sort

Проверяем поддержку serverless-функций

В современных версиях CVAT конфигурация Nuclio находится в файле:

components/serverless/docker-compose.serverless.yml

Проверим наличие файла:

test -f components/serverless/docker-compose.serverless.yml \

    && echo "Serverless Compose file: OK" \

    || echo "Serverless Compose file: NOT FOUND"

Проверим наличие скриптов развёртывания моделей:

test -f serverless/deploy_cpu.sh \

    && echo "CPU deployment script: OK" \

    || echo "CPU deployment script: NOT FOUND"

 

test -f serverless/deploy_gpu.sh \

    && echo "GPU deployment script: OK" \

    || echo "GPU deployment script: NOT FOUND"

На этом этапе контейнер Nuclio может ещё не быть запущен. Проверим его наличие:

docker ps \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' \

    | grep -Ei 'NAME|nuclio'

Также проверим, установлен ли CLI-инструмент nuctl:

if command -v nuctl >/dev/null 2>&1; then

    nuctl version

else

    echo "nuctl пока не установлен"

fi

Версия nuctl должна соответствовать версии Nuclio, указанной в components/serverless/docker-compose.serverless.yml. Официальная документация CVAT отдельно предупреждает, что несовпадение версий клиента и платформы может привести к ошибкам при развёртывании функций.

Посмотреть используемый образ Nuclio можно командой:

grep -nE 'image:.*nuclio|NUCLIO_VERSION' \

    components/serverless/docker-compose.serverless.yml

Проверяем ресурсы сервера

Выведем информацию о процессоре:

lscpu | grep -E \

    'Model name|Socket|Core|Thread|CPU\(s\)'

Проверим объём оперативной памяти:

free -h

Проверим свободное место:

df -h /

docker system df

Для первоначальной проверки интеграции модель можно запустить на CPU. Для обработки большого массива изображений или использования тяжёлых нейросетей желательно наличие GPU.

Проверить наличие NVIDIA GPU можно командой:

nvidia-smi

Если выводится таблица с названием видеокарты, версией драйвера и объёмом памяти, сервер готов к дальнейшей проверке GPU-контейнеров.

Если отображается ошибка:

nvidia-smi: command not found

или:

No devices were found

на сервере отсутствует доступный NVIDIA GPU либо не установлен драйвер.

Сохраняем диагностику в файл

Чтобы сохранить исходную конфигурацию установки, выполним единый диагностический блок из корневого каталога CVAT:

{

    echo "===== DATE ====="

    date -Is

 

    echo

    echo "===== HOST ====="

    hostname

    uname -a

 

    echo

    echo "===== CVAT DIRECTORY ====="

    pwd

 

    echo

    echo "===== CVAT GIT ====="

    git branch --show-current 2>/dev/null || true

    git describe --tags --always --dirty 2>/dev/null || true

    git rev-parse --short HEAD 2>/dev/null || true

 

    echo

    echo "===== DOCKER ====="

    docker --version

    docker compose version

 

    echo

    echo "===== CVAT CONTAINERS ====="

    docker ps \

        --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' \

        | grep -Ei 'NAME|cvat|traefik|nuclio'

 

    echo

    echo "===== SERVERLESS FILES ====="

    test -f components/serverless/docker-compose.serverless.yml \

        && echo "docker-compose.serverless.yml: OK" \

        || echo "docker-compose.serverless.yml: NOT FOUND"

 

    test -f serverless/deploy_cpu.sh \

        && echo "deploy_cpu.sh: OK" \

        || echo "deploy_cpu.sh: NOT FOUND"

 

    test -f serverless/deploy_gpu.sh \

        && echo "deploy_gpu.sh: OK" \

        || echo "deploy_gpu.sh: NOT FOUND"

 

    echo

    echo "===== NUCTL ====="

    if command -v nuctl >/dev/null 2>&1; then

        nuctl version

    else

        echo "nuctl: NOT INSTALLED"

    fi

 

    echo

    echo "===== MEMORY ====="

    free -h

 

    echo

    echo "===== DISK ====="

    df -h /

 

    echo

    echo "===== GPU ====="

    if command -v nvidia-smi >/dev/null 2>&1; then

        nvidia-smi \

            --query-gpu=name,driver_version,memory.total \

            --format=csv,noheader

    else

        echo "NVIDIA GPU: NOT DETECTED"

    fi

} | tee ~/cvat-preannotation-audit.txt

Результат будет сохранён в файле:

~/cvat-preannotation-audit.txt

На этом этапе мы только собираем информацию и ничего не меняем в работающей конфигурации CVAT.

 

Проверка версии, контейнеров и компонентов автоматической аннотации на сервере CVAT.

2. Устанавливаем и подключаем Nuclio к CVAT

Для запуска моделей автоматической разметки self-hosted CVAT использует serverless-функции Nuclio. Модель разворачивается в отдельном Docker-контейнере и становится доступна CVAT через HTTP-интерфейс Nuclio.

Интеграция состоит из трёх частей:

  1. Nuclio Dashboard — сервис, управляющий функциями.
  2. nuctl — консольная утилита для сборки, развёртывания и удаления функций.
  3. Контейнеры моделей — отдельные Docker-контейнеры, выполняющие инференс.

В актуальных версиях CVAT инфраструктура Nuclio запускается с помощью дополнительного Compose-файла components/serverless/docker-compose.serverless.yml. После запуска serverless-компонентов модели необходимо разворачивать отдельно.

2.1. Переходим в каталог CVAT

Подключитесь к серверу по SSH и перейдите в каталог, из которого запускается CVAT:

ssh <пользователь>@<адрес-сервера>

cd /opt/cvat

Путь /opt/cvat приведён как пример. Проверить, что выбран правильный каталог, можно командой:

pwd

ls -la

В нём должен находиться основной файл:

docker-compose.yml

2.2. Находим serverless-конфигурацию

Расположение файла зависит от версии и структуры репозитория CVAT. В новых версиях используется путь:

components/serverless/docker-compose.serverless.yml

В старых или модифицированных установках файл может находиться в другом каталоге.

Выполним автоматический поиск:

find . \

    -maxdepth 4 \

    -type f \

    -name '*serverless*.yml' \

    -o \

    -name '*serverless*.yaml'

Для дальнейших команд сохраним найденный путь в переменную:

if [ -f components/serverless/docker-compose.serverless.yml ]; then

    SERVERLESS_COMPOSE="components/serverless/docker-compose.serverless.yml"

 

elif [ -f docker-compose.serverless.yml ]; then

    SERVERLESS_COMPOSE="docker-compose.serverless.yml"

 

elif [ -f serverless/docker-compose.serverless.yml ]; then

    SERVERLESS_COMPOSE="serverless/docker-compose.serverless.yml"

 

else

    echo "ОШИБКА: serverless Compose-файл не найден"

    exit 1

fi

 

echo "Serverless Compose: $SERVERLESS_COMPOSE"

Ожидаемый результат:

Serverless Compose: components/serverless/docker-compose.serverless.yml

Переменная действует только в текущей SSH-сессии. После повторного подключения её потребуется задать заново.

2.3. Проверяем содержимое конфигурации

Посмотрим, какие сервисы добавляет serverless-конфигурация:

docker compose \

    -f docker-compose.yml \

    -f "$SERVERLESS_COMPOSE" \

    config --services

В списке должен присутствовать сервис Nuclio, например:

nuclio

Проверим, какой образ Nuclio будет запущен:

docker compose \

    -f docker-compose.yml \

    -f "$SERVERLESS_COMPOSE" \

    config \

    | grep -E -A 3 -B 3 'nuclio/dashboard'

Версия Nuclio понадобится позднее для установки совместимой версии nuctl.

2.4. Запускаем CVAT вместе с Nuclio

Если CVAT использует только основной Compose-файл, выполните:

docker compose \

    -f docker-compose.yml \

    -f "$SERVERLESS_COMPOSE" \

    up -d

Команда не создаёт CVAT заново. Docker Compose сравнит текущую конфигурацию с новой, запустит отсутствующие serverless-сервисы и при необходимости обновит связанные контейнеры.

Именно такой способ включения Nuclio указан для стандартного self-hosted развёртывания CVAT.

Если CVAT запущен с HTTPS-конфигурацией

При наличии дополнительного файла HTTPS необходимо сохранить его в команде запуска:

docker compose \

    -f docker-compose.yml \

    -f docker-compose.https.yml \

    -f "$SERVERLESS_COMPOSE" \

    up -d

Если используются другие Compose-файлы

В serverless-команду нужно перенести все файлы, которые используются при обычном запуске CVAT.

Например, если CVAT обычно запускается так:

docker compose \

    -f docker-compose.yml \

    -f docker-compose.override.yml \

    -f docker-compose.prod.yml \

    up -d

то Nuclio следует подключить так:

docker compose \

    -f docker-compose.yml \

    -f docker-compose.override.yml \

    -f docker-compose.prod.yml \

    -f "$SERVERLESS_COMPOSE" \

    up -d

Serverless-файл добавляется последним, чтобы его настройки дополнили основную конфигурацию.

2.5. Проверяем контейнер Nuclio

Выведем состояние контейнеров:

docker ps \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' \

    | grep -Ei 'NAME|nuclio|cvat'

Ожидаемый результат:

NAMES       IMAGE                                      STATUS

nuclio      quay.io/nuclio/dashboard:<версия>-amd64    Up ... (healthy)

cvat_server cvat/server:<версия>                       Up ...

cvat_ui     cvat/ui:<версия>                           Up ...

Контейнер nuclio должен иметь состояние:

Up

или:

Up ... (healthy)

Посмотреть последние сообщения Nuclio можно командой:

docker logs --tail 100 nuclio

Проверим доступность панели управления с самого сервера:

curl -sS -o /dev/null \

    -w 'HTTP status: %{http_code}\n' \

    http://127.0.0.1:8070

Допустимый результат:

HTTP status: 200

В некоторых версиях сервер может вернуть код перенаправления:

HTTP status: 302

Это также означает, что HTTP-сервис Nuclio отвечает.

Порт Nuclio Dashboard не рекомендуется открывать напрямую в интернет. Панель должна быть доступна только администратору сервера или через SSH-туннель.

2.6. Проверяем активацию serverless-функций в CVAT

Serverless-конфигурация должна передать серверу CVAT соответствующий параметр окружения.

Проверим его:

docker inspect cvat_server \

    --format '{{range .Config.Env}}{{println .}}{{end}}' \

    | grep -E '^CVAT_SERVERLESS='

Ожидаемый результат:

CVAT_SERVERLESS=1

Если переменная отсутствует, сначала проверьте итоговую Compose-конфигурацию:

docker compose \

    -f docker-compose.yml \

    -f "$SERVERLESS_COMPOSE" \

    config \

    | grep -n -A 5 -B 5 'CVAT_SERVERLESS'

Затем пересоздайте сервер и связанные worker-контейнеры:

docker compose \

    -f docker-compose.yml \

    -f "$SERVERLESS_COMPOSE" \

    up -d --force-recreate

Использовать --force-recreate следует только в том случае, если обычный запуск не передал настройки serverless-компонента.

2.7. Определяем версию Nuclio

Узнаем полный образ запущенного контейнера:

NUCLIO_IMAGE=$(

    docker inspect nuclio \

        --format '{{.Config.Image}}'

)

 

echo "$NUCLIO_IMAGE"

Пример:

quay.io/nuclio/dashboard:1.13.0-amd64

Извлечём номер версии:

NUCLIO_VERSION=$(

    echo "$NUCLIO_IMAGE" \

        | sed -E 's#.*:([0-9]+\.[0-9]+\.[0-9]+)(-[a-z0-9]+)?$#\1#'

)

 

echo "Nuclio version: $NUCLIO_VERSION"

Пример результата:

Nuclio version: 1.13.0

Если вместо номера выводится вся строка образа, посмотрите её вручную:

docker inspect nuclio \

    --format '{{.Config.Image}}'

Версия находится после двоеточия:

quay.io/nuclio/dashboard:1.13.0-amd64

                         └────────┘

                            версия

2.8. Устанавливаем утилиту nuctl

Для развёртывания функций потребуется CLI-утилита nuctl.

Версия nuctl должна совпадать с версией Nuclio Dashboard. Это требование отдельно отмечено в официальной документации CVAT.

Сначала определим архитектуру сервера:

uname -m

Автоматически преобразуем её в название, используемое в релизах Nuclio:

case "$(uname -m)" in

    x86_64)

        NUCTL_ARCH="amd64"

        ;;

 

    aarch64|arm64)

        NUCTL_ARCH="arm64"

        ;;

 

    *)

        echo "Неподдерживаемая архитектура: $(uname -m)"

        exit 1

        ;;

esac

 

echo "nuctl architecture: $NUCTL_ARCH"

Скачиваем nuctl той же версии, что и Nuclio Dashboard:

curl -fL \

    "https://github.com/nuclio/nuclio/releases/download/${NUCLIO_VERSION}/nuctl-${NUCLIO_VERSION}-linux-${NUCTL_ARCH}" \

    -o "/tmp/nuctl-${NUCLIO_VERSION}"

Устанавливаем бинарный файл:

sudo install \

    -m 0755 \

    "/tmp/nuctl-${NUCLIO_VERSION}" \

    /usr/local/bin/nuctl

Проверяем путь:

command -v nuctl

Ожидаемый результат:

/usr/local/bin/nuctl

Проверяем версию:

nuctl version

Версии Nuclio Dashboard и nuctl должны совпадать:

echo "Dashboard: $NUCLIO_VERSION"

nuctl version

2.9. Проверяем подключение nuctl к Nuclio

Запросим список проектов:

nuctl get projects \

    --platform local

Если модели пока не разворачивались, список может быть пустым.

Запросим список функций:

nuctl get functions \

    --platform local

До установки первой модели команда может не показать ни одной функции. Это нормальное состояние.

После успешного развёртывания моделей в списке появятся:

Рабочая функция обычно имеет состояние:

ready

2.10. Общая проверка установки

Выполним итоговый диагностический блок:

echo "===== NUCLIO CONTAINER ====="

 

docker ps \

    --filter name=nuclio \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'

 

echo

echo "===== CVAT SERVERLESS ====="

 

docker inspect cvat_server \

    --format '{{range .Config.Env}}{{println .}}{{end}}' \

    | grep -E '^CVAT_SERVERLESS=' \

    || echo "CVAT_SERVERLESS не найден"

 

echo

echo "===== NUCTL VERSION ====="

 

nuctl version

 

echo

echo "===== NUCLIO PROJECTS ====="

 

nuctl get projects \

    --platform local

 

echo

echo "===== NUCLIO FUNCTIONS ====="

 

nuctl get functions \

    --platform local

Если контейнер Nuclio запущен, версия nuctl совпадает с версией Dashboard, а команды nuctl get projects и nuctl get functions выполняются без ошибки соединения, инфраструктура готова к развёртыванию первой модели.

На следующем этапе мы создадим и запустим функцию детекции объектов, после чего модель появится в интерфейсе CVAT.

 

Запущенный сервис Nuclio, версия консольной утилиты nuctl и список развёрнутых serverless-функций.

3. Выбираем модель для первой предразметки

Для проверки интеграции используем детектор YOLO v7 в формате ONNX. Он получает изображение и возвращает прямоугольники объектов, названия классов и confidence score.

Такой пример удобен для первого запуска по нескольким причинам:

В официальном репозитории CVAT YOLO v7 входит в каталог готовых serverless-моделей. Функция описана как detector, работает с ONNX-моделью и возвращает объекты 80 классов COCO.

На первом этапе мы развернём стандартную модель. После проверки всей цепочки её можно будет заменить собственной ONNX-моделью и изменить список классов.

3.1. Почему важно использовать файлы своей версии CVAT

Структура функций, версия Python, параметры Nuclio и Docker-сеть менялись между версиями CVAT.

Поэтому не рекомендуется без проверки копировать каталог модели из ветки develop в старую установку CVAT. Надёжнее использовать serverless-функцию из того же Git-тега или commit, на котором работает сервер.

Проверим текущую версию репозитория:

cd /opt/cvat

 

git branch --show-current

git describe --tags --always --dirty

git rev-parse --short HEAD

Пример результата:

develop

v2.70.0

a1b2c3d4

Если репозиторий имеет локальные изменения, команда git describe может добавить суффикс:

-dirty

До завершения настройки модели обновлять CVAT или переключать Git-ветку не нужно.

3.2. Проверяем каталог serverless

Убедимся, что каталог с функциями существует:

test -d serverless \

    && echo "Каталог serverless найден" \

    || echo "Каталог serverless не найден"

Посмотрим его содержимое:

find serverless \

    -maxdepth 3 \

    -type d \

    | sort \

    | head -n 100

Найдём все функции, в названии которых встречается YOLO:

find serverless \

    -type d \

    -iname '*yolo*' \

    | sort

В актуальном репозитории CVAT ожидается каталог:

serverless/onnx/WongKinYiu/yolov7/nuclio

Проверим его автоматически:

YOLO_FUNCTION_DIR="serverless/onnx/WongKinYiu/yolov7/nuclio"

 

if [ -d "$YOLO_FUNCTION_DIR" ]; then

    echo "YOLO function directory: OK"

    echo "$YOLO_FUNCTION_DIR"

else

    echo "YOLO v7 function directory: NOT FOUND"

fi

3.3. Вариант для старой версии CVAT

В старых версиях репозитория готовой функции YOLO v7 может не быть. При этом могут присутствовать другие варианты YOLO, например OpenVINO YOLO v3.

Найдём все подходящие файлы конфигурации:

find serverless \

    -type f \

    \( -name 'function.yaml' -o -name 'function-gpu.yaml' \) \

    -print0 \

    | xargs -0 grep -il 'yolo' \

    | sort

Также найдём каталоги моделей-детекторов:

grep -RIl \

    --include='function.yaml' \

    --include='function-gpu.yaml' \

    'type: detector' \

    serverless \

    | sort

Если YOLO v7 отсутствует, есть два варианта:

  1. использовать совместимый YOLO-пример из текущей версии CVAT;
  2. подготовить собственную Nuclio-функцию под используемую версию платформы.

Для первого запуска предпочтителен первый вариант. Он снижает риск несовместимости между CVAT, Nuclio и форматом ответа функции.

3.4. Проверяем файлы функции YOLO v7

В официальном примере используются четыре основных файла:

function.yaml

function-gpu.yaml

main.py

model_handler.py

Проверим их наличие:

YOLO_FUNCTION_DIR="serverless/onnx/WongKinYiu/yolov7/nuclio"

 

for file in \

    function.yaml \

    function-gpu.yaml \

    main.py \

    model_handler.py

do

    if [ -f "$YOLO_FUNCTION_DIR/$file" ]; then

        printf '%-25s %s\n' "$file" "OK"

    else

        printf '%-25s %s\n' "$file" "NOT FOUND"

    fi

done

Ожидаемый результат:

function.yaml             OK

function-gpu.yaml         OK

main.py                   OK

model_handler.py          OK

В актуальном официальном примере каталог действительно содержит отдельные CPU- и GPU-конфигурации, HTTP-обработчик и класс для загрузки и запуска ONNX-модели.

3.5. За что отвечает каждый файл

function.yaml

Главный конфигурационный файл Nuclio-функции для CPU.

Он определяет:

Посмотрим основные параметры:

sed -n '1,45p' \

    "$YOLO_FUNCTION_DIR/function.yaml"

Отдельно выведем технические настройки:

grep -nE \

    'name:|type:|runtime:|handler:|baseImage:|eventTimeout:|maxRequestBodySize:' \

    "$YOLO_FUNCTION_DIR/function.yaml"

В официальной конфигурации модель зарегистрирована как detector, обработчиком указан main:handler, а классы имеют тип rectangle.

function-gpu.yaml

Альтернативная конфигурация для запуска инференса на NVIDIA GPU.

Проверим, какие GPU-ресурсы она запрашивает:

grep -nE \

    'nvidia.com/gpu|baseImage:|onnxruntime-gpu|runtime:|handler:' \

    "$YOLO_FUNCTION_DIR/function-gpu.yaml"

Для первого запуска будем использовать CPU-вариант. Это позволит проверить интеграцию независимо от NVIDIA Container Toolkit и конфигурации GPU.

main.py

HTTP-обработчик Nuclio.

Он выполняет четыре основные операции:

  1. получает JSON-запрос от CVAT;
  2. декодирует изображение;
  3. передаёт изображение обработчику модели;
  4. возвращает найденные объекты в формате JSON.

Посмотрим начало файла:

sed -n '1,220p' \

    "$YOLO_FUNCTION_DIR/main.py"

Найдём основную функцию:

grep -nE \

    '^def handler|^def init_context' \

    "$YOLO_FUNCTION_DIR/main.py"

Обычно init_context загружает модель один раз при запуске контейнера, а handler обрабатывает каждый новый запрос.

model_handler.py

Этот файл содержит основную логику инференса:

Проверим используемые библиотеки:

grep -nE \

    '^import |^from ' \

    "$YOLO_FUNCTION_DIR/model_handler.py"

Найдём класс обработчика:

grep -nE \

    '^class |^[[:space:]]+def ' \

    "$YOLO_FUNCTION_DIR/model_handler.py" \

    | head -n 40

3.6. Проверяем метаданные модели для CVAT

CVAT получает информацию о модели из раздела metadata.annotations файла function.yaml.

Выведем начало конфигурации:

sed -n '1,30p' \

    "$YOLO_FUNCTION_DIR/function.yaml"

Основные поля выглядят так:

metadata:

  name: onnx-wongkinyiu-yolov7

  namespace: cvat

  annotations:

    name: YOLO v7

    type: detector

    spec: |

      [

        { "id": 0, "name": "person", "type": "rectangle" },

        { "id": 1, "name": "bicycle", "type": "rectangle" },

        { "id": 2, "name": "car", "type": "rectangle" }

      ]

Значение полей:

Поле

Назначение

metadata.name

Системное имя Nuclio-функции

namespace

Пространство имён функции

annotations.name

Название модели в интерфейсе CVAT

annotations.type

Тип модели

annotations.spec

Классы и типы создаваемых объектов

id

Числовой идентификатор класса модели

name

Название класса

type

Тип аннотации CVAT

Для детектора bounding box используется:

"type": "rectangle"

Названия классов из annotations.spec позднее будут сопоставляться с метками задачи CVAT.

3.7. Проверяем команду развёртывания

В актуальном репозитории CVAT функция разворачивается через скрипт:

serverless/deploy_cpu.sh

Посмотрим его содержимое:

sed -n '1,220p' serverless/deploy_cpu.sh

Проверим права на выполнение:

ls -l serverless/deploy_cpu.sh

Если executable-флаг отсутствует:

chmod +x serverless/deploy_cpu.sh

Скрипт выполняет несколько действий:

В текущем официальном скрипте функции подключаются к сети cvat_cvat, а в параметры передаются CVAT_FUNCTIONS_REDIS_HOST и CVAT_FUNCTIONS_REDIS_PORT.

Проверим имя реальной Docker-сети на сервере:

docker network ls \

    --format 'table {{.Name}}\t{{.Driver}}\t{{.Scope}}' \

    | grep -Ei 'NAME|cvat'

Обычно сеть называется:

cvat_cvat

Однако при другом Compose project name название может отличаться, например:

annotation_cvat

razmetka_cvat

Узнать, к каким сетям подключён cvat_server, можно командой:

docker inspect cvat_server \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

Название этой сети понадобится при ручном развёртывании собственной модели.

3.8. Итоговая проверка перед сборкой

Выполним единый блок:

cd /opt/cvat

 

YOLO_FUNCTION_DIR="serverless/onnx/WongKinYiu/yolov7/nuclio"

 

echo "===== CVAT VERSION ====="

git describe --tags --always --dirty

git rev-parse --short HEAD

 

echo

echo "===== YOLO DIRECTORY ====="

if [ -d "$YOLO_FUNCTION_DIR" ]; then

    echo "$YOLO_FUNCTION_DIR: OK"

else

    echo "$YOLO_FUNCTION_DIR: NOT FOUND"

fi

 

echo

echo "===== YOLO FILES ====="

for file in \

    function.yaml \

    function-gpu.yaml \

    main.py \

    model_handler.py

do

    test -f "$YOLO_FUNCTION_DIR/$file" \

        && echo "$file: OK" \

        || echo "$file: NOT FOUND"

done

 

echo

echo "===== FUNCTION METADATA ====="

if [ -f "$YOLO_FUNCTION_DIR/function.yaml" ]; then

    grep -nE \

        'name: YOLO|type: detector|runtime:|handler:|baseImage:' \

        "$YOLO_FUNCTION_DIR/function.yaml"

fi

 

echo

echo "===== CVAT NETWORK ====="

docker inspect cvat_server \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

 

echo

echo "===== NUCTL ====="

nuctl version

Если каталог и все четыре файла найдены, версия nuctl выводится без ошибки, а Docker-сеть CVAT определена, можно переходить к сборке и развёртыванию модели.

3.2. Находим рабочий каталог установленного CVAT

Все команды для развёртывания serverless-моделей необходимо выполнять из корневого каталога репозитория CVAT.

Если выполнить:

find serverless

из домашнего каталога пользователя, система будет искать путь:

/root/serverless

и вернёт ошибку:

find: ‘serverless’: No such file or directory

Это не означает, что CVAT или Nuclio установлены неправильно. Сначала необходимо определить каталог, из которого был запущен Docker Compose.

Определяем рабочий каталог через контейнер CVAT

Контейнеры Docker Compose содержат служебные метки с информацией о проекте.

Посмотрим имя Compose-проекта:

docker inspect cvat_server \

    --format '{{ index .Config.Labels "com.docker.compose.project" }}'

Ожидаемый результат:

cvat

Получим рабочий каталог проекта:

docker inspect cvat_server \

    --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'

Например:

/root/cvat

или:

/opt/cvat

Посмотрим, какие Compose-файлы использовались при запуске:

docker inspect cvat_server \

    --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}'

Пример результата:

/root/cvat/docker-compose.yml,/root/cvat/docker-compose.override.yml

Для удобства сохраним найденный каталог в переменную:

CVAT_DIR=$(

    docker inspect cvat_server \

        --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'

)

 

echo "CVAT directory: $CVAT_DIR"

Проверим, что каталог существует:

if [ -n "$CVAT_DIR" ] && [ -d "$CVAT_DIR" ]; then

    echo "Каталог CVAT найден: $CVAT_DIR"

else

    echo "Рабочий каталог не удалось определить через Docker labels"

fi

Если путь определился, перейдём в него:

cd "$CVAT_DIR"

Проверим текущее положение:

pwd

И содержимое каталога:

ls -la

В корне стандартной установки CVAT должен находиться основной Compose-файл:

docker-compose.yml

Проверим его наличие:

test -f docker-compose.yml \

    && echo "docker-compose.yml: OK" \

    || echo "docker-compose.yml: NOT FOUND"

Если метка рабочего каталога отсутствует

В старых версиях Docker Compose служебная метка working_dir может отсутствовать или возвращать пустую строку.

Выведем все Compose-метки контейнера:

docker inspect cvat_server \

    --format '{{json .Config.Labels}}' \

    | python3 -m json.tool

Если Python отсутствует:

docker inspect cvat_server \

    --format '{{json .Config.Labels}}'

Также посмотрим каталоги, подключённые к контейнеру:

docker inspect cvat_server \

    --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

После этого выполним ограниченный поиск Compose-файлов в типичных каталогах:

sudo find \

    /opt \

    /srv \

    /home \

    /root \

    -maxdepth 6 \

    -type f \

    \( \

        -name 'docker-compose.yml' \

        -o -name 'docker-compose.yaml' \

        -o -name 'compose.yml' \

        -o -name 'compose.yaml' \

    \) \

    2>/dev/null \

    | sort

Искать следует каталог, в котором одновременно присутствуют:

docker-compose.yml

cvat/

serverless/

components/

Наличие конкретных директорий зависит от версии CVAT.

Проверяем, что найден именно репозиторий CVAT

После перехода в предполагаемый каталог выполним:

pwd

 

git remote -v 2>/dev/null || true

git branch --show-current 2>/dev/null || true

git describe --tags --always --dirty 2>/dev/null || true

git rev-parse --short HEAD 2>/dev/null || true

Проверим основные файлы:

for path in \

    docker-compose.yml \

    cvat \

    serverless

do

    if [ -e "$path" ]; then

        printf '%-25s %s\n' "$path" "FOUND"

    else

        printf '%-25s %s\n' "$path" "NOT FOUND"

    fi

done

Ищем каталог serverless

Теперь поиск выполняется относительно корня CVAT:

find . \

    -maxdepth 4 \

    -type d \

    -iname '*serverless*' \

    | sort

Для большинства версий CVAT ожидается каталог:

./serverless

Официальные примеры serverless-моделей CVAT хранятся в репозитории платформы и разворачиваются через nuctl и скрипты из каталога serverless.

Проверим его:

if [ -d serverless ]; then

    echo "Каталог serverless найден"

else

    echo "Каталог serverless в текущем checkout отсутствует"

fi

Если каталог найден, посмотрим доступные модели:

find serverless \

    -mindepth 1 \

    -maxdepth 4 \

    -type d \

    | sort \

    | head -n 150

Найдём модели семейства YOLO:

find serverless \

    -type d \

    -iname '*yolo*' \

    | sort

Почему Nuclio может работать без каталога serverless

Каталог serverless требуется во время подготовки и развёртывания функции. После сборки модель работает как самостоятельный Docker-контейнер.

Поэтому возможна ситуация, когда:

На уже развёрнутую функцию отсутствие исходного каталога напрямую не влияет. Но для установки новой модели исходники функции потребуются.

Итоговая диагностическая команда

Выполним единый блок:

echo "===== COMPOSE PROJECT ====="

 

docker inspect cvat_server \

    --format 'Project: {{ index .Config.Labels "com.docker.compose.project" }}'

 

docker inspect cvat_server \

    --format 'Working directory: {{ index .Config.Labels "com.docker.compose.project.working_dir" }}'

 

docker inspect cvat_server \

    --format 'Compose files: {{ index .Config.Labels "com.docker.compose.project.config_files" }}'

 

echo

echo "===== CURRENT DIRECTORY ====="

 

pwd

 

echo

echo "===== CONTAINER NETWORKS ====="

 

docker inspect cvat_server \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

 

echo

echo "===== CONTAINER MOUNTS ====="

 

docker inspect cvat_server \

    --format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

По текущей диагностике Docker-сеть уже определена:

cvat_cvat

Эта сеть будет использоваться при развёртывании новой Nuclio-функции.

 

 

Определение рабочего каталога Docker Compose и поиск исходников serverless-моделей в установленном CVAT.

3. Выбираем встроенную модель YOLOv5

В разных версиях CVAT набор готовых serverless-моделей отличается. Поэтому перед развёртыванием необходимо проверить, какие функции уже входят в установленную версию платформы.

В рассматриваемой установке доступны две модели семейства YOLO:

serverless/openvino/omz/public/yolo-v3-tf

serverless/pytorch/ultralytics/yolov5

Для первого запуска используем Ultralytics YOLOv5 на PyTorch.

Эта модель решает задачу Object Detection: находит объекты на изображении и возвращает прямоугольники, названия классов и коэффициенты уверенности. В CVAT результаты модели преобразуются в аннотации типа rectangle.

Использование модели из текущего Git checkout предпочтительнее, чем копирование новой функции из ветки develop. Serverless-функция должна быть совместима с используемыми версиями CVAT, Nuclio и Python.

В официальной документации CVAT также рекомендуется разворачивать встроенные функции из каталога serverless клонированного репозитория. После развёртывания функция становится доступна CVAT для автоматической аннотации.

3.1. Проверяем версию CVAT

Перейдём в корневой каталог установки:

cd /root/cvat

Проверим Git-ветку, тег и commit:

echo "===== CVAT VERSION ====="

 

git branch --show-current

git describe --tags --always --dirty

git rev-parse --short HEAD

Дополнительно посмотрим дату последнего commit:

git log -1 \

    --date=iso \

    --format='Commit: %H%nDate: %ad%nSubject: %s'

Эти сведения стоит сохранить: они помогут воспроизвести установку и подобрать совместимые версии компонентов.

До завершения настройки модели не следует выполнять:

git pull

или переключать CVAT на другую ветку. Обновление исходников может потребовать миграции базы данных и пересборки всех контейнеров.

3.2. Проверяем каталог YOLOv5

Зададим путь к встроенной модели:

YOLO_ROOT="/root/cvat/serverless/pytorch/ultralytics/yolov5"

Проверим наличие каталога:

if [ -d "$YOLO_ROOT" ]; then

    echo "YOLOv5 directory: OK"

else

    echo "YOLOv5 directory: NOT FOUND"

fi

Посмотрим структуру:

find "$YOLO_ROOT" \

    -maxdepth 4 \

    -type f \

    | sort

Обычно внутри находится каталог nuclio, содержащий исходники serverless-функции:

serverless/pytorch/ultralytics/yolov5/

└── nuclio/

    ── function.yaml

    └── main.py

В некоторых версиях дополнительно могут присутствовать:

function-gpu.yaml

model_handler.py

README.md

Набор файлов зависит от версии CVAT.

3.3. Автоматически определяем каталог функции

Найдём CPU-конфигурацию:

YOLO_FUNCTION_FILE=$(

    find "$YOLO_ROOT" \

        -type f \

        -name 'function.yaml' \

        | head -n 1

)

 

echo "Function file: $YOLO_FUNCTION_FILE"

Определим каталог, который нужно передать скрипту развёртывания:

YOLO_FUNCTION_DIR=$(

    dirname "$YOLO_FUNCTION_FILE"

)

 

echo "Function directory: $YOLO_FUNCTION_DIR"

Проверим результат:

test -f "$YOLO_FUNCTION_DIR/function.yaml" \

    && echo "function.yaml: OK" \

    || echo "function.yaml: NOT FOUND"

 

test -f "$YOLO_FUNCTION_DIR/main.py" \

    && echo "main.py: OK" \

    || echo "main.py: NOT FOUND"

Ожидаемый каталог:

/root/cvat/serverless/pytorch/ultralytics/yolov5/nuclio

3.4. Изучаем конфигурацию модели

Файл function.yaml определяет, как Nuclio соберёт и запустит контейнер модели.

Выведем его содержимое:

sed -n '1,240p' \

    "$YOLO_FUNCTION_DIR/function.yaml"

Для сокращённой проверки выведем только ключевые параметры:

grep -nE \

    '^[[:space:]]*name:|type:|framework:|runtime:|handler:|baseImage:|image:|commands:|spec:' \

    "$YOLO_FUNCTION_DIR/function.yaml"

В конфигурации нас интересуют следующие поля:

Параметр

Назначение

metadata.name

Системное имя Nuclio-функции

annotations.name

Название модели в интерфейсе CVAT

annotations.type

Тип модели, например detector

annotations.framework

Используемый ML-фреймворк

annotations.spec

Список классов модели

runtime

Версия среды выполнения Python

handler

Функция, принимающая запросы

baseImage

Базовый Docker-образ

build.commands

Команды установки зависимостей

Покажем метаданные отдельно:

sed -n '/metadata:/,/spec:/p' \

    "$YOLO_FUNCTION_DIR/function.yaml"

Найдём имя, под которым функция будет зарегистрирована в Nuclio:

awk '

    /^metadata:/ {

        metadata=1

        next

    }

 

    metadata && /^[[:space:]]{2}name:/ {

        print

        exit

    }

' "$YOLO_FUNCTION_DIR/function.yaml"

Проверим тип модели:

grep -n \

    'type: detector' \

    "$YOLO_FUNCTION_DIR/function.yaml"

Если вывод содержит:

type: detector

CVAT будет воспринимать функцию как детектор объектов.

3.5. Проверяем классы модели

В стандартном варианте YOLOv5 использует классы датасета COCO. Среди них:

person

bicycle

car

motorcycle

bus

truck

dog

cat

chair

bottle

Полный список хранится в поле annotations.spec.

Выведем этот фрагмент:

grep -n -A 100 \

    'spec:' \

    "$YOLO_FUNCTION_DIR/function.yaml" \

    | head -n 110

Названия классов важны: при запуске предразметки CVAT предложит сопоставить классы модели с метками задачи.

Например:

Класс модели: person

Метка задачи: Человек

или:

Класс модели: car

Метка задачи: Автомобиль

Названия не обязаны полностью совпадать — сопоставление можно выполнить вручную перед запуском аннотации.

3.6. Проверяем код HTTP-обработчика

Откроем файл main.py:

sed -n '1,260p' \

    "$YOLO_FUNCTION_DIR/main.py"

Найдём функции инициализации и обработки запросов:

grep -nE \

    '^def |^[[:space:]]+def ' \

    "$YOLO_FUNCTION_DIR/main.py"

Обычно в файле присутствуют две основные функции:

def init_context(context):

    ...

и:

def handler(context, event):

    ...

init_context вызывается при запуске контейнера. На этом этапе загружается модель.

handler вызывается при каждом запросе CVAT. Он:

  1. получает изображение;
  2. декодирует его;
  3. запускает инференс;
  4. преобразует предсказания в формат CVAT;
  5. возвращает JSON с объектами.

В YOLOv5-функциях того периода модель могла загружаться через torch.hub.load. Готовый пример CVAT уже содержит необходимый код, поэтому перед первым запуском изменять main.py не требуется. Примеры подключения пользовательских весов требуют отдельных изменений и будут рассмотрены после проверки стандартной модели.

Проверим, какая модель загружается:

grep -nE \

    'torch\.hub\.load|yolov5[nsmlx]|model =' \

    "$YOLO_FUNCTION_DIR/main.py"

Например, строка может содержать:

torch.hub.load("ultralytics/yolov5", "yolov5s")

Обозначение yolov5s соответствует компактной версии модели. Она подходит для первого CPU-запуска, поскольку требует меньше памяти, чем более крупные варианты.

3.7. Проверяем скрипт развёртывания CVAT

Перейдём в корень CVAT:

cd /root/cvat

Проверим наличие CPU-скрипта:

test -f serverless/deploy_cpu.sh \

    && echo "deploy_cpu.sh: OK" \

    || echo "deploy_cpu.sh: NOT FOUND"

Проверим права:

ls -l serverless/deploy_cpu.sh

Если файл не имеет права на выполнение:

chmod +x serverless/deploy_cpu.sh

Посмотрим содержимое скрипта:

sed -n '1,260p' \

    serverless/deploy_cpu.sh

Особое внимание нужно обратить на:

Выведем только важные строки:

grep -nE \

    'nuctl|project|network|redis|function.yaml|platform|deploy' \

    serverless/deploy_cpu.sh

3.8. Проверяем Docker-сеть

Nuclio-функция должна быть подключена к той же сети, через которую CVAT взаимодействует со своими сервисами.

Проверим сеть сервера:

docker inspect cvat_server \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

Для рассматриваемой установки результат:

cvat_cvat

Проверим существующую функцию:

docker inspect nuclio-nuclio-centernet-detector \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

В выводе также должна присутствовать:

cvat_cvat

Это подтверждает, что модель и CVAT находятся в общей Docker-сети.

3.9. Проверяем, не развёрнута ли YOLOv5 ранее

Выведем список функций Nuclio:

nuctl get functions \

    --platform local

Также проверим Docker-контейнеры:

docker ps -a \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' \

    | grep -Ei 'NAME|yolo|nuclio'

Если функции YOLOv5 в списке нет, её можно будет развернуть без конфликта имён.

Если функция уже существует, сначала нужно проверить её состояние и конфигурацию. Повторное развёртывание с тем же именем может обновить существующую функцию.

3.10. Выполняем итоговую проверку

Запустим единый диагностический блок:

cd /root/cvat

 

YOLO_ROOT="/root/cvat/serverless/pytorch/ultralytics/yolov5"

 

YOLO_FUNCTION_FILE=$(

    find "$YOLO_ROOT" \

        -type f \

        -name 'function.yaml' \

        | head -n 1

)

 

YOLO_FUNCTION_DIR=$(

    dirname "$YOLO_FUNCTION_FILE"

)

 

echo "===== CVAT VERSION ====="

 

git describe --tags --always --dirty

git rev-parse --short HEAD

 

echo

echo "===== YOLO FUNCTION DIRECTORY ====="

 

echo "$YOLO_FUNCTION_DIR"

 

echo

echo "===== YOLO FILES ====="

 

find "$YOLO_FUNCTION_DIR" \

    -maxdepth 2 \

    -type f \

    | sort

 

echo

echo "===== FUNCTION PARAMETERS ====="

 

grep -nE \

    'name:|type: detector|framework:|runtime:|handler:|baseImage:' \

    "$YOLO_FUNCTION_DIR/function.yaml"

 

echo

echo "===== MODEL LOADING ====="

 

grep -nE \

    'torch\.hub\.load|yolov5[nsmlx]|model =' \

    "$YOLO_FUNCTION_DIR/main.py"

 

echo

echo "===== CVAT NETWORK ====="

 

docker inspect cvat_server \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

 

echo

echo "===== EXISTING FUNCTIONS ====="

 

nuctl get functions \

    --platform local

На этом этапе мы только проверили функцию. Сборка Docker-образа и загрузка весов начнутся на следующем шаге.

 

 

Проверка конфигурации встроенной serverless-функции YOLOv5 перед развёртыванием в Nuclio.

 

4. Разворачиваем YOLOv5 в Nuclio

После проверки конфигурации можно собрать serverless-функцию и подключить её к CVAT.

Для рассматриваемой версии платформы используется встроенная функция:

serverless/pytorch/ultralytics/yolov5/nuclio

Её конфигурация содержит следующие параметры:

Имя функции: ultralytics-yolov5

Название в CVAT: YOLO v5

Тип: detector

Фреймворк: PyTorch

Runtime: Python 3.6

Базовый образ: ultralytics/yolov5:latest-cpu

Обработчик: main:handler

В старой версии CVAT функция рассчитана на Nuclio 1.8.14 и Python 3.6. Менять runtime на Python 3.8, 3.9 или более новую версию перед первым запуском не следует: это может нарушить совместимость с базовым образом и кодом функции.

Python 3.6 в Nuclio 1.8.14 уже отмечался как устаревший, но для старого встроенного примера CVAT его лучше оставить без изменений до успешного тестового запуска. В более новых версиях CVAT необходимо использовать конфигурацию модели из соответствующего checkout репозитория.

4.1. Переходим в каталог CVAT

cd /root/cvat

Проверим текущий каталог:

pwd

Ожидаемый результат:

/root/cvat

Зададим путь к функции:

YOLO_FUNCTION_DIR="serverless/pytorch/ultralytics/yolov5/nuclio"

Проверим основные файлы:

test -f "$YOLO_FUNCTION_DIR/function.yaml" \

    && echo "function.yaml: OK" \

    || echo "function.yaml: NOT FOUND"

 

test -f "$YOLO_FUNCTION_DIR/main.py" \

    && echo "main.py: OK" \

    || echo "main.py: NOT FOUND"

Ожидаемый результат:

function.yaml: OK

main.py: OK

4.2. Проверяем способ загрузки модели

Посмотрим фрагмент main.py, отвечающий за инициализацию:

grep -nE \

    'def init_context|torch\.hub\.load|context\.user_data|yolov5|model[[:space:]]*=' \

    "$YOLO_FUNCTION_DIR/main.py"

Для стандартной функции YOLOv5 код обычно загружает модель при запуске контейнера и сохраняет её в context.user_data.

Пример:

def init_context(context):

    model = torch.hub.load(...)

    context.user_data.model = model

Загрузка модели в init_context важна: веса и структура нейронной сети загружаются один раз при запуске контейнера, а не при обработке каждого изображения.

Посмотрим весь файл:

sed -n '1,240p' \

    "$YOLO_FUNCTION_DIR/main.py"

Перед первым развёртыванием изменять main.py не требуется.

4.3. Создаём резервную копию конфигурации

Перед изменениями сохраним исходные файлы:

BACKUP_DIR="/root/cvat-model-backups/yolov5-$(date +%Y%m%d-%H%M%S)"

 

mkdir -p "$BACKUP_DIR"

 

cp \

    "$YOLO_FUNCTION_DIR/function.yaml" \

    "$YOLO_FUNCTION_DIR/main.py" \

    "$BACKUP_DIR/"

Проверим резервную копию:

find "$BACKUP_DIR" \

    -maxdepth 1 \

    -type f \

    -ls

Файлы будут сохранены вне каталога Git-репозитория:

/root/cvat-model-backups/

4.4. Проверяем версию nuctl

Версия консольного клиента должна соответствовать версии Nuclio Dashboard.

Выведем образ работающего Nuclio:

docker inspect nuclio \

    --format '{{.Config.Image}}'

Для рассматриваемого сервера:

quay.io/nuclio/dashboard:1.8.14-amd64

Проверим клиент:

nuctl version

В выводе должна присутствовать версия:

1.8.14

Дополнительно можно выполнить:

echo "Nuclio Dashboard:"

docker inspect nuclio \

    --format '{{.Config.Image}}'

 

echo

echo "nuctl:"

nuctl version

При несовпадении версий развёртывание функции лучше не запускать.

4.5. Проверяем Docker-сеть

Функция должна находиться в одной Docker-сети с CVAT.

CVAT_NETWORK=$(

    docker inspect cvat_server \

        --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}' \

        | head -n 1

)

 

echo "CVAT network: $CVAT_NETWORK"

Для рассматриваемой установки:

CVAT network: cvat_cvat

Проверим, что сеть существует:

docker network inspect "$CVAT_NETWORK" \

    >/dev/null \

    && echo "Docker network: OK"

4.6. Проверяем базовый образ YOLOv5

В function.yaml используется базовый образ:

ultralytics/yolov5:latest-cpu

Проверим, присутствует ли он на сервере:

docker image inspect \

    ultralytics/yolov5:latest-cpu \

    >/dev/null 2>&1 \

    && echo "Base image is already available" \

    || echo "Base image is not downloaded"

Посмотрим существующие локальные образы YOLO:

docker images \

    --digests \

    ultralytics/yolov5

Если образ отсутствует, загрузим его отдельно:

docker pull ultralytics/yolov5:latest-cpu

Отдельная загрузка помогает заранее обнаружить проблемы с Docker Hub, DNS, свободным местом или авторизацией, не дожидаясь запуска полной сборки Nuclio.

После загрузки зафиксируем идентификатор образа:

docker image inspect \

    ultralytics/yolov5:latest-cpu \

    --format 'Image ID: {{.Id}}{{println}}Created: {{.Created}}{{println}}Digests: {{json .RepoDigests}}'

Тег latest-cpu является изменяемым, поэтому идентификатор или digest стоит сохранить в технической документации проекта.

4.7. Проверяем свободное место

Сборка функции создаёт дополнительные Docker-слои.

df -h /

Посмотрим использование диска Docker:

docker system df

Не следует выполнять перед сборкой:

docker system prune -a

Эта команда может удалить неиспользуемые образы, которые нужны другим контейнерам или для быстрого отката.

4.8. Проверяем существующие функции

nuctl get functions \

    --platform local

На сервере уже может присутствовать другая функция, например:

centernet-detector

Это не мешает установке YOLOv5, поскольку у неё другое системное имя:

ultralytics-yolov5

Дополнительно проверим контейнеры:

docker ps -a \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' \

    | grep -Ei 'NAME|nuclio|yolov5|centernet'

4.9. Проверяем скрипт deploy_cpu.sh

CVAT содержит вспомогательный скрипт для развёртывания CPU-функций:

ls -l serverless/deploy_cpu.sh

Если файл не исполняемый:

chmod +x serverless/deploy_cpu.sh

Посмотрим, какие параметры он передаёт в Nuclio:

grep -nE \

    'nuctl|project|platform|network|redis|deploy' \

    serverless/deploy_cpu.sh

Официальный способ развёртывания встроенной CPU-функции CVAT — передать скрипту каталог, содержащий function.yaml и исходный код обработчика.

4.10. Запускаем сборку и развёртывание

Из корневого каталога CVAT выполним:

cd /root/cvat

Запустим функцию:

./serverless/deploy_cpu.sh \

    serverless/pytorch/ultralytics/yolov5/nuclio

Для одновременного сохранения полного журнала используем:

set -o pipefail

 

./serverless/deploy_cpu.sh \

    serverless/pytorch/ultralytics/yolov5/nuclio \

    2>&1 \

    | tee "/root/yolov5-deploy-$(date +%Y%m%d-%H%M%S).log"

Во время выполнения Nuclio:

  1. прочитает function.yaml;
  2. загрузит необходимые Docker-образы;
  3. подготовит Python-обработчик;
  4. соберёт итоговый образ функции;
  5. создаст контейнер модели;
  6. подключит его к Docker-сети CVAT;
  7. зарегистрирует функцию в проекте cvat;
  8. проверит готовность HTTP-обработчика.

Сборка может выводить предупреждение об устаревшем Python 3.6. Для этой версии функции предупреждение само по себе не означает ошибку.

Успешное завершение обычно сопровождается сообщениями:

Build complete

Function deploy complete

Function is ready

Критическим является итоговое состояние функции, а не наличие предупреждений в середине журнала.

4.11. Проверяем состояние функции

После завершения сборки выполним:

nuctl get functions \

    --platform local

В списке должна появиться функция YOLOv5 со статусом:

ready

Проверим Docker-контейнер:

docker ps \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' \

    | grep -Ei 'NAME|yolov5'

Имя контейнера обычно начинается с:

nuclio-nuclio-ultralytics-yolov5

Точное имя получим автоматически:

YOLO_CONTAINER=$(

    docker ps \

        --format '{{.Names}}' \

        | grep -i 'yolov5' \

        | head -n 1

)

 

echo "YOLO container: $YOLO_CONTAINER"

Проверим статус:

docker inspect "$YOLO_CONTAINER" \

    --format 'Status: {{.State.Status}}{{println}}Started: {{.State.StartedAt}}{{println}}Restarting: {{.State.Restarting}}'

4.12. Проверяем журнал функции

docker logs \

    --tail 100 \

    "$YOLO_CONTAINER"

Для просмотра только ошибок:

docker logs \

    "$YOLO_CONTAINER" \

    2>&1 \

    | grep -Ei \

        'error|exception|traceback|failed'

Отсутствие вывода у второй команды означает, что в текущем журнале не найдено строк с типичными признаками ошибки.

Проверим, не перезапускается ли контейнер:

docker inspect "$YOLO_CONTAINER" \

    --format 'Restart count: {{.RestartCount}}'

Ожидаемый результат:

Restart count: 0

4.13. Проверяем подключение к сети CVAT

docker inspect "$YOLO_CONTAINER" \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

В выводе должна присутствовать сеть:

cvat_cvat

Проверим одновременно сеть CVAT и модели:

echo "CVAT server networks:"

 

docker inspect cvat_server \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

 

echo

echo "YOLOv5 networks:"

 

docker inspect "$YOLO_CONTAINER" \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

4.14. Итоговая проверка

cd /root/cvat

 

YOLO_CONTAINER=$(

    docker ps \

        --format '{{.Names}}' \

        | grep -i 'yolov5' \

        | head -n 1

)

 

echo "===== NUCTL FUNCTIONS ====="

 

nuctl get functions \

    --platform local

 

echo

echo "===== YOLO CONTAINER ====="

 

docker ps \

    --filter "name=$YOLO_CONTAINER" \

    --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'

 

echo

echo "===== YOLO NETWORKS ====="

 

docker inspect "$YOLO_CONTAINER" \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

 

echo

echo "===== RESTART COUNT ====="

 

docker inspect "$YOLO_CONTAINER" \

    --format '{{.RestartCount}}'

 

echo

echo "===== LAST LOG LINES ====="

 

docker logs \

    --tail 20 \

    "$YOLO_CONTAINER"

Если функция имеет состояние ready, контейнер запущен, подключён к сети cvat_cvat и не находится в цикле перезапуска, модель успешно развёрнута.

На следующем этапе проверим, появилась ли YOLOv5 в интерфейсе CVAT, создадим тестовую задачу и запустим автоматическую предразметку.

 

Успешно развёрнутая функция YOLOv5 в Nuclio и работающий контейнер модели.

5. Запускаем автоматическую предразметку в CVAT

После развёртывания проверим состояние функции:

cd /root/cvat

 

nuctl get functions --platform local

У функции ultralytics-yolov5 должен быть статус ready.

Дополнительно проверим контейнер:

docker ps \

    --format 'table {{.Names}}\t{{.Status}}' \

    | grep -Ei 'NAME|yolov5'

Проверяем модель в интерфейсе

Откройте CVAT и перейдите на страницу Models. В списке должна появиться модель:

YOLO v5

Страница Models показывает serverless-модели, которые были развёрнуты и доступны для автоматической или полуавтоматической разметки.

Если модель не появилась, обновите страницу и проверьте:

nuctl get functions --platform local

docker logs --tail 100 nuclio

Создаём тестовую задачу

Создайте новую задачу и загрузите 5–10 изображений с объектами, которые YOLOv5 умеет распознавать.

Добавьте несколько меток:

person

car

bicycle

dog

cat

Для первой проверки лучше использовать английские названия, совпадающие с классами модели. Позднее модель можно сопоставить и с русскими метками.

Запускаем автоматическую аннотацию

Откройте страницу Tasks, найдите тестовую задачу и выберите:

Actions → Automatic annotation

В появившемся окне:

  1. Выберите модель YOLO v5.
  2. Сопоставьте классы модели с метками задачи.
  3. Установите порог уверенности 0.5.
  4. Нажмите Annotate.

Официальный сценарий CVAT также предусматривает выбор модели, сопоставление меток и настройку порога уверенности перед запуском обработки.

Пример сопоставления:

Класс YOLOv5

Метка задачи

person

person

car

car

dog

dog

Неиспользуемые классы можно не сопоставлять.

Параметр Clean old annotations удаляет существующую разметку перед запуском модели. Для пустой тестовой задачи он не требуется.

Следим за работой модели

Во время обработки можно открыть вторую SSH-сессию и смотреть журнал контейнера:

YOLO_CONTAINER=$(

    docker ps \

        --format '{{.Names}}' \

        | grep -i yolov5 \

        | head -n 1

)

 

docker logs \

    --follow \

    --tail 50 \

    "$YOLO_CONTAINER"

После завершения CVAT покажет окончание операции. Откройте задачу и перейдите в Job.

На изображениях должны появиться прямоугольники с названиями классов модели.

Если объектов слишком мало, уменьшите порог до 0.3. Если модель создаёт много ошибочных прямоугольников, увеличьте его до 0.6–0.7.

Автоматическая разметка не заменяет проверку человеком. Разметчик должен удалить ложные срабатывания, добавить пропущенные объекты и скорректировать границы прямоугольников.

Откройте раздел Tasks, найдите нужную задачу и раскройте меню Actions на её карточке. В некоторых версиях CVAT это меню обозначено значком с тремя точками. Выберите пункт Automatic annotation.

 

 

CVAT: Tasks → открыть задачу → Actions → Automatic annotation

6. Проверяем результат предразметки

После завершения Automatic annotation откройте задачу и перейдите в Job. CVAT должен добавить найденные объекты как обычные прямоугольники, доступные для редактирования.

Разметчику необходимо проверить:

Автоматическая аннотация создаёт предварительную разметку, но не заменяет ручной контроль качества. CVAT позволяет сопоставить классы модели с метками задачи перед запуском обработки.

Подбираем порог уверенности

Для первого запуска можно использовать значение:

0.5

Практическая настройка:

Оптимальный порог лучше определить на небольшой тестовой выборке, а затем использовать для всего датасета.

Проверяем работу модели на сервере

Посмотрим статус функции:

nuctl get functions --platform local

Найдём контейнер YOLOv5:

YOLO_CONTAINER=$(

    docker ps \

        --format '{{.Names}}' \

        | grep -i yolov5 \

        | head -n 1

)

 

echo "$YOLO_CONTAINER"

Проверим последние запросы:

docker logs \

    --tail 100 \

    "$YOLO_CONTAINER"

Для просмотра журнала во время запуска предразметки:

docker logs \

    --follow \

    --tail 30 \

    "$YOLO_CONTAINER"

Остановить просмотр можно сочетанием:

Ctrl+C

Это не останавливает контейнер или обработку задачи.

Если аннотации не появились

Проверьте состояние функции:

nuctl get functions --platform local

Она должна иметь статус ready.

Проверьте ошибки контейнера:

docker logs "$YOLO_CONTAINER" 2>&1 \

    | grep -Ei 'error|exception|traceback|failed'

Проверьте, что модель подключена к сети CVAT:

docker inspect "$YOLO_CONTAINER" \

    --format '{{range $name, $value := .NetworkSettings.Networks}}{{println $name}}{{end}}'

Ожидаемая сеть:

cvat_cvat

Если функция работает, но объекты не найдены:

  1. уменьшите confidence threshold;
  2. используйте изображения с классами COCO;
  3. проверьте сопоставление классов;
  4. убедитесь, что изображения корректно открываются в CVAT.

После проверки сохраните исправленную разметку кнопкой Save в интерфейсе Job.

 

Результат автоматической предразметки YOLOv5, открытый для проверки и корректировки разметчиком.

 

Заключение

Подключение модели к CVAT позволяет перенести значительную часть однотипной работы с разметчика на алгоритм. Вместо того чтобы создавать каждый объект вручную, специалист получает готовую предразметку и сосредотачивается на проверке классов, исправлении границ и добавлении пропущенных объектов.

В этом руководстве мы:

Главное преимущество такого подхода — модель становится частью привычного процесса разметки. Разметчикам не требуется запускать отдельные скрипты, выгружать изображения или вручную импортировать результаты. Предсказания создаются непосредственно в задаче CVAT и редактируются как обычные аннотации.

При этом предразметку не следует рассматривать как полностью автоматическую замену ручной работы. Качество результата зависит от состава датасета, классов модели, порога уверенности и различий между обучающими данными модели и реальными изображениями проекта. Перед обработкой большого массива данных модель необходимо проверить на небольшой выборке и оценить, действительно ли исправление предсказаний занимает меньше времени, чем разметка с нуля.

Стандартная YOLOv5 подходит для проверки самой интеграции и работы с распространёнными объектами из датасета COCO. Для производственного проекта обычно требуется собственная модель, обученная на целевых классах: промышленном оборудовании, медицинских изображениях, товарах, документах, дорожной инфраструктуре или других специализированных объектах.

После успешного тестового запуска встроенную модель можно заменить собственной. Для этого потребуется изменить список классов в function.yaml, подключить веса модели и адаптировать обработчик так, чтобы он возвращал результаты в формате CVAT. Общая схема работы при этом останется прежней:

Изображение в CVAT

Serverless-функция Nuclio

Инференс модели

Предварительные аннотации

Проверка разметчиком

Готовый датасет

Таким образом, CVAT и Nuclio позволяют создать управляемый процесс разметки с участием человека: модель ускоряет обработку данных, а разметчик обеспечивает точность и соответствие требованиям проекта.

 

О компании US-DATA

US-DATA помогает компаниям готовить данные для обучения моделей машинного обучения, компьютерного зрения и искусственного интеллекта. Команда выполняет разметку изображений, видео, текстов и аудио, разрабатывает инструкции для разметчиков, организует контроль качества и подготавливает датасеты в форматах, необходимых для обучения моделей.

Подробнее об услугах:

Если вам необходимо подготовить датасет, организовать предразметку с помощью модели или масштабировать работу команды аннотаторов, специалисты US-DATA помогут подобрать инструменты и выстроить процесс с учётом требований вашего ML-проекта.