Paligemma2 не детектирует объекты: ошибки bounding box и рабочее решение | AiManual
AiManual Logo Ai / Manual.
02 Янв 2026 Гайд

Paligemma2 молчит на локальной машине: почему модель видит объекты, но не показывает bounding boxes

Разбор ошибок при локальном запуске Paligemma2: пустые координаты, проблемы с библиотеками, рабочий скрипт для object detection на Python.

Тишина вместо bounding boxes

Вы скачали Paligemma2, запустили скрипт из документации, загрузили картинку. Модель работает, инференс происходит, токены генерируются. Но на выходе - пустота. Ни одного bounding box'а. Ни одной координаты. Просто тишина.

Это не ваш код сломался. Это типичная проблема с мультимодальными моделями, которые выходят за рамки стандартных Hugging Face pipelines. Paligemma2 - не YOLO, не DETR, и не обычный object detector. Это гибрид, который работает по своим правилам.

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

Корень проблемы: модель возвращает токены, а не координаты

Вот что происходит на самом деле. Когда вы вызываете стандартный пайплайн:

from transformers import pipeline

pipe = pipeline("image-text-to-text", model="google/paligemma2-3b-pt-224")
result = pipe("Что на картинке?", image="cat.jpg")
print(result)

Вы получаете текстовое описание. Что-то вроде "На картинке кошка сидит на диване". Отлично. Но где же bounding boxes?

Проблема в том, что Paligemma2 по умолчанию работает в текстовом режиме. Она генерирует описания, а не детектирует объекты. Для получения координат нужно:

  1. Использовать специальный формат промпта
  2. Парсить выходные токены особым образом
  3. Конвертировать нормализованные координаты в пиксельные

И самое главное - большинство примеров в интернете устарели или написаны для демо на Hugging Face Spaces, где всё работает "волшебным образом".

1 Почему стандартные примеры не работают

Откройте официальный ноутбук от Google. Там красивый код с bounding boxes. Попробуйте запустить его локально - получите ошибку импорта или пустой вывод. Почему?

Что обещают Что получаете Причина
Автоматический детект объектов Текстовое описание Не указан format="bbox" в промпте
Координаты в пикселях Нормализованные [0,1] значения Отсутствует конвертация
Множество объектов Только главный объект Не настроен num_output_tokens

Вот типичный неправильный промпт, который вы найдёте в 90% туториалов:

# НЕ РАБОТАЕТ локально
prompt = "What objects are in this image?"

И вот промпт, который сработает:

# РАБОТАЕТ
prompt = "\n\n\n cat"

Видите разницу? Первый - это вопрос. Второй - это инструкция в специальном формате, которую модель понимает как запрос на генерацию bounding box'ов.

Рабочее решение: полный скрипт от первого запуска до визуализации

Хватит теории. Вот код, который гарантированно работает на свежей установке. Проверено на Ubuntu 22.04, Windows 11 WSL2, Python 3.10.

💡
Первое правило локального запуска моделей - всегда проверять совместимость версий библиотек. Особенно с transformers и torch.

2 Шаг 1: Установка с правильными версиями

Не используйте просто pip install transformers. Это путь к ошибкам совместимости. Вот рабочий набор:

# Создаём виртуальное окружение
python -m venv paligemma_env
source paligemma_env/bin/activate  # или paligemma_env\Scripts\activate на Windows

# Устанавливаем конкретные версии
pip install torch==2.3.1 torchvision==0.18.1 --index-url https://download.pytorch.org/whl/cu121
pip install transformers==4.42.4
pip install accelerate==0.31.0
pip install Pillow==10.3.0
pip install matplotlib==3.8.4

Почему именно эти версии? Потому что в transformers 4.43+ изменился API для мультимодальных моделей, а torch 2.4 может конфликтовать с CUDA 12.1. Если сталкиваетесь с ошибками при локальном запуске больших LLM - всегда начинайте с проверки версий.

3 Шаг 2: Полный рабочий скрипт детекции

Создайте файл paligemma_detection.py:

import torch
from transformers import PaliGemmaForConditionalGeneration, PaliGemmaProcessor
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
import re

# 1. Загрузка модели и процессора
print("Загружаем модель...")
model_id = "google/paligemma2-3b-pt-224"

# Используем bfloat16 для экономии памяти, но можно и float32
model = PaliGemmaForConditionalGeneration.from_pretrained(
    model_id,
    torch_dtype=torch.bfloat16,
    device_map="auto",
    revision="float16"  # Важно! Без этого будет ошибка загрузки
)

processor = PaliGemmaProcessor.from_pretrained(model_id)

# 2. Подготовка изображения
image_path = "your_image.jpg"  # замените на свой файл
raw_image = Image.open(image_path).convert("RGB")

# 3. Критически важный промпт
# Формат:  - это специальные токены для координат
# Четыре токена = [x_min, y_min, x_max, y_max] в нормализованных координатах
# После координат - метка класса
prompt = "\n\n\n person"

# 4. Обработка и инференс
inputs = processor(
    text=prompt,
    images=raw_image,
    return_tensors="pt",
    padding=True
).to(model.device)

# Генерируем больше токенов для нескольких объектов
with torch.no_grad():
    output = model.generate(
        **inputs,
        max_new_tokens=100,  # Увеличиваем для нескольких объектов
        do_sample=False,
        num_beams=1
    )

# 5. Декодирование результата
generated_text = processor.decode(output[0], skip_special_tokens=False)
print("Сырой вывод модели:", generated_text)

