Spaces:
Running
on
Zero
Running
on
Zero
feat: Automatically set generation config's `pad_token_id` and `eos_token_id` from the tokenizer and suppress Hugging Face logging warnings.
Browse files
README.md
CHANGED
|
@@ -11,25 +11,107 @@ license: apache-2.0
|
|
| 11 |
|
| 12 |
# DeepSeek-OCR-2 & MedGemma-1.5 Multimodal Analysis
|
| 13 |
|
| 14 |
-
|
| 15 |
|
| 16 |
-
## 🚀
|
| 17 |
|
| 18 |
-
-
|
| 19 |
-
- **
|
| 20 |
-
-
|
| 21 |
-
-
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
##
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
| 28 |
|
| 29 |
-
##
|
| 30 |
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# DeepSeek-OCR-2 & MedGemma-1.5 Multimodal Analysis
|
| 13 |
|
| 14 |
+
Локальна демонстрація OCR та мультимодального аналізу, що стикає дві найсучасніші моделі Hugging Face: **deepseek-ai/DeepSeek-OCR-2** для загального OCR і **google/medgemma-1.5-4b-it** для медичних зображень.
|
| 15 |
|
| 16 |
+
## 🚀 Що тут є
|
| 17 |
|
| 18 |
+
- **Гнучкий Gradio-інтерфейс**: підтримка завантаження PDF та зображень, вибір моделі, додатковий промпт, клавіші «очистити», «запустити», «зберегти в файл» і автоматичне очікування відповідей.
|
| 19 |
+
- **ZeroGPU-готовність**: `app_hf.py` оптимізовано для Hugging Face Spaces з переходом моделей на GPU тільки на час inference.
|
| 20 |
+
- **Локальна версія з MPS**: `app.py` підтримує запуск на Python + Metal (Mac) без ZeroGPU.
|
| 21 |
+
- **Модулі для порівняння**: `compare_models.py`, `test_real_docs.py` та інші демонструють, як застосувати моделі скриптово.
|
| 22 |
+
- **Підтримка PDF**: усереднювання через PyMuPDF (`fitz`) і перетворення кожної сторінки в зображення для подальшого аналізу.
|
| 23 |
+
- **Збереження результатів**: усі результати пишуться до `outputs/ocr_result_<timestamp>.txt`, а окремі структури для відлагодження (`outputs/`, `ocr_results*`, `temp_comp.png`).
|
| 24 |
|
| 25 |
+
## 🧠 Архітектура
|
| 26 |
|
| 27 |
+
1. **ModelManager** (`app_hf.py`/`app.py`) кешує завантаження під різні моделі, налаштовує `pad_token_id`, `eos_token_id` та відповідні `generation_config`.
|
| 28 |
+
2. Вхід — зображення або PDF (через `gr.Image` чи `gr.File`). Якщо це PDF, кожна сторінка рендериться через `fitz.Matrix(2, 2)`.
|
| 29 |
+
3. DeepSeek запускається через метод `model.infer()` з файлами з диску. MedGemma — через `processor.apply_chat_template` + `model.generate()`.
|
| 30 |
+
4. Вивід накопичується в пам’яті і показується в текстовому полі Gradio, кастомний промпт передається до обох моделей.
|
| 31 |
|
| 32 |
+
## 🧰 Потрібне середовище
|
| 33 |
|
| 34 |
+
- Python 3.10+ (рекомендується 3.10/3.11 через сумісність із `transformers`).
|
| 35 |
+
- CUDA 11+ та GPU, якщо хочете запускати DeepSeek/MedGemma локально на CUDA; Mac з MPS теж підтримується.
|
| 36 |
+
- `hf_token` (особливо для MedGemma) з повноваженнями моделі.
|
| 37 |
+
- Залежності: `pip install -r requirements.txt`.
|
| 38 |
|
| 39 |
+
## ⚙️ Налаштування
|
| 40 |
+
|
| 41 |
+
### 1. Загальні кроки
|
| 42 |
+
|
| 43 |
+
```bash
|
| 44 |
+
git clone https://github.com/deepseek-ai/DeepSeek-OCR-2.git
|
| 45 |
+
cd DeepSeek-OCR-2
|
| 46 |
+
python -m venv venv # або ваш улюблений venv
|
| 47 |
+
source venv/bin/activate
|
| 48 |
+
pip install -r requirements.txt
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
### 2. Для Hugging Face Spaces (ZeroGPU)
|
| 52 |
+
|
| 53 |
+
1. У Settings ➜ Variables and secrets додайте `HF_TOKEN=<ваш токен>` (потрібен для `google/medgemma-1.5-4b-it`).
|
| 54 |
+
2. Переконайтеся, що `app_hf.py` використовується як `app_file` (вказано у metadata).
|
| 55 |
+
3. Spaces автоматично запускає `demo.queue().launch()`; GPU буде використовуватися тільки в `@spaces.GPU`.
|
| 56 |
+
4. У критичних системах з нульовим GPU подбайте про `TRANSFORMERS_CACHE`, `HF_HOME`, `HF_HUB_CACHE` — вони вже прив’язані до `~/.cache/huggingface` або `/data/.huggingface`.
|
| 57 |
+
|
| 58 |
+
### 3. Локальний запуск
|
| 59 |
+
|
| 60 |
+
```bash
|
| 61 |
+
source venv/bin/activate
|
| 62 |
+
python app.py # Створює інтерфейс Gradio з підтримкою MPS/CUDA
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
- Для MPS (Mac) переконайтеся, що `torch.backends.mps.is_available()` повертає `True`; після завершення скрипт викликає `torch.mps.empty_cache()`.
|
| 66 |
+
- При запуску на CUDA рекомендується додати перемінну `TRANSFORMERS_VERBOSITY=info` для діагностики та вивчати журнали.
|
| 67 |
+
|
| 68 |
+
## 🎮 Як користуватись
|
| 69 |
+
|
| 70 |
+
1. У веб-інтерфейсі завантажте зображення (`.png`, `.jpg`, `.jpeg`) або PDF.
|
| 71 |
+
2. Оберіть модель: DeepSeek для OCR або MedGemma для медичних сценаріїв.
|
| 72 |
+
3. За потреби напишіть **користувацький промпт** (див. `prompt_input`).
|
| 73 |
+
4. Натисніть **«Запустити аналіз»**.
|
| 74 |
+
5. Результат з’явиться у текстовому полі; можна зберегти кнопкою 💾 або завантажити файл.
|
| 75 |
+
6. «Очистити» скидає усі поля.
|
| 76 |
+
|
| 77 |
+
## 💾 Результати і структура виводу
|
| 78 |
+
|
| 79 |
+
- `outputs/ocr_result_<YYYYMMDD_HHMMSS>.txt`: основний текстовий файл із результатами (згенеровано через `save_result_to_file`).
|
| 80 |
+
- `ocr_results/` та `ocr_results_pdf12/`: історичні папки для тестових документів.
|
| 81 |
+
- `temp_comp.png`: проміжні зображення, які створюються під час розробки.
|
| 82 |
+
- `ocr_results_package.zip`: приклад пакетованих результатів.
|
| 83 |
+
|
| 84 |
+
## 🧪 Тестування та сценарії
|
| 85 |
+
|
| 86 |
+
- `test_inference.py`, `test_real_docs.py`, `test_minimal.py`, `test_medgemma.py` — приклади запуску обох моделей без Gradio.
|
| 87 |
+
- `compare_models.py` — бенчмарк порівняння DeepSeek і MedGemma.
|
| 88 |
+
- `ocr_full_pdf12.py` — повна обробка PDF довжиною до 12 сторінок.
|
| 89 |
+
- `generate_test_image.py` — генерація синтетичного зображення для швидких перевірок.
|
| 90 |
+
- Запускайте тести через `python test_real_docs.py` тощо, переконайтесь, що моделі завантажені і в кеші.
|
| 91 |
+
|
| 92 |
+
## 🛠️ Поширені проблеми
|
| 93 |
+
|
| 94 |
+
- `The following generation flags are not valid... temperature`: виникає при передачі параметрів із CLI – лог зараз приглушений.
|
| 95 |
+
- `attention mask and pad token id were not set`: переконайтеся, що `pad_token_id`/`eos_token_id` встановлені в токенізаторі й `generation_config`.
|
| 96 |
+
- `MedGemma` вимагає `HF_TOKEN` із доступом до моделі — без нього завантаження не пройде.
|
| 97 |
+
- `CUDA` або `MPS` може бути відсутній: `run_ocr` автоматично використовує `contextlib.nullcontext` і не вимагає GPU.
|
| 98 |
+
- Якщо бачите `torch.cuda.empty_cache()` або `gc.collect()` у логах — це нормальна очистка ZeroGPU-ресурсів.
|
| 99 |
+
|
| 100 |
+
## 🗂️ Структура проекту
|
| 101 |
+
|
| 102 |
+
- `app_hf.py`: Gradio demo для Hugging Face Spaces + ZeroGPU, GPU-секція охоплена `spaces.GPU`.
|
| 103 |
+
- `app.py`: локальний Gradio-інтерфейс із підтримкою MPS.
|
| 104 |
+
- `compare_models.py`: скрипт порівняння.
|
| 105 |
+
- `convert_docs.py`, `convert_full_pdf.py`: конвертери та допоміжні утиліти.
|
| 106 |
+
- `requirements.txt`: основні залежності (`transformers`, `gradio`, `torch`, `Pillow`, `PyMuPDF`, `huggingface_hub`).
|
| 107 |
+
- `doc_images`, `doc_for_testing`: набір тестових файлів.
|
| 108 |
+
|
| 109 |
+
## 🤝 Контрибуція
|
| 110 |
+
|
| 111 |
+
1. Створіть issue з описом завдання/помилки.
|
| 112 |
+
2. Відгалужте репозиторій, реалізуйте зміни, напишіть тести/опис.
|
| 113 |
+
3. Відкрийте PR із обґрунтуванням впливу.
|
| 114 |
+
|
| 115 |
+
## 📜 Ліцензія
|
| 116 |
+
|
| 117 |
+
Проект поширюється під ліцензією Apache-2.0. Деталі див. у `LICENSE`.
|
app_hf.py
CHANGED
|
@@ -11,6 +11,7 @@ except ImportError:
|
|
| 11 |
|
| 12 |
import gradio as gr
|
| 13 |
from transformers import AutoModel, AutoTokenizer, AutoProcessor, AutoModelForImageTextToText
|
|
|
|
| 14 |
import torch
|
| 15 |
import os
|
| 16 |
from PIL import Image
|
|
@@ -49,6 +50,7 @@ warnings.filterwarnings("ignore", message="CUDA is not available or torch_xla is
|
|
| 49 |
warnings.filterwarnings("ignore", message="The following generation flags are not valid and may be ignored")
|
| 50 |
warnings.filterwarnings("ignore", message="The attention mask and the pad token id were not set")
|
| 51 |
warnings.filterwarnings("ignore", message="You are using a model of type .* to instantiate a model of type .*")
|
|
|
|
| 52 |
|
| 53 |
# --- Configuration ---
|
| 54 |
DEEPSEEK_MODEL = 'deepseek-ai/DeepSeek-OCR-2'
|
|
@@ -104,6 +106,11 @@ class ModelManager:
|
|
| 104 |
)
|
| 105 |
if hasattr(model, "config") and getattr(model.config, "pad_token_id", None) is None and getattr(tokenizer, "pad_token_id", None) is not None:
|
| 106 |
model.config.pad_token_id = tokenizer.pad_token_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
model.eval()
|
| 108 |
self.models[model_name] = model
|
| 109 |
self.processors[model_name] = tokenizer
|
|
@@ -120,6 +127,11 @@ class ModelManager:
|
|
| 120 |
# Ensure pad_token_id is set
|
| 121 |
if processor.tokenizer.pad_token_id is None:
|
| 122 |
processor.tokenizer.pad_token_id = processor.tokenizer.eos_token_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
self.models[model_name] = model
|
| 124 |
self.processors[model_name] = processor
|
| 125 |
|
|
|
|
| 11 |
|
| 12 |
import gradio as gr
|
| 13 |
from transformers import AutoModel, AutoTokenizer, AutoProcessor, AutoModelForImageTextToText
|
| 14 |
+
from transformers import logging as hf_logging
|
| 15 |
import torch
|
| 16 |
import os
|
| 17 |
from PIL import Image
|
|
|
|
| 50 |
warnings.filterwarnings("ignore", message="The following generation flags are not valid and may be ignored")
|
| 51 |
warnings.filterwarnings("ignore", message="The attention mask and the pad token id were not set")
|
| 52 |
warnings.filterwarnings("ignore", message="You are using a model of type .* to instantiate a model of type .*")
|
| 53 |
+
hf_logging.set_verbosity_error()
|
| 54 |
|
| 55 |
# --- Configuration ---
|
| 56 |
DEEPSEEK_MODEL = 'deepseek-ai/DeepSeek-OCR-2'
|
|
|
|
| 106 |
)
|
| 107 |
if hasattr(model, "config") and getattr(model.config, "pad_token_id", None) is None and getattr(tokenizer, "pad_token_id", None) is not None:
|
| 108 |
model.config.pad_token_id = tokenizer.pad_token_id
|
| 109 |
+
if hasattr(model, "generation_config"):
|
| 110 |
+
if getattr(model.generation_config, "pad_token_id", None) is None and getattr(tokenizer, "pad_token_id", None) is not None:
|
| 111 |
+
model.generation_config.pad_token_id = tokenizer.pad_token_id
|
| 112 |
+
if getattr(model.generation_config, "eos_token_id", None) is None and getattr(tokenizer, "eos_token_id", None) is not None:
|
| 113 |
+
model.generation_config.eos_token_id = tokenizer.eos_token_id
|
| 114 |
model.eval()
|
| 115 |
self.models[model_name] = model
|
| 116 |
self.processors[model_name] = tokenizer
|
|
|
|
| 127 |
# Ensure pad_token_id is set
|
| 128 |
if processor.tokenizer.pad_token_id is None:
|
| 129 |
processor.tokenizer.pad_token_id = processor.tokenizer.eos_token_id
|
| 130 |
+
if hasattr(model, "generation_config"):
|
| 131 |
+
if getattr(model.generation_config, "pad_token_id", None) is None:
|
| 132 |
+
model.generation_config.pad_token_id = processor.tokenizer.pad_token_id
|
| 133 |
+
if getattr(model.generation_config, "eos_token_id", None) is None:
|
| 134 |
+
model.generation_config.eos_token_id = processor.tokenizer.eos_token_id
|
| 135 |
self.models[model_name] = model
|
| 136 |
self.processors[model_name] = processor
|
| 137 |
|