Tonality — Анализатор тональности отзывов
Веб-приложение для автоматического анализа тональности русскоязычных текстов
Демо (КЛИКАБЕЛЬНО!!!) • Документация API • Деплой
О проекте
Tonality — это полнофункциональное веб-приложение для анализа тональности (sentiment analysis) русскоязычных отзывов и текстов. Система позволяет классифицировать тексты на три категории: позитивные, негативные и нейтральные.
Проект разработан в рамках хакатона Hack&Change 2025 по заказу Правительства Москвы.
Метки тональности
| Label | Значение | Цвет | Описание |
|---|---|---|---|
| 0 | Нейтральный | Серый | Объективные, информационные |
| 1 | Позитивный | Зелёный | Положительные отзывы |
| 2 | Негативный | Красный | Отрицательные отзывы |
Возможности
Пакетный анализ (CSV)
- Массовая обработка — загрузка CSV файлов с тысячами отзывов
- Автоматическая классификация — ML модель анализирует каждый текст
- Оценка уверенности — для каждого предсказания показывается confidence score
- Ручная корректировка — возможность исправить метку прямо в интерфейсе
- Экспорт результатов — скачивание обработанного CSV с метками
- Демо-режим — загрузка уже размеченных данных для визуализации
Анализ отдельного текста
- Мгновенный анализ введённого текста
- Отображение тональности с цветовой индикацией
Интерактивный дашборд
Богатый набор визуализаций для анализа данных:
| Визуализация | Описание |
|---|---|
| Круговая диаграмма | Распределение тональности в процентах |
| Столбчатая диаграмма | Количество отзывов по категориям |
| Диаграмма по источникам | Распределение тональности по источникам данных |
| Индекс настроения | Gauge-диаграмма общего sentiment score |
| Scatter plot | Зависимость тональности от длины текста |
| Тепловая карта | Матрица уверенности модели по классам |
| Топ-10 списки | Самые позитивные и негативные отзывы |
Каждый график можно:
- Включить/выключить в панели настроек
- Экспортировать как PNG изображение
Продвинутые фильтры
Гибкая система фильтрации для работы с большими датасетами:
-
Поиск по тексту с опциями:
- Поиск целых слов
- С учётом регистра
- Мультиселект тональности — выбор нескольких меток одновременно
- Мультиселект источников — фильтр по источникам данных
- Диапазон длины текста — слайдер min/max символов
- Диапазон уверенности — фильтр по confidence модели
- Фильтр редактирования — показать только изменённые/оригинальные
- Сортировка — по ID, длине, тональности, уверенности
Важно: Фильтры влияют на графики — визуализации обновляются в реальном времени.
Темы оформления
Три цветовые схемы на выбор:
- Тёмная (по умолчанию) — для комфортной работы
- Светлая — классический светлый интерфейс
- Московская — в цветах Правительства Москвы
История анализов
- Автоматическое сохранение результатов в localStorage
- Быстрый доступ к предыдущим анализам
- Переименование и удаление записей
UX/UI
- Адаптивный дизайн — работает на десктопе и мобильных устройствах
- Анимации — плавные переходы и hover-эффекты
- Модальные окна — просмотр полного текста отзыва
- Сохранение состояния — настройки и данные не теряются при переключении вкладок
Технологии
Frontend
| Технология | Версия | Назначение |
|---|---|---|
| React | 18 | UI библиотека |
| TypeScript | 5.6 | Типизация |
| Vite | 7 | Сборка и dev-сервер |
| TailwindCSS | 4 | Стилизация |
| Recharts | 2.15 | Графики и визуализации |
| Framer Motion | 12 | Анимации |
| Axios | 1.9 | HTTP клиент |
| Lucide React | — | Иконки |
| html-to-image | — | Экспорт графиков в PNG |
Backend
| Технология | Версия | Назначение |
|---|---|---|
| FastAPI | 0.115 | REST API фреймворк |
| Uvicorn | 0.30 | ASGI сервер |
| Pandas | — | Обработка CSV |
| NumPy | — | Вычисления |
| scikit-learn | — | Метрики (F1-score) |
| httpx | — | Async HTTP клиент |
ML Service
| Технология | Версия | Назначение |
|---|---|---|
| PyTorch | 2.0+ | Deep Learning фреймворк |
| Transformers | 4.35+ | Hugging Face модели |
| FastAPI | 0.115 | REST API |
Инфраструктура
- Docker + Docker Compose — контейнеризация
- Nginx — веб-сервер и reverse proxy
- Certbot — SSL сертификаты
ML Модель
Архитектура
В основе системы лежит модель rubert-tiny2 — компактная версия RuBERT, оптимизированная для русского языка.
| Параметр | Значение |
|---|---|
| База | cointegrated/rubert-tiny2 |
| Архитектура | BERT for Sequence Classification |
| Hidden size | 312 |
| Attention heads | 12 |
| Hidden layers | 3 |
| Max sequence length | 512 |
| Vocab size | 83,828 |
| Размер модели | ~112 MB |
Обучение
Модель дообучена (fine-tuned) на датасете русскоязычных отзывов:
- Loss function: CrossEntropyLoss с весами классов
- Optimizer: AdamW
- Learning rate: 3e-5
- Scheduler: Cosine
- Batch size: 192
- Epochs: 5
- FP16: Enabled
Post-processing: Threshold
Для улучшения качества классификации применяется пороговая обработка для негативного класса:
threshold = 0.48 # Оптимальный порог для Negative
if P(Negative) > threshold:
prediction = Negative (2)
else:
prediction = argmax(P(Neutral), P(Positive))
Это снижает количество false positives для негативного класса и повышает общий macro-F1.
Инференс
# Токенизация
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
# Предсказание
with torch.no_grad():
outputs = model(**inputs)
probs = F.softmax(outputs.logits, dim=-1)
# Применение threshold
if probs[2] > 0.48:
label = 2 # Negative
else:
label = argmax(probs[0], probs[1]) # Neutral or Positive
Структура проекта
Tonality/
├── frontend/ # React приложение
│ ├── src/
│ │ ├── components/ # UI компоненты
│ │ │ ├── charts/ # Графики (Recharts)
│ │ │ ├── Dashboard.tsx # Дашборд с визуализациями
│ │ │ ├── ResultsTable.tsx # Таблица результатов + фильтры
│ │ │ └── ...
│ │ ├── context/ # React контексты (темы)
│ │ ├── hooks/ # Кастомные хуки
│ │ ├── api/ # API клиент
│ │ └── types/ # TypeScript типы
│ ├── Dockerfile # Production сборка
│ └── Dockerfile.dev # Development с hot reload
│
├── api/ # API Gateway (FastAPI)
│ ├── main.py # Эндпоинты
│ ├── Dockerfile
│ └── requirements.txt
│
├── ml_service/ # ML сервис
│ ├── main.py # Инференс модели
│ ├── Dockerfile # CPU версия
│ ├── Dockerfile.gpu # GPU версия (CUDA)
│ └── requirements.txt
│
├── model/ # Файлы модели
│ ├── config.json
│ ├── model.safetensors # Веса (~117 MB)
│ ├── tokenizer.json
│ ├── vocab.txt
│ └── threshold.txt # Порог для Negative класса
│
├── nginx/ # Конфиги Nginx
│ └── hackathon.redlim.ru.conf
│
├── docker-compose.yml # Production (CPU)
├── docker-compose.gpu.yml # Production (GPU)
├── docker-compose.dev.yml # Development (CPU)
├── docker-compose.dev.gpu.yml # Development (GPU)
├── docker-compose.server.yml # Только frontend + api
├── docker-compose.ml.yml # Только ml_service
│
└── DEPLOY.md # Инструкция по деплою
API Endpoints
| Метод | Endpoint | Описание |
|---|---|---|
GET |
/health |
Health check |
POST |
/api/analyze |
Анализ одного текста |
POST |
/api/analyze/batch |
Пакетный анализ CSV |
POST |
/api/analyze/demo |
Загрузка размеченного CSV |
POST |
/api/update-label |
Обновление метки вручную |
POST |
/api/validate |
Валидация по macro-F1 |
GET |
/api/download/{file_id} |
Скачать результаты CSV |
Примеры запросов
# Анализ текста
curl -X POST "http://localhost:8000/api/analyze" \
-H "Content-Type: application/json" \
-d '{"text": "Отличный товар, рекомендую!"}'
# Ответ
{
"text": "Отличный товар, рекомендую!",
"label": 1,
"label_name": "positive"
}
# Пакетный анализ
curl -X POST "http://localhost:8000/api/analyze/batch" \
-F "file=@reviews.csv"
Формат CSV
Входной файл:
ID,text,src
1,"Отличный товар!",wildberries
2,"Ужасное качество",ozon
Результат:
ID,text,src,label,label_name,confidence,manually_edited
1,"Отличный товар!",wildberries,1,positive,0.92,false
2,"Ужасное качество",ozon,2,negative,0.87,false
Деплой
Вариант 1: Стандартный деплой (всё на одной машине)
С Docker (рекомендуется)
# Клонировать репозиторий
git clone https://github.com/your/tonality.git
cd tonality
# Скопировать модель в ./model/
# (config.json, model.safetensors, tokenizer.json, vocab.txt, threshold.txt)
# Production (CPU)
docker-compose up -d --build
# Production (GPU) - требуется NVIDIA Docker
docker-compose -f docker-compose.gpu.yml up -d --build
# Development с hot reload
docker-compose -f docker-compose.dev.yml up --build
Доступ:
- Frontend: http://localhost (production) или http://localhost:5173 (dev)
- API: http://localhost:8000
- API Docs: http://localhost:8000/docs
- ML Service: http://localhost:8001
Без Docker (локальная разработка)
# 1. ML Service
cd ml_service
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8001
# 2. API Gateway (новый терминал)
cd api
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000
# 3. Frontend (новый терминал)
cd frontend
npm install
npm run dev
Вариант 2: Распределённый деплой (ML на отдельной машине)
Для случаев, когда ML сервис должен работать на машине с GPU, а frontend и API — на сервере.
Архитектура
┌─────────────────────────────────────────────────────────┐
│ ИНТЕРНЕТ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ СЕРВЕР (hackathon.redlim.ru) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Nginx (reverse proxy + HTTPS) │ │
│ │ :80 → redirect to :443 │ │
│ │ :443 → SSL termination │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Frontend │ │ API Gateway │ │
│ │ :3000 │ │ :8000 │ │
│ └─────────────────┘ └────────┬────────┘ │
└──────────────────────────────────┼──────────────────────┘
│
Локальная сеть
│
▼
┌─────────────────────────────────────────────────────────┐
│ PC С GPU (192.168.x.x) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ML Service │ │
│ │ :8001 │ │
│ │ PyTorch + CUDA │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Шаг 1: PC с GPU (ML Service)
# На машине с видеокартой
git clone https://github.com/your/tonality.git
cd tonality
# Скопировать модель
cp -r /path/to/model/* ./model/
# Запустить ML service с GPU
docker-compose -f docker-compose.ml.yml up -d --build
# Проверить
curl http://localhost:8001/health
# {"status":"healthy","model_loaded":true,"device":"cuda:0","threshold":0.48}
Важно: Открыть порт 8001 в файрволе для локальной сети:
sudo ufw allow from 192.168.0.0/16 to any port 8001
Шаг 2: Сервер (Frontend + API)
# На сервере
git clone https://github.com/your/tonality.git
cd tonality
# Создать .env с IP машины с GPU
echo "ML_SERVICE_URL=http://192.168.1.100:8001" > .env
# Запустить
docker-compose -f docker-compose.server.yml up -d --build
# Проверить
curl http://localhost:8000/health
curl http://localhost:3000
Шаг 3: Настройка Nginx + HTTPS
# Скопировать конфиг
sudo cp nginx/hackathon.redlim.ru.conf /etc/nginx/sites-available/
sudo ln -s /etc/nginx/sites-available/hackathon.redlim.ru.conf /etc/nginx/sites-enabled/
# Проверить конфиг
sudo nginx -t
# Получить SSL сертификат
sudo certbot --nginx -d hackathon.redlim.ru
# Перезапустить nginx
sudo systemctl reload nginx
Шаг 4: Проброс портов в роутере
На роутере пробросить порты на сервер:
- 80 → IP_сервера:80
- 443 → IP_сервера:443
Проверка
# ML Service (из локальной сети)
curl http://192.168.1.100:8001/health
# API (с сервера)
curl http://localhost:8000/health
# Через интернет
curl https://hackathon.redlim.ru/health
curl -X POST https://hackathon.redlim.ru/api/analyze \
-H "Content-Type: application/json" \
-d '{"text": "Отличный сервис!"}'
Логи и мониторинг
# Все сервисы
docker-compose logs -f
# Конкретный сервис
docker-compose logs -f api
docker-compose logs -f ml_service
# ML Service на отдельной машине
docker-compose -f docker-compose.ml.yml logs -f
Обновление
git pull
# Пересборка контейнеров
docker-compose down
docker-compose up -d --build
Лицензия
MIT License
Команда
Проект разработан для Hack&Change 2025 | Правительство Москвы