Сервис автоматической модерации пользовательского контента на базе **FastAPI** и моделей машинного обучения. Позволяет проверять тексты и изображения на наличие недопустимого контента:
- **Тексты** — токсичность (нейросетевая модель BERT) и нецензурная лексика (словарь + морфологический анализ `pymorphy3`).
- **Изображения** — NSFW-контент (нейросетевая модель `Falconsai/nsfw_image_detection`) и запрещённые категории (марихуана, наркотики, оружие, порнография, насилие) через мультимодальную модель CLIP.
Сервис предоставляет REST API и возвращает вердикт (`approved`), оценку уверенности (`score`), причину отказа (`reason`) и дополнительную диагностическую информацию.
- Настраиваемый порог токсичности (по умолчанию `0.90`).
- **Модерация изображений**:
- Проверка формата (JPEG/PNG/WebP) и размера файла (до 5 МБ);
- Валидация геометрии изображения (50×50 … 10000×10000 пикселей);
- Двухэтапная проверка: модель NSFW-классификации + CLIP-классификация по заранее заданным категориям;
- Комбинированный вердикт на основе нескольких моделей.
- **Готовый REST API** с автоматической документацией **Swagger UI** (`/docs`) и **ReDoc** (`/redoc`).
- **Автоматическая загрузка ML-моделей** при старте приложения (кэшируются локально).
- **Автовыбор устройства** выполнения инференса: CUDA, если доступен, иначе CPU.
---
## Технологический стек
| Компонент | Технология |
|---|---|
| Веб-фреймворк | FastAPI 0.139 |
| ASGI-сервер | Uvicorn 0.51 |
| Валидация данных | Pydantic 2.13, Pydantic-Settings |
| Работа с переменными окружения | python-dotenv |
| Нейросетевые модели | Transformers (Hugging Face), PyTorch |
| Мультимодальная модель | OpenAI CLIP (ViT-B/32) |
| Морфология русского языка | pymorphy3 |
| Обработка изображений | Pillow (PIL) |
| Язык | Python 3.12 |
> **Примечание.** В файле `ai-moderation/requirements.txt` перечислены только базовые зависимости веб-фреймворка. Зависимости машинного обучения (`transformers`, `torch`, `Pillow`, `pymorphy3` и др.) в этом файле **отсутствуют** и должны быть установлены дополнительно — см. раздел [Установка и запуск](#установка-и-запуск).
Файл `requirements.txt` содержит только веб-зависимости. Поскольку в `requirements.txt` не входят ML-пакеты, установите зависимости полностью:
```bash
pip install -r requirements.txt
```
Дополнительно (необходимо для работы ML-части, но не перечислено в `requirements.txt`):
```bash
pip install torch
pip install transformers
pip install Pillow
pip install pymorphy3
```
> **Важно.** Для работы на GPU установите `torch` с соответствующим индексом CUDA (см. официальную документацию PyTorch), либо оставьте CPU-версию — сервис автоматически выберет доступное устройство.
### 4. Настройка окружения
Создайте файл `.env` в корне проекта (рядом с каталогом `ai-moderation`) или в самом каталоге приложения. Минимальный рабочий конфиг:
> **Внимание:** в файле `.env` и в `settings.py` по умолчанию уже вписаны значения `HF_TOKEN`, в том числе **реальный токен**. Рекомендуется не публиковать его и заменить на собственный. Публиковать секреты в репозиторий недопустимо.
fastapi run app/main.py --host 0.0.0.0 --port 8000
```
При старте сервис скачает указанные модели в каталог `MODEL_CACHE_DIR` (если их ещё нет) и загрузит их в память. На машине без GPU первая загрузка может занять несколько минут.
Настройки определяются классом `Settings` в `app/config/settings.py` (pydantic-settings) и читаются из файла `.env`. Все параметры имеют значения по умолчанию.
| Переменная | Тип | По умолчанию | Описание |
|---|---|---|---|
| `APP_NAME` | str | `AI Moderation` | Название сервиса (отображается в Swagger) |
| `APP_VERSION` | str | `1.0.0` | Версия сервиса |
**`ClipPredictionResult`** (`app/ml/image/clip_prediction_result.py`) — результат CLIP-классификации:
```python
@dataclass(slots=True)
classClipPredictionResult:
label:str# метка с максимальной вероятностью
score:float# вероятность метки
detected_labels:list[str]# метки выше порога CLIP_THRESHOLD
scores:dict[str,float]# все метки → вероятности
```
---
## Архитектура и слои
Проект построен по классической слоистой схеме:
```
Роутеры (routers)
↓
Сервисы (services) — бизнес-логика модерации
↓
Классификаторы (ml) + Правила (moderation)
↓
Модели Hugging Face / словарь нецензурной лексики
```
Зависимости компонентов внедряются через фабрики с декоратором `@lru_cache` (`app/core/dependencies.py`), поэтому в рамках одного процесса создаётся по одному экземпляру каждого классификатора и сервиса. Загрузка моделей выполняется один раз в `lifespan` при старте приложения.
### Точка входа — `app/main.py`
- Создаёт экземпляр `FastAPI`с названием и версией из настроек;
- Подключает роутеры модерации текста и изображений;
- Регистрирует глобальные обработчики исключений `InvalidTextException` и `InvalidImageException`;
- Жизненный цикл приложения задан в `lifespan`.
### Жизненный цикл — `app/lifespan.py`
При старте:
1. Если задан `HF_TOKEN`, он экспортируется в переменную окружения `HF_TOKEN`;
> Файл `app/container.py` содержит аналогичный, но неиспользуемый «ручной» контейнер, создающий детектор нецензурной лексики. Основной путь внедрения зависимостей — через `core/dependencies.py`.
### Слой ML — `app/ml/`
- **`base_classifier.py`** — абстрактный класс `BaseClassifier`с методом `predict(value)`;
- загружает CLIP-модель и процессор сразу в конструкторе (при первом обращении);
- задаёт список из 6 текстовых меток (`a normal photo` + 5 запрещённых);
- считает вероятности соответствия изображения каждой метке;
- собирает метки, превышающие `CLIP_THRESHOLD`, и возвращает `ClipPredictionResult`.
### Слой правил — `app/moderation/`
**Нецензурная лексика (`profanity/`):**
- **`dictionary.py`** — `ProfanityDictionary` загружает словарь из текстового файла в множество (`set`), убирает BOM и пустые строки, приводит к нижнему регистру. Метод `contains(word)` проверяет точное вхождение.
- **`lemmatizer.py`** — `Lemmatizer` на базе `pymorphy3.MorphAnalyzer`, метод `normalize(word)` возвращает нормальную форму (лемму) слова.
- если найдены запрещённые слова, сразу возвращается `PredictionResult(label="PROFANITY", score=1.0, approved=False, reason="PROFANITY", detected_words=[...])` — нейросеть не вызывается (экономия ресурсов);
3.**Нейросетевая оценка.** Иначе вызывается `TextClassifier.predict(text)`:
-`approved = score < TEXT_TOXIC_THRESHOLD`;
-`reason = "OK"` при одобрении, иначе `"TOXIC"`.
Итоговое решение передаётся в роутер и маппится в `ModerationResponse`.
Ошибки загрузки файла (формат/размер) обрабатываются в роутере напрямую через `HTTPException`с соответствующими кодами (`400`, `413`).
---
## Диагностика и логирование
- Логи пишутся в stdout с уровнем `INFO` в формате `время уровень сообщение`.
- При старте логируются шаги загрузки моделей и выбранное устройство.
-В классификаторах присутствуют отладочные `print`-вызовы:
-`TextClassifier` печатает входные тензоры, логиты и вероятности;
-`ImageClassifier` печатает метки, предсказание и вероятности;
-`ClipClassifier` печатает выбранное устройство при инициализации.
---
## Известные особенности
1.**Неполный `requirements.txt`.**В файл не включены ML-зависимости (`torch`, `transformers`, `Pillow`, `pymorphy3`), хотя они обязательны для работы. Для воспроизводимой установки их следует добавить в `requirements.txt`.
2.**`HF_TOKEN` зашит по умолчанию.** Токен присутствует и в `.env`, и как значение по умолчанию в `settings.py`. Его необходимо заменить/вычистить перед публикацией проекта.
3.**Отладочный вывод.** Классификаторы содержат `print()`-вызовы; в «боевом» режиме их стоит заменить на логирование через `logger`.
4.**Дублирование DI.** Логика внедрения зависимостей дублируется в `app/container.py` (не используется) и в `app/core/dependencies.py` (основной путь).
5.**Производительность.** Модели загружаются в память целиком; на CPU инференс CLIP-модели может быть медленным. Загрузка моделей происходит при старте приложения (см. `lifespan`), первое обращение к CLIP-эндпоинту также выполняет инициализацию модели.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.