Библиотека машинного обучения для автоматического детектирования и идентификации морских млекопитающих (китов и дельфинов) по снимкам аэрофотосъёмки. Система применяет метрическое обучение на основе ArcFace для идентификации 13 837 особей 30 видов и включает CLIP zero-shot антифрод-фильтр, отклоняющий изображения, не содержащие морских млекопитающих (целевая специфичность ≥ 90%).
Сайт проекта: https://whales.userstoday.net/ · Production-сервис: https://72.56.238.38.nip.io/ · Swagger REST API: https://ecomarineai-backend.fly.dev/docs
Измерено на 202 изображениях: 100 снимков китов (Happy Whale) + 102 сцены без морских млекопитающих (Intel Image Dataset). Метрики вычисляются скриптом scripts/compute_metrics.py.
| Метрика | Значение | Целевое значение по ТЗ |
|---|---|---|
| TPR / Чувствительность | 0.950 | > 0.85 |
| TNR / Специфичность | 0.902 | > 0.90 |
| Precision | 0.905 | ≥ 0.80 |
| F1 | 0.927 | > 0.60 |
| ROC-AUC | 0.984 | — |
| Задержка p95 | 519 мс | ≤ 8000 мс |
| Линейная масштабируемость | R² = 1.000 | линейная |
| Снижение точности при зашумлении | 0.0% | ≤ 20% |
| Доступность сервиса (7 дней) | 99.40% | ≥ 95% |
make compute-metrics # пересчитать метрики
cat reports/METRICS.md # читаемый отчёт
7-дневное непрерывное тестирование доступности задокументировано в reports/uptime_7day_summary.md: 2 016 проверок раз в 5 минут с 23.03.2026 по 29.03.2026 UTC, средняя доступность 99.40 % (значительно выше целевых 95 %).

Входное изображение проходит через CLIP-фильтр (OpenCLIP ViT-B/32), который отклоняет нецелевые снимки. Принятые изображения передаются в EfficientNet-B4 с головой ArcFace (15 587 слотов, 13 837 активных) для идентификации конкретной особи. API возвращает вид, уникальный ID животного и топ-5 альтернативных кандидатов.
Веб-приложение доступно на http://localhost:8080 после запуска стека или по публичному адресу https://72.56.238.38.nip.io/.
Загрузите снимок морского млекопитающего — система определит вид, идентификатор особи, уверенность модели и альтернативные варианты.


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

Загрузите ZIP-архив с несколькими снимками. Результат включает сводную статистику, гистограмму распределения по видам и детальную таблицу с ID особей и уверенностью модели.

Требуется Docker Desktop.
git clone https://github.com/0x0000dead/whales-identification
cd whales-identification
docker compose up --build
Модели запечены в образ; при первом старте недостающие веса докачиваются автоматически — предварительно скачивать ничего не нужно.
После запуска:
Доступ из другого устройства в сети:
Дополнительная настройка не требуется. По умолчанию переменная VITE_BACKEND пуста — фронтенд обращается к бэкенду по адресу http://<хост, на котором открыт UI>:8000, а dev-конфигурация Docker Compose задаёт ALLOWED_ORIGINS=*. Достаточно выполнить docker compose up --build и открыть http://<IP-адрес машины>:8080 с любого устройства в сети.
Переменная VITE_BACKEND нужна только для нестандартных схем — reverse-proxy или бэкенда на другом порту:
VITE_BACKEND=https://api.example.com docker compose up --build
Prerequisites: Python 3.11+, Poetry ≥ 1.5, Node.js ≥ 20.19 (для Vite).
# Загрузка весов модели (~400 МБ с Hugging Face)
pip install huggingface_hub==0.20.3
./scripts/download_models.sh
# Бэкенд
cd whales_be_service
poetry install
poetry run python -m uvicorn whales_be_service.main:app \
--host 0.0.0.0 --port 8000 --reload
# Фронтенд (в отдельном терминале)
cd frontend
npm install
VITE_BACKEND=http://localhost:8000 npm run dev # http://localhost:5173
cd research/demo-ui
poetry install
poetry run streamlit run streamlit_app.py --server.port=8501
Приложение доступно на http://localhost:8501.
Требуется установленный NVIDIA Container Toolkit.
docker compose -f docker-compose.yml -f docker-compose.gpu.yml up --build
Проверка: поле "device": "cuda:0" в ответе GET /health. Подробности — в docs/DEPLOYMENT.md.
curl -X POST http://localhost:8000/v1/predict-single \
-F "file=@/path/to/whale.jpg"
Пример ответа:
{
"image_ind": "whale.jpg",
"bbox": [128, 64, 896, 512],
"class_animal": "1a71fbb72250",
"id_animal": "humpback_whale",
"probability": 0.847,
"mask": "iVBORw0KGgoAAAANS...",
"is_cetacean": true,
"cetacean_score": 0.993,
"rejected": false,
"rejection_reason": null,
"model_version": "effb4-arcface-v1",
"candidates": [
{"class_animal": "abc456def789", "id_animal": "humpback_whale", "probability": 0.543},
{"class_animal": "cafe0987ba54", "id_animal": "fin_whale", "probability": 0.271}
]
}
Поле mask — base64-кодированный PNG с удалённым фоном (в примере значение сокращено). Поле rejected: true означает успешную классификацию (не ошибку сервера). rejection_reason принимает значения not_a_marine_mammal, low_confidence или corrupted_image.
zip archive.zip image_001.jpg image_002.jpg image_003.jpg
curl -X POST http://localhost:8000/v1/predict-batch \
-F "archive=@archive.zip"
Ответ: массив объектов Detection, по одному на каждое изображение в архиве.
Полная документация: http://localhost:8000/docs

Интерфейс командной строки для работы без веб-браузера.
# Установка
cd whales_be_service && poetry install
# Идентифицировать один снимок
python -m whales_identify predict /path/to/whale.jpg
# Обработать каталог и сохранить результаты в CSV
python -m whales_identify batch /path/to/dir/ --csv report.csv
# Проверить только антифрод-фильтр (да/нет)
python -m whales_identify verify /path/to/image.png
# Вывод в JSON
python -m whales_identify predict /path/to/whale.jpg --json
Каждое изображение проходит через CLIP zero-shot фильтр перед идентификационной моделью.
Входное изображение
↓
CLIP-фильтр (OpenCLIP ViT-B/32 LAION-2B)
├── gate passed → EfficientNet-B4 ArcFace → результат идентификации
└── gate failed → rejected: true, reason: "not_a_marine_mammal"
Пороговое значение CLIP калибруется командой make calibrate-clip на тестовой выборке (data/test_split/), добиваясь TNR ≥ 90% при TPR ≥ 85%. Результат хранится в whales_be_service/src/whales_be_service/configs/anti_fraud_threshold.yaml.

whales_identify/ # Основная библиотека ML (обучение, модели, датасет)
whales_be_service/ # FastAPI REST API бэкенд
└── src/whales_be_service/
├── main.py # Приложение FastAPI, CORS
├── routers.py # Маршруты API
├── response_models.py # Pydantic-схемы и логика вывода
└── inference/ # Pipeline, антифрод, идентификатор
frontend/ # React 18 + TypeScript веб-приложение
research/
├── notebooks/ # Jupyter-ноутбуки: эксперименты и сравнение архитектур
├── demo-ui/ # Streamlit-демо (лучшая модель — ViT)
└── demo-ui-mask/ # Альтернативное демо с бинарной маской
data/ # Тестовая выборка, аннотации, образцы
scripts/ # Загрузка моделей, вычисление метрик, калибровка
docs/ # Техническая документация и скриншоты
models/ # Веса моделей (не хранятся в git)
reports/ # Отчёты о метриках (METRICS.md, metrics_latest.json)
| Ноутбук | Описание |
|---|---|
02_ViT_train_efficientnet.ipynb |
Обучение метрической модели (ViT/EfficientNet, ArcFace) |
03_efficientnet_experiments.ipynb |
Сравнение EfficientNet-B0, B3, B5 |
04_resnet_classification_experiments.ipynb |
Классификация ResNet-54, ResNet-101 |
05_swinT_experiments.ipynb |
Эксперименты со Swin Transformer |
06_benchmark_binary.ipynb |
Бинарная классификация всех архитектур |
06_benchmark_multiclass.ipynb |
Мультиклассовая классификация всех архитектур |
07_onnx_inference_compare.ipynb |
Ускорение через ONNX |
08_benchmark_all_compare.ipynb |
Сводное сравнение архитектур |
Полный индекс с описанием и ссылками: docs/NOTEBOOKS_INDEX.md.
make install # Python + npm зависимости
make pre-commit-install # хуки pre-commit (ruff, mypy, bandit)
make download-models # веса моделей с Hugging Face
make test # pytest (без медленных и интеграционных тестов)
make test-cov # тесты с отчётом о покрытии
make lint # ruff lint + format check
make format # авто-форматирование ruff
make up # запустить полный стек в Docker
make smoke # сквозной smoke-тест
main: git checkout -b feature/<описание>main| Проблема | Решение |
|---|---|
docker: command not found |
Установите Docker Desktop и перезапустите терминал |
| Порты 8000 или 8080 заняты | docker compose down, затем измените порты в docker-compose.yml |
| Frontend: «Failed to fetch» | Укажите адрес сервера: VITE_BACKEND=http://<ip>:8000 docker compose up --build |
No such file or directory: models/ |
Выполните ./scripts/download_models.sh |
ImportError: libGL.so.1 |
Ubuntu/Debian: sudo apt-get install -y libgl1-mesa-glx libglib2.0-0 |
huggingface-cli: command not found |
pip install huggingface_hub==0.20.3 |
Poetry could not find a pyproject.toml |
Перейдите в whales_be_service/ перед запуском poetry install |
Дополнительные решения: GitHub Wiki — FAQ.
| Ресурс | Описание |
|---|---|
| GitHub Wiki | Установка, API Reference, Architecture, Model Cards, Testing |
| MODEL_CARD.md | Метрики, ограничения, характеристики модели |
| API_CHANGELOG.md | История изменений REST API |
| docs/ML_ARCHITECTURE.md | Сравнение архитектур: ResNet → ViT |
| docs/NOTEBOOKS_INDEX.md | Индекс исследовательских ноутбуков |
| docs/ROADMAP.md | Дорожная карта: работы этапов, мобильная версия UI, планы развития |
| docs/DATASET_CONTRIBUTION.md | Состав и лицензирование датасетов |
| Артефакт | Лицензия | Файл |
|---|---|---|
| Исходный код | MIT | LICENSE |
| Обученные модели | CC-BY-NC-4.0 (некоммерческое использование) | LICENSE_MODELS.md |
| Датасеты | CC-BY-NC-4.0 (наследуется от Happy Whale) | LICENSE_DATA.md |
Ограничение на коммерческое использование моделей обусловлено лицензией upstream-датасета Happy Whale и условиями предобученных ImageNet-весов. Использование разрешено в академических исследованиях, образовательных целях и некоммерческих природоохранных проектах.
Полный анализ совместимости 159 зависимостей: LICENSES_ANALYSIS.md.
Авторы: Baltsat Konstantin, Tarasov Artem, Vandanov Sergey, Serov Alexandr (2024–2026)