301 lines
16 KiB
Markdown
301 lines
16 KiB
Markdown
# Классификатор типов документов — задача 62625
|
||
|
||
Этот репозиторий — автономный тестовый прототип.
|
||
Он не импортирует код Vetro и не меняет текущий механизм загрузки. Сначала
|
||
модель необходимо обучить и измерить качество на документах, которых она не
|
||
видела.
|
||
|
||
## Текущее состояние модели
|
||
|
||
Текущая тестовая модель `request_document_types.pt` обучена распознавать только
|
||
четыре типа документов:
|
||
|
||
| Значение `RequestFileType` | Тип документа |
|
||
|---|---|
|
||
| `referral_for_repairs` | Направление на ремонт |
|
||
| `inspection_act` | Акт осмотра |
|
||
| `cc_approval` | Согласование страхового случая |
|
||
| `inspection_photo` | Фото осмотра |
|
||
|
||
Документ другого типа модель пока не сможет корректно определить: она выберет
|
||
наиболее похожий из этих четырёх классов либо вернёт `unknown`, если
|
||
уверенность ниже установленного порога.
|
||
|
||
### Полученные результаты распознавания
|
||
|
||
Порог принятия результата — 0.70. Все четыре проверенных документа были
|
||
приняты моделью:
|
||
|
||
| Файл | Распознанный тип | Уверенность | Следующие варианты |
|
||
|---|---|---:|---|
|
||
| `1.pdf` | `referral_for_repairs` | 99.6794% | `cc_approval` — 0.2871%, `inspection_act` — 0.0221% |
|
||
| `2.pdf` | `inspection_act` | 97.1619% | `referral_for_repairs` — 2.2419%, `cc_approval` — 0.3883% |
|
||
| `3.png` | `cc_approval` | 92.1763% | `referral_for_repairs` — 7.6405%, `inspection_act` — 0.1245% |
|
||
| `4.jpg` | `inspection_photo` | 99.7211% | `inspection_act` — 0.2390%, `referral_for_repairs` — 0.0324% |
|
||
|
||
Каждый файл содержал одну страницу. Эти результаты подтверждают, что запуск
|
||
инференса работает и модель уверенно классифицировала четыре конкретных
|
||
примера. Они не являются оценкой общей точности модели: для такой оценки
|
||
нужно выполнить команду `evaluate` на независимом `test_dataset`, который не
|
||
использовался при обучении.
|
||
|
||
## 1. Подготовка окружения
|
||
|
||
Для Windows рекомендуется Python 3.12 (64-bit) и отдельное виртуальное
|
||
окружение. PyTorch для Windows официально поддерживает Python до версии 3.12.
|
||
Если Python ещё не установлен, используйте Python 3.12.10 — это последняя
|
||
версия ветки 3.12 с готовым Windows installer.
|
||
|
||
```powershell
|
||
python -m venv .venv-document-classifier
|
||
.\.venv-document-classifier\Scripts\Activate.ps1
|
||
python -m pip install --upgrade pip
|
||
python -m pip install -r requirements.txt
|
||
```
|
||
|
||
### Если существующее окружение перестало запускаться
|
||
|
||
Файл `.venv-document-classifier\pyvenv.cfg` содержит путь к Python, на основе
|
||
которого создавалось окружение. Если этот Python был удалён или перемещён,
|
||
появляется ошибка `Unable to create process`. Такое окружение нужно создать
|
||
заново после установки Python 3.12:
|
||
|
||
```powershell
|
||
deactivate
|
||
Rename-Item .venv-document-classifier .venv-document-classifier-old
|
||
py -3.12 -m venv .venv-document-classifier
|
||
.\.venv-document-classifier\Scripts\Activate.ps1
|
||
python -m pip install --upgrade pip
|
||
python -m pip install -r requirements.txt
|
||
```
|
||
|
||
Старый каталог оставлен как резервная копия. После успешной проверки новой
|
||
среды его можно удалить вручную.
|
||
|
||
В `requirements.txt` зафиксированы совместимые версии PyTorch и torchvision
|
||
с CUDA 12.8 и официальный индекс `cu128`. Поэтому все зависимости устанавливаются
|
||
одной командой. Обычный `pip install torch torchvision` без CUDA-индекса может
|
||
установить CPU-сборку, которая не умеет работать с RTX 3060 Ti.
|
||
|
||
Если CPU-сборка уже установлена, исправьте текущее активное окружение:
|
||
|
||
```powershell
|
||
python -m pip uninstall -y torch torchvision
|
||
python -m pip install --no-cache-dir -r requirements.txt
|
||
```
|
||
|
||
Проверьте, что PyTorch видит видеокарту:
|
||
|
||
```powershell
|
||
python -c "import torch; ok=torch.cuda.is_available(); print('torch:',torch.__version__,'cuda build:',torch.version.cuda,'available:',ok,'GPU:',torch.cuda.get_device_name(0) if ok else 'нет')"
|
||
```
|
||
|
||
Ожидаемый результат на текущем ПК: `True` и `NVIDIA GeForce RTX 3060 Ti`.
|
||
Если выводится `False`, установлена CPU-сборка PyTorch — до обучения нужно
|
||
поставить CUDA-сборку командой выше.
|
||
|
||
Для первого запуска обучения нужен интернет: torchvision скачает
|
||
предобученные ImageNet-веса MobileNetV3. Если интернета нет, можно добавить
|
||
`--no-pretrained`, но качество обычно будет ниже.
|
||
|
||
## 2. Подготовка датасета
|
||
|
||
Создайте один каталог на каждый тип. Имя каталога должно быть **точным
|
||
строковым значением** `RequestFileType` из Vetro, а не русским заголовком:
|
||
|
||
```text
|
||
dataset/
|
||
├── referral_for_repairs/
|
||
│ ├── direction_0001.pdf
|
||
│ └── direction_0002.jpg
|
||
├── inspection_act/
|
||
│ ├── act_0001.pdf
|
||
│ └── act_0002.png
|
||
└── check/
|
||
├── invoice_0001.pdf
|
||
└── invoice_0002.pdf
|
||
```
|
||
|
||
Поддерживаются PDF, PNG, JPG/JPEG, BMP, TIFF и WEBP. DOC/DOCX/XLS следует
|
||
заранее преобразовать в PDF. Один многостраничный PDF считается одним
|
||
документом: все его страницы попадут только в train или только в validation.
|
||
|
||
Практические требования к данным:
|
||
|
||
- минимум — 2 документа на тип, разумный старт — 50–100;
|
||
- лучше 200+ документов на каждый часто встречающийся тип;
|
||
- не складывайте копии одного документа под разными именами;
|
||
- включите сканы и фото разного качества, документы разных страховых компаний;
|
||
- номера дел, ФИО и прочие персональные данные должны храниться только в
|
||
защищённой инфраструктуре; этот скрипт никуда их не отправляет;
|
||
- число документов по типам желательно выровнять. В коде есть компенсация
|
||
дисбаланса классов, но она не заменяет реальные примеры.
|
||
|
||
Отложите отдельный каталог `test_dataset` (обычно 10–20% исходных документов)
|
||
**до обучения**. Он должен иметь ту же структуру и не должен пересекаться с
|
||
`dataset`. Встроенный validation нужен для выбора эпохи, а `test_dataset` —
|
||
для честной финальной оценки.
|
||
|
||
## 3. Обучение
|
||
|
||
```powershell
|
||
python document_type_classifier.py train `
|
||
--data .\dataset `
|
||
--model .\models\request_document_types.pt `
|
||
--device cuda `
|
||
--epochs 25 `
|
||
--batch-size 64 `
|
||
--workers 0 `
|
||
--log-every 10
|
||
```
|
||
|
||
Эти значения подобраны для текущего компьютера:
|
||
|
||
- AMD Ryzen 5 7500F: 6 ядер / 12 потоков;
|
||
- 32 ГБ оперативной памяти;
|
||
- NVIDIA GeForce RTX 3060 Ti, 8 ГБ видеопамяти.
|
||
|
||
По умолчанию уже используются `batch-size=64`, `workers=0`, размер изображения
|
||
224×224 и mixed precision. В памяти хранится только индекс путей и страниц;
|
||
каждое изображение декодируется непосредственно перед своим batch. Это не
|
||
позволяет фотографиям высокого разрешения занять всю оперативную память перед
|
||
первой эпохой. Явные параметры в примере оставлены для воспроизводимости. Если
|
||
появляется `CUDA out of memory`, сначала установите
|
||
`--batch-size 32`, затем 16. Если CUDA работает нестабильно, добавьте
|
||
`--no-amp`; для принудительного CPU используйте `--device cpu --batch-size 16
|
||
--workers 0`.
|
||
|
||
Во время запуска логируются сканирование и состав датасета, чтение каждых 25
|
||
документов, загрузка модели, каждый 10-й batch, validation, время эпохи и
|
||
примерное оставшееся время. Частоту можно изменить параметрами
|
||
`--dataset-log-every` и `--log-every`.
|
||
|
||
Скрипт печатает `val_accuracy` после каждой эпохи, сохраняет только лучшую
|
||
модель и останавливается после пяти эпох без улучшения.
|
||
|
||
Реальные JPEG из Vetro могут содержать неполный последний блок или
|
||
нестандартную структуру, хотя нормально открываются браузером. Для таких
|
||
файлов включён tolerant-режим Pillow: доступная часть изображения участвует в
|
||
обучении. Сообщение `Пропуск поврежденного файла` теперь остаётся только для
|
||
файлов, которые действительно невозможно декодировать.
|
||
|
||
## 4. Честная проверка
|
||
|
||
```powershell
|
||
python document_type_classifier.py evaluate `
|
||
--model .\models\request_document_types.pt `
|
||
--data .\test_dataset
|
||
```
|
||
|
||
В отчёте:
|
||
|
||
- `accuracy_with_unknown_as_error` — доля всех верных ответов;
|
||
- `accepted_share` — доля документов, для которых уверенность не ниже порога;
|
||
- `by_expected_type` — ошибки по каждому реальному типу.
|
||
|
||
Для автозагрузки недостаточно одной общей accuracy. Проверьте каждый тип и
|
||
особенно пары, которые модель путает. Рекомендуемый критерий пилота: не менее
|
||
95% точности среди принятых ответов; остальные файлы должны оставаться на
|
||
ручном выборе типа.
|
||
|
||
Порог можно сделать строже:
|
||
|
||
```powershell
|
||
python document_type_classifier.py evaluate `
|
||
--model .\models\request_document_types.pt `
|
||
--data .\test_dataset `
|
||
--threshold 0.85
|
||
```
|
||
|
||
## 5. Распознавание
|
||
|
||
Один документ:
|
||
|
||
```powershell
|
||
python document_type_classifier.py predict `
|
||
--model .\models\request_document_types.pt `
|
||
--file .\document.pdf
|
||
```
|
||
|
||
Каталог документов:
|
||
|
||
```powershell
|
||
python document_type_classifier.py predict `
|
||
--model .\models\request_document_types.pt `
|
||
--file .\incoming
|
||
```
|
||
|
||
На каждый файл выводится одна JSON-строка:
|
||
|
||
```json
|
||
{"file":"document.pdf","document_type":"inspection_act","confidence":0.9631,"accepted":true,"pages":2,"alternatives":[...]}
|
||
```
|
||
|
||
Если уверенность ниже 0.70, `document_type` будет `unknown` и
|
||
`accepted=false`. При интеграции такой документ нужно показывать пользователю
|
||
для ручного выбора. `alternatives` полезны для интерфейса и анализа ошибок.
|
||
|
||
Все команды следует выполнять из корня репозитория.
|
||
|
||
## 6. Возможная интеграция после завершения тестового пилота
|
||
|
||
1. Загрузить модель один раз при старте отдельного worker/API-сервиса, а не
|
||
заново для каждого HTTP-запроса.
|
||
2. После получения временного файла вызвать классификацию до
|
||
`document.upload_file(...)`.
|
||
3. Проверить, что предсказанная строка входит в `RequestFileType`.
|
||
4. При `accepted=true` передать её как `filetype`; при `false` сохранить
|
||
текущий ручной выбор.
|
||
5. Логировать ожидаемый/исправленный пользователем тип и confidence. Эти
|
||
исправления формируют следующую версию датасета.
|
||
6. Версионировать файл модели и иметь возможность мгновенно выключить
|
||
автоклассификацию конфигурационным флагом.
|
||
|
||
Не рекомендуется запускать тяжёлый инференс синхронно внутри нескольких
|
||
web-процессов без замера времени и памяти. Для промышленной интеграции лучше
|
||
обернуть классификатор внутренним API или фоновой задачей и ограничить размер
|
||
и число страниц входного файла.
|
||
|
||
## 7. Скачивание документов через авторизованный браузер
|
||
|
||
Создайте, например, `links.txt`, указав одну HTTP/HTTPS-ссылку на строку:
|
||
|
||
```text
|
||
https://example.org/document-1.pdf
|
||
https://example.org/document-2.jpg
|
||
```
|
||
|
||
Скрипт не скачивает файлы HTTP-клиентом Python. Он последовательно передаёт
|
||
ссылки системному браузеру, поэтому используется уже существующая браузерная
|
||
авторизация, а имя и расширение файла определяет сам сервер.
|
||
|
||
Перед запуском откройте настройки загрузок Chrome/Edge:
|
||
|
||
1. Установите каталог загрузки:
|
||
`.\downloads`.
|
||
2. Отключите запрос места сохранения для каждого файла.
|
||
3. Разрешите сайту автоматическое скачивание нескольких файлов, если браузер
|
||
покажет соответствующий запрос.
|
||
|
||
Затем запустите:
|
||
|
||
```powershell
|
||
python download_files.py links.txt
|
||
```
|
||
|
||
Пустые строки и строки, начинающиеся с `#`, игнорируются. Между ссылками по
|
||
умолчанию выдерживается пауза 1.5 секунды. Для пробного запуска первых десяти:
|
||
|
||
```powershell
|
||
python download_files.py links.txt --limit 10
|
||
```
|
||
|
||
Если выполнение было прервано после 350-й ссылки:
|
||
|
||
```powershell
|
||
python download_files.py links.txt --start 351
|
||
```
|
||
|
||
Пауза настраивается параметром `--delay`, например `--delay 3`. Скрипт не
|
||
переименовывает файлы: браузер сохраняет оригинальное имя из ответа сервера.
|