# 6. Парсинг bounding boxes из токенов
def parse_bbox_tokens(text):
    """Извлекает координаты из токенов вида """
    # Ищем последовательности токенов
    pattern = r''
    matches = re.findall(pattern, text)
    
    bboxes = []
    labels = []
    
    # Группируем по 4 токена + метка
    for i in range(0, len(matches), 4):
        if i + 3 >= len(matches):
            break
            
        # Конвертируем токены в нормализованные координаты [0, 1000]
        coords = [
            int(matches[i]) / 1000.0,
            int(matches[i+1]) / 1000.0,
            int(matches[i+2]) / 1000.0,
            int(matches[i+3]) / 1000.0
        ]
        
        # Ищем метку после координат
        # Простой парсинг - в реальном коде нужно сложнее
        bboxes.append(coords)
        labels.append("object")  # временная метка
    
    return bboxes, labels

bboxes, labels = parse_bbox_tokens(generated_text)
print(f"Найдено {len(bboxes)} объектов")
for i, (bbox, label) in enumerate(zip(bboxes, labels)):
    print(f"Объект {i}: {label} - bbox {bbox}")

# 7. Конвертация в пиксельные координаты и визуализация
def draw_bboxes(image, bboxes, labels):
    """Рисует bounding boxes на изображении"""
    draw = ImageDraw.Draw(image)
    width, height = image.size
    
    for bbox, label in zip(bboxes, labels):
        # Конвертируем нормализованные координаты в пиксельные
        x_min = bbox[0] * width
        y_min = bbox[1] * height
        x_max = bbox[2] * width
        y_max = bbox[3] * height
        
        # Рисуем прямоугольник
        draw.rectangle(
            [x_min, y_min, x_max, y_max],
            outline="red",
            width=3
        )
        # Подписываем
        draw.text(
            (x_min, y_min - 20),
            label,
            fill="red"
        )
    
    return image

# Визуализируем результат
if bboxes:
    result_image = draw_bboxes(raw_image.copy(), bboxes, labels)
    result_image.save("detection_result.jpg")
    print("Результат сохранён в detection_result.jpg")
else:
    print("Объекты не обнаружены. Попробуйте изменить промпт.")

Три самых частых косяка и как их избежать

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

1. Ошибка: "Token indices sequence length is longer than..."

Решение: Добавьте padding=True в вызов processor'а. И проверьте, что промпт начинается с "\n\n\n" - это не опечатка, а требование формата.

2. Ошибка: "CUDA out of memory"

Решение: Paligemma2-3b требует около 8GB VRAM в float16. Если не хватает:

# Вместо device_map="auto"
model = PaliGemmaForConditionalGeneration.from_pretrained(
    model_id,
    torch_dtype=torch.float32,  # или даже torch.float16
    device_map="cpu",  # или "cuda:0" если одна карта
    low_cpu_mem_usage=True,
    revision="float16"
)

Или используйте load_in_4bit=True с bitsandbytes, но это отдельная история с совместимостью библиотек.

3. Ошибка: Модель возвращает только один объект

Решение: Увеличьте max_new_tokens до 200-300. И измените промпт:

# Для детекции нескольких объектов
prompt = "Detect all objects: \n\n\n person  car"

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

Почему это происходит? Архитектурные особенности Paligemma2

Paligemma2 - не классический object detector. Это языковая модель с vision encoder. Она не предсказывает bounding boxes напрямую, а генерирует текстовое описание, в которое встроены специальные токены координат.

Вот как это работает под капотом:

  1. Vision encoder (SigLIP) преобразует изображение в патчи
  2. Языковая модель (Gemma) получает эти патчи + текстовый промпт
  3. Модель генерирует последовательность токенов
  4. Специальные токены <loc_XXXX> конвертируются в координаты

Проблема в том, что эта конвертация - отдельный пост-процессинг, который не встроен в стандартный пайплайн Hugging Face. Вот почему простой вызов pipeline() не работает.

Если вам нужен готовый object detector без танцев с бубном, посмотрите на мультимодальный краулер событий - там другой подход к локальному анализу видео.

Альтернативы: когда Paligemma2 - не лучший выбор

Несмотря на крутое название, Paligemma2 не всегда оптимален для локального детектирования. Вот когда стоит выбрать другую модель:

Задача Лучший выбор Почему
Real-time детекция с камеры YOLOv8, YOLOv10 В 50 раз быстрее, меньше памяти
Детекция + описание Grounding DINO + BLIP Точнее, проще в настройке
Мультимодальный поиск CLIP + ретрайвер Лучшее качество семантического поиска
Локальный анализ CCTV Custom YOLO + трекер Стабильнее, можно дообучить

Paligemma2 сильна в zero-shot сценариях, когда нужно детектировать объекты без обучения. Но если у вас фиксированный набор классов (люди, машины, лица) - берите традиционные детекторы.

Финальный совет: не верьте демкам, тестируйте локально

Hugging Face Spaces создаёт ложное ощущение простоты. Там работает, потому что:

  • Используются специальные wrapper'ы (Gradio, Streamlit)
  • Настроены правильные версии библиотек
  • Добавлен скрытый пост-процессинг
  • Иногда даже используется облачный инференс

Ваш локальный скрипт должен быть самодостаточным. Проверяйте каждую зависимость. Сохраняйте requirements.txt с конкретными версиями. И помните - если что-то работает в демке, но не работает у вас, это не ваша вина. Это недостаток документации.

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

А теперь запускайте скрипт. Если снова не работает - пишите в комментариях, разберём конкретно вашу ошибку. Потому что в мире локальных моделей универсальных решений не бывает. Только конкретные костыли для конкретных версий библиотек.