- BrainTools - https://www.braintools.ru -
Недавно получил задачу сделать автоматизированную оцифровку характеристик из паспортов товаров в БД, а не изменение параметров вручную в ERP. Я подумал, было бы здорово поделиться, как я это сделал, с вами на Хабре!
Базовые задачи:
Нужно, чтобы это все работало локально
Система должна принимать разные форматы (.doc, .pdf, .png)
Возможность создавать динамические таблицы, куда ИИ будет заполнять сама информацию, а не хардкодить для каждой категории паспорта свои отчеты
Желательно, чтобы все работало на одной видеокарте (в моем случае 3090 на 24GB VRAM)
Для реализации задачи, я решил развернуть 2 ИИ модели — deepseek-ocr-7b и Qwen-1.5. Первая будет считывать текст из изображений, а вторая преобразовывать сырой текст в json, который, затем, будет распределяться по полям таблицы.
Скачать весь проект вы можете тут — https://github.com/Chashchin-Dmitry/llm-ocr-handmade [1]
Для начала, нам нужно установить драйвер NVIDIA. В моем случае, это 576.66 серия, но если у вас другая видеокарта, то введите свою модель и серию тут [2].
Перезагружаем комп/сервер и пишем команду:
nvidia-smi
Если команда показала что-то с Nvidia и номер модели, значит мы удачно скачали драйверы.
Windows:
-Docker Desktop + включить WSL2 backend
-В настройках Docker Desktop включить “Use WSL 2 based engine”
Linux:
# NVIDIA Container Toolkit
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo systemctl restart docker
docker run --rm --gpus all nvidia/cuda:12.1-base nvidia-smi
Должна показать твою видеокарту.
Очень важно подобрать под себя верную версию vLLM под вашу видеокарту. Немаловажно, она должна быть совместима с вашей версией драйвера.
Для DeepSeek-OCR и Qwen-1.5, я выбрал vLLM v0.11.2 с CUDA 12.9 (с версией ниже, у вас ничего не запустится)
# Скачивание образа (43GB!)
docker pull vllm/vllm-openai:v0.11.2
Подробнее, как подобрать нужную версию vLLM под вашу GPU можно тут [3].
Мне понравилось отзывы от новой deepseek-ocr, а еще она очень компактная, поэтому, я решил, что она идеально подойдет к моему кейсу. Вот ее требования:
DeepSeek-OCR:
Модель: deepseek-ai/DeepSeek-OCR
Размер: 3B параметров
VRAM: ~8-10GB
Задача: Извлечение текста из изображений
Моя задача состоит развернуть все разом на одной видеокарте, поэтому я выбирал самые маленькие LLM, которые только есть. Более того, мне надо сырой текст преобразовывать в json, с чем микро модели будут справляться идеально и, самое главное, быстро.
Вот какие могут быть варианты для вас:
|
Модель |
Параметры |
VRAM |
Качество |
|---|---|---|---|
|
Qwen2.5-1.5B-Instruct |
1.5B |
~5GB |
Базовое |
|
Qwen2.5-3B-Instruct |
3B |
~8GB |
Хорошее |
|
Qwen2.5-7B-Instruct |
7B |
~16GB |
Отличное |
Мы выбрали 1.5B — влезает вместе с OCR на 24GB и достаточен для структуризации JSON.
version: '3.8'
services:
# DeepSeek-OCR
vllm-ocr:
image: vllm/vllm-openai:v0.11.2
container_name: vllm-ocr
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
ports:
- "8001:8000"
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
command: >
--model deepseek-ai/DeepSeek-OCR
--trust-remote-code
--max-model-len 4096
--gpu-memory-utilization 0.30
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
# Qwen для структуризации
vllm-qwen:
image: vllm/vllm-openai:v0.11.2
container_name: vllm-qwen
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
ports:
- "8002:8000"
volumes:
- ~/.cache/huggingface:/root/.cache/huggingface
command: >
--model Qwen/Qwen2.5-1.5B-Instruct
--trust-remote-code
--max-model-len 16384
--gpu-memory-utilization 0.35
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
# FastAPI Backend
backend:
build: .
container_name: ocr-backend
ports:
- "8000:8000"
volumes:
- ./backend:/app/backend
- ./frontend:/app/frontend
- ./uploads:/app/uploads
environment:
- DATABASE_URL=mysql+pymysql://root:password@host.docker.internal:3306/ocr_documents
- VLLM_OCR_URL=http://vllm-ocr:8000
- VLLM_QWEN_URL=http://vllm-qwen:8000
extra_hosts:
- "host.docker.internal:host-gateway"
command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload
|
Параметр |
Значение |
Зачем |
|---|---|---|
|
|
0.30 / 0.35 |
Делим GPU между моделями |
|
|
4096 / 16384 |
Максимальный контекст |
|
|
– |
Для кастомного кода моделей |
Важнейший нюанс! DeepSeek-OCR использует особый формат промпта, не стандартный OpenAI.
messages = [
{"role": "system", "content": "You are OCR assistant"},
{"role": "user", "content": "Extract text from image"}
]
messages = [
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{image_base64}"}
},
{
"type": "text",
"text": "<|grounding|>Convert the document to markdown."
}
]
}
]
|
Промпт |
Результат |
|---|---|
|
|
Текст + координаты элементов |
|
|
Только текст |
Потенциальная проблема, которая у вас может возникнуть — “max_tokens is too large”
DeepSeek-OCR имеет контекст 4096 токенов. Если запросить max_tokens=4096, а input занимает 100 токенов — ошибка [4].
Решение:
# OCR Service
payload = {
"model": "deepseek-ai/DeepSeek-OCR",
"max_tokens": 3500, # Оставляем запас для input
"temperature": 0.0,
...
}
# Qwen Structurizer
payload = {
"model": "Qwen/Qwen2.5-1.5B-Instruct",
"max_tokens": 1024, # Достаточно для JSON
"temperature": 0.1,
...
}
Проблема: OCR возвращает 8000+ токенов из многостраничного PDF, а Qwen имеет контекст 2048.
Решение: Увеличить --max-model-len в docker-compose:
vllm-qwen:
command: >
--model Qwen/Qwen2.5-1.5B-Instruct
--max-model-len 16384 # Было 2048
--gpu-memory-utilization 0.35 # Увеличили с 0.20
Важно: Больший контекст = больше VRAM. На 24GB влезает 16K контекст для 1.5B модели.
STRUCTURING_SYSTEM_PROMPT = """Ты - эксперт по структуризации данных из документов.
Твоя задача - извлечь конкретные данные из текста документа и вернуть их в JSON формате.
Правила:
1. Извлекай ТОЛЬКО те поля, которые указаны в схеме
2. Если поле не найдено в документе - укажи null
3. Сохраняй форматирование дат и чисел как в оригинале
4. Не выдумывай данные - только то, что есть в документе
5. Возвращай ТОЛЬКО валидный JSON, без пояснений"""
def build_structuring_prompt(columns, ocr_text):
fields = "n".join([f'- "{col["name"]}": {col["description"]}' for col in columns])
return f"""Извлеки данные из документа по следующей схеме:
ПОЛЯ ДЛЯ ИЗВЛЕЧЕНИЯ:
{fields}
ТЕКСТ ДОКУМЕНТА:
"""
{ocr_text}
"""
Верни JSON. Если поле не найдено - укажи null."""
# 1. Запуск моделей (первый раз скачивает веса ~10GB)
docker-compose up -d vllm-ocr vllm-qwen
# 2. Ожидание загрузки (5-10 минут)
docker logs vllm-ocr -f
# Ждём: "Uvicorn running on http://0.0.0.0:8000"
# 3. Проверка моделей
curl http://localhost:8001/v1/models # OCR
curl http://localhost:8002/v1/models # Qwen
# 4. Запуск backend
docker-compose up -d backend
# 5. Открыть UI
# http://localhost:8000
Пора зайти и посмотреть что получилось. Сразу скажу — весь фронт я навайбкодил, потому не ругайтесь на английский язык.
Уже неплохо. Можно создать свою схему-таблицу и настроить в ней колонки. Давайте это и сделаем.
Добавим поля-колонки
Вот так выглядит настроенная схема
Отлично, теперь давайте запустим первый PDF файл. В качестве примера, я зашел по первой ссылке в Яндекс Маркет и распечатал характеристики ведра
Примерно такой результат должна выдать программа (у меня не сохранился оригинал оцифровки ведра выше).
По моему очень даже неплохой результат для такого серьезного кейса. А главное, работает все локально без запросов в облако.
Делитесь своим опытом [5] настройки моего контейнера.
Буду рад за лайк и подписку на канал :) https://t.me/notes_from_cto [6]
Автор: Dmitrii-Chashchin
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/23069
URLs in this post:
[1] https://github.com/Chashchin-Dmitry/llm-ocr-handmade: https://github.com/Chashchin-Dmitry/llm-ocr-handmade
[2] тут: https://www.nvidia.com/en-us/drivers/
[3] тут: https://github.com/deepseek-ai/DeepSeek-OCR
[4] ошибка: http://www.braintools.ru/article/4192
[5] опытом: http://www.braintools.ru/article/6952
[6] https://t.me/notes_from_cto: https://t.me/notes_from_cto
[7] Источник: https://habr.com/ru/articles/975824/?utm_source=habrahabr&utm_medium=rss&utm_campaign=975824
Нажмите здесь для печати.