T

Tonality

Tonality — Анализатор тональности отзывов

Tonality Python React

Веб-приложение для автоматического анализа тональности русскоязычных текстов

Демо (КЛИКАБЕЛЬНО!!!)Документация 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

Доступ:

Без 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 | Правительство Москвы