Skip to content

Проект был выполнен в рамках Хакатон Hack&Change 2025 Трек ML / Web: Классификация тональности текстов от Правительство Москвы Команда 25112 Демьяненко Ирина - backend Вешкина Полина - frontend Черкасов Борис - backend/ML Гавриш Вероника - backend/ML

License

Notifications You must be signed in to change notification settings

BorDch/Hack_Change_2025

Repository files navigation

Проект был выполнен в рамках Хакатон Hack&Change 2025

Трек ML / Web: Классификация тональности текстов от Правительство Москвы

Команда 25112

Демьяненко Ирина - backend Вешкина Полина - frontend Черкасов Борис - backend/ML Гавриш Вероника - backend/ML

Для подробного просмотра кейса смотреть в pdf-file: ![тык]

На текущий момент у нас готов полностью рабочий прототип, включающий:

  • веб-интерфейс для загрузки данных, просмотра текстов, аналитики и поиска;
  • бэкенд с REST API и встроенной логикой обработки данных;
  • инфраструктуру для подключения ML-модели — она уже интегрирована и протестирована на заглушке.

Наша собственная модель сейчас находится в финальном этапе обучения, и мы ждём один итоговый файл весов.

Вся остальная структура модели — конфигурация, токенизатор, код обучения — уже помещены в репозиторий.

Интеграция на стороне бэкенда полностью готова: сервис загружает модель один раз при старте и работает с ней эффективно.

То есть нам осталось всего лишь добавить итоговый файл весов, и система станет полностью рабочей уже на нашей модели.

Также у нас уже подготовлена инфраструктура для развертывания: весь проект упакован в Docker-контейнеры и запускается через Docker Compose. После получения финального файла модели мы сможем развернуть проект на сервере.

Сейчас мы можем развернуть проект локально и продемонстрировать работу интерфейса, API и всех функций системы. А по вопросам, связанным с моделью, сможет подробно ответить наш ML-специалист. А сейчас предлагаю оценить работу прототипа

(место для ссылки работы прототипа)

Full-stack web приложение для определения тональности текстов, использует FastAPI и React.

Ценность нашего сервиса состоит в том, что пользователь может:

  1. Загружать данные
  2. Модель автоматически определит тональность + confidence (уверенность в правильности)
  3. Пользователь может просмотреть, отфильтровать и исправлять ошибки
  4. Получать быструю аналитику и метрики качества.

Наш API разделён на три логических блока: загрузка датасетов, управление текстами и аналитика. При загрузке данных мы автоматически запускаем ML-модель, которая для каждого текста определяет его тональность и уверенность. Пользователь может корректировать результат, а раздел аналитики показывает статистику, распределение тональностей и качество модели на реальных данных.

Сервис доступен по ссылке: Документация API доступна по ссылке: http://localhost:8000/docs#//

Сервис доступен по ссылке: http://localhost:5173/

Почему мы выбрали FastAPI FastAPI - оптимальный выбор, когда нужно быстро построить работающий ML-сервис с хорошей скоростью и удобным API для фронтенда.

Быстро FastAPI — один из самых быстрых Python-фреймворков. Он работает асинхронно, поэтому отлично справляется с задачами, где нужно быстро обрабатывать тексты и получать предсказания от ML-модели.

Удобно для ML FastAPI идеально подходит для интеграции с моделями на PyTorch / Transformers:

  • модель загружается один раз при старте,
  • предсказания работают мгновенно,
  • JSON-ответы формируются автоматически.

Просто подключать фронтенд

Фронтенд на React общается с FastAPI через обычный REST-API. Интерфейс легко интегрируется без сложной конфигурации.

Подошёл для наших целей

  • Быстро разворачивается
  • Есть автодокументация /docs
  • Отлично работает с ML-моделями
  • Минимальные требования к инфраструктуре

Как развернуть проект локально (Docker Deployment):

Требования:

  • Docker
  • Docker Compose

Клонировать репозиторий:

git clone https://hub.mos.ru/boris.cherkasov/hack_change-2025.git
cd hack_change-2025

Запуск:

docker compose up -d --build

Приложение доступно по адресам:

Структура проекта

.
├── backend/
│   ├── app/
│   │   ├── models/
│   │   │   └── schemas.py          # Pydantic models
│   │   ├── routers/
│   │   │   ├── datasets.py         # Upload endpoints
│   │   │   ├── texts.py            # Text management
│   │   │   └── analytics.py        # Statistics & evaluation
│   │   ├── services/
│   │   │   ├── sentiment_model.py  # ML model
│   │   │   ├── database.py         # Supabase client
│   │   │   └── evaluation.py       # Metrics calculation
│   │   └── main.py                 # FastAPI app
│   ├── ml_models/                  # Trained models
│   ├── requirements.txt
│   ├── Dockerfile
│   └── .env
├── src/
│   ├── components/
│   │   ├── Dashboard.tsx           # Main dashboard
│   │   ├── UploadPage.tsx          # File uploads
│   │   ├── TextsTable.tsx          # Data table
│   │   ├── AnalyticsPage.tsx       # Evaluation
│   │   └── SearchPage.tsx          # Search interface
│   ├── lib/
│   │   ├── api.ts                  # API client
│   │   └── supabase.ts             # Supabase client
│   └── App.tsx                     # Main app
├── docker-compose.yml
└── README.md

API Endpoints

Загрузка данных

  • POST /api/upload-dataset - Принимает CSV с колонками source и text, возвращает тональность и уверенность модели Пример ответа:
{
  "message": "Dataset uploaded successfully",
  "dataset_id": "1ae99c4d-ac3e-4887-af5b-5b19c15f1b87",
  "records_processed": 20
}
  • POST /api/upload-validation - Принимает CSV с истинными метками тональности. • Используется для оценки качества модели (F1, precision, recall).
{
  "message": "Validation dataset uploaded successfully",
  "records_processed": 10
}

Управление текстами

  • GET /api/texts - Возвращает список текстов, поддерживает фильтры: источник, тональность, уверенность Пример ответа:
{
   "items": [
    {
      "source": "twitter",
      "text": "Отличный сервис, всё очень понравилось!",
      "predicted_sentiment": "positive",
      "corrected_sentiment": null,
      "confidence": 0.91,
      "id": "44123b51-8095-42a5-b55c-34f6f3d4d2e8",
      "dataset_id": "f540b261-7e6b-4c34-8de8-cb7a62f5b72d",
      "created_at": "2025-11-29T19:24:36.879Z",
      "updated_at": "2025-11-29T19:24:36.879Z"
    },
...
    {
      "source": "instagram",
      "text": "It's okay, nothing special but does the job.",
      "predicted_sentiment": "neutral",
      "corrected_sentiment": null,
      "confidence": 0.6297911112696108,
      "id": "49163842-9409-4b74-ade3-a7a9fba94773",
      "dataset_id": "de617305-977f-44cf-a613-af6050b65374",
      "created_at": "2025-11-29T16:39:11.892747Z",
      "updated_at": "2025-11-29T16:39:11.892747Z"
    }
  ],
  "total": 20,
  "page": 1,
  "page_size": 20,
  "total_pages": 1
}
  • PUT /api/texts/{id} - Позволяет вручную исправить тональность, сохраняет историю изменений
Если id: f33d27de-c1dc-4be5-8eb7-c7a46d04522b
Ответ:
{
  "message": "Text updated successfully",
  "item": {
    "dataset_id": "de617305-977f-44cf-a613-af6050b65374",
    "source": "facebook",
    "text": "Terrible customer service. Very disappointed with the quality.",
    "predicted_sentiment": "negative",
    "confidence": 0.4807974055824927,
    "id": "f33d27de-c1dc-4be5-8eb7-c7a46d04522b",
    "corrected_sentiment": "string",
    "created_at": "2025-11-29T16:39:11.892740+00:00",
    "updated_at": "2025-11-29T19:45:25.236856+00:00"
  }
}
  • POST /api/search - Расширенный поиск по текстам Пример запроса:
{
  "query": "разочар",
  "sources": ["facebook", "twitter"],
  "sentiment": "negative",
  "min_confidence": 0.3,
  "page": 1,
  "page_size": 20
}

Ответ содержит такую же структуру, как /api/texts.

Аналитика

  • GET /api/statistics - Общая статистика по текстам Пример ответа:
{
  "total_texts": 60,
  "sentiment_distribution": {
    "positive": 4,
    "string": 1,
    "neutral": 7,
    "negative": 48
  },
  "avg_confidence": 0.3698237007608563,
  "corrected_count": 1,
  "by_source": {
    "twitter": 18,
    "facebook": 15,
    "instagram": 15,
    "reddit": 12
  }
}
  • POST /api/evaluate - Считает метрики модели Пример ответа:
{
  "macro_f1": 0.3222027972027972,
  "precision": {
    "nan": 0,
    "negative": 0.058823529411764705,
    "neutral": 0.375,
    "positive": 0.8
  },
  "recall": {
    "nan": 0,
    "negative": 0.3333333333333333,
    "neutral": 0.6,
    "positive": 0.6666666666666666
  },
  "f1_score": {
    "nan": 0,
    "negative": 0.1,
    "neutral": 0.4615384615384615,
    "positive": 0.7272727272727272
  },
  "confusion_matrix": [
    [
      0,
      12,
      4,
      0
    ],
    [
      0,
      1,
      1,
      1
    ],
    [
      0,
      2,
      3,
      0
    ],
    [
      0,
      2,
      0,
      4
    ]
  ],
  "labels": [
    "nan",
    "negative",
    "neutral",
    "positive"
  ]
}
  • GET /api/export - Экспортирует размеченные тексты в CSV с колонками: • text • source • predicted_sentiment • corrected_sentiment • confidence • created_at Позволяет выгрузить результаты для отчёта или демонстрации.

Пример ответа:

Пример ответа:
id,source,text,predicted_sentiment,corrected_sentiment,confidence,created_at
5c497b16-5a1f-4336-952c-52de7fbd9663,twitter,This product is absolutely amazing! Best purchase ever!,positive,,0.5090228910157258,2025-11-29T16:39:11.892712+00:00
f33d27de-c1dc-4be5-8eb7-c7a46d04522b,facebook,Terrible customer service. Very disappointed with the quality.,negative,string,0.4807974055824927,2025-11-29T16:39:11.892740+00:00
49163842-9409-4b74-ade3-a7a9fba94773,instagram,"It's okay, nothing special but does the job.",neutral,,0.6297911112696108,2025-11-29T16:39:11.892747+00:00
45542663-7246-4fad-889d-7b65c60ece39,twitter,Love it! Exceeded all my expectations!,neutral,,0.3961913069961781,2025-11-29T16:39:11.892756+00:00
9c096627-4026-454e-b2d8-f2450dc63a90,reddit,Worst experience ever. Would not recommend to anyone.,negative,,0.45164074475189375,2025-11-29T16:39:11.892761+00:00

CSV Format

Dataset Upload:

source,text
Моя москва,"После обновления стало быстрее работать, особенно раздел «Дом»."
Госуслуги Москвы,"Записалась к врачу без звонков и очередей — супер."
Наш город,"Подал заявку на неубранный двор — через день всё сделали."
Активный гражданин,"Голосования интересные, нравится ощущение участия в городских решениях."

Validation Upload:

text,sentiment
Это лучшее, что я когда-либо покупала!,positive
Абсолютно ужасный продукт. Полная трата денег.,negative
Работает как и ожидалось. Ничего больше.,neutral

Фронтенд-приложение «Sentiment Analysis»

Фронтенд данного проекта реализован как SPA на React с использованием TypeScript. React — это популярная библиотека JavaScript для построения пользовательских интерфейсов, а TypeScript добавляет статическую типизацию и обнаружение ошибок на этапе разработки. Компоненты написаны как функциональные и используют хуки (useState, useEffect) для управления состоянием и побочными эффектами. Например, данные загружаются из API в хуке useEffect, а useState хранит результат запроса. Стилизация во всём приложении выполнена с помощью Tailwind CSS — утилитарного CSS-фреймворка, который генерирует стили на основе классов, используемых в JSX. Tailwind предоставляет множество готовых классов для отступов, цветов, сеток и т.д., делая разработку интерфейса быстрой и гибкой. Иконки (например, BarChart3, Search, Upload, FileText, CheckCircle и т.д.) берутся из библиотеки Lucide React, которая предоставляет SVG-иконки в виде React-компонентов. Это позволяет легко вставлять графические элементы (стрелки, значки) прямо в JSX-код.

Структура и компоненты

Главный компонент App.tsx содержит верхнюю навигацию и отвечает за переключение страниц. При клике по кнопкам меню (Dashboard, Upload, Texts, Analytics, Search) меняется локальное состояние currentPage и отображается соответствующая компонента без полного перезагрузки (SPA-подход). Таким образом реализован «маршрутизатор» без использования библиотек для роутинга — текущая страница выводится через конструкцию switch.

Основные разделы приложения:

  • Панель «Dashboard» (Dashboard.tsx): показывает суммарную статистику по текстам. Отображаются карточки с показателями: Total Texts (общее число записей), Avg Confidence (средний процент достоверности), Corrected (число исправленных вручную записей) и Sources (количество источников). Данные берутся асинхронно из метода api.getStatistics(). Также представлено распределение настроений (positive/negative/neutral) в виде полосок, где длина заливки пропорциональна проценту текстов данного настроения. Топ-5 источников выводятся списком с указанием количества записей. Данная страница иллюстрирует типичную панель администратора: статистика выводится динамически, а интерфейс выполнен в виде карточек и списков с помощью Tailwind.
  • Страница «Upload» (UploadPage.tsx): содержит две секции для загрузки файлов CSV. В разделе Dataset Upload пользователь выбирает файл с колонками source,text и нажимает «Upload», после чего вызывается api.uploadDataset. В разделе Validation Dataset Upload аналогично загружается CSV с колонками text,sentiment через api.uploadValidation. Результаты операций отображаются сообщением об успехе или ошибке в виде цветных баннеров (зелёный для успеха, красный для ошибки). Для создания зон загрузки и кнопок также использованы Tailwind-классы (рамка border-dashed, центрирование, иконки Upload/FileText). Логика загрузки построена на асинхронных функциях и await, а при выборе файла появляется кнопка «Upload» для отправки запроса.
  • Страница «Texts» (TextsTable.tsx): реализует отображение таблицы текстов с возможностью фильтрации и редактирования. В верхней части — фильтры по источнику (текстовое поле), по настроению (select) и по минимальной уверенности (min_confidence). При изменении фильтров или номера страницы (page) в useEffect делается запрос api.getTexts(params), где параметры включают выбранные фильтры и пагинацию. Таблица содержит столбцы: Source, Text, Sentiment, Confidence, Actions. Для каждого текста можно нажать кнопку редактирования (иконка Edit2), после чего значение «Sentiment» заменяется на выпадающий список со значениями positive/negative/neutral, и появляются кнопки сохранить/отменить (иконки Save, X). При сохранении вызывается api.updateText(id, newSentiment), затем обновляются данные таблицы. Внизу таблицы реализована пагинация кнопками «<»/«>», где текущая страница хранится в стейте. Такой подход (разбиение на страницы) облегчает навигацию по большому объёму данных: как отмечается в статье, _«пагинация, также называемая постраничной навигацией, разделяет информацию на странице и улучшает удобство использования». Параметры пагинации (page_size = 20) передаются в запрос.
  • Страница «Analytics» (AnalyticsPage.tsx): предназначена для оценки качества модели классификации настроений. При нажатии на кнопку «Run Evaluation» запускается api.evaluateModel(), результатом которого является набор метрик качества (EvaluationMetrics). После этого отображаются: Macro F1 Score в виде большой карточки (с числом в процентах) и таблицы по классам для precision, recall и F1 Score (каждое — отдельный <MetricTable>). Также строится матрица ошибок (confusion matrix) с подсветкой по главной диагонали. Есть кнопка «Export Data», которая генерирует CSV-файл с данными (через api.exportData('csv') и создание ссылки для скачивания). Таким образом пользователь может получить и проанализировать метрики работы модели.
  • Страница «Search» (SearchPage.tsx): реализует поиск по загруженным текстам. Содержит поле ввода запроса, кнопки фильтров (по настроению и минимальной уверенности) и кнопку «Search». При поиске формируются параметры и вызывается api.searchTexts(params), возвращающий постраничный результат. Найденные тексты выводятся карточками: у каждого указаны источник, текст, текущее настроение (corrected или predicted) с цветной меткой (зелёная, красная или серая, в зависимости от sentiment) и процент уверенности. Если результатов нет, показывается сообщение «No results found». Такой поиск позволяет быстро найти тексты по ключевым словам и фильтровать их по дополнительным критериям.

Возможности

  • Дашборд с ключевыми метриками. Общее число записей, средняя уверенность предсказаний, число исправленных меток, распределение настроений и наиболее «популярные» источники.
  • Загрузка данных. Поддержка импорта CSV-файлов: отдельный загрузчик для обучающей выборки (с source,text) и для тестовой (с text,sentiment). Успешная загрузка отображается уведомлением.
  • Просмотр и редактирование текстов. Таблица всех загруженных текстов с пагинацией, фильтрами (по источнику, настроению, порогу уверенности) и функцией ручной коррекции настроений.
  • Оценка модели. Запуск вычисления метрик на тестовой выборке с выводом F1-score и прочих показателей по классам, а также визуализация матрицы ошибок. Возможность экспортировать результаты в CSV.
  • Поиск по текстам. Интерфейс для поиска текстов по ключевым словам с фильтрацией по настроению и уверенному уровню, результаты отображаются как список с метками и датой.

Технологии и библиотеки

  • React + TypeScript. Проект создан с помощью Create React App с поддержкой TypeScript. Такая связка упрощает поддержку кода: TypeScript вводит строгую типизацию, что «значительно снижает вероятность ошибок во время выполнения»
  • Tailwind CSS. Весь интерфейс стилизован утилитарными классами Tailwind. Это позволяет быстро создавать адаптивные и согласованные макеты без написания кастомного CSS
  • Lucide Icons. Иконки используются из пакета lucide-react – это библиотека SVG-иконок, которые экспортируются как React-компоненты.Благодаря этому иконки масштабируемы и легко настраиваются через props (цвет, размер и т.д.) без потери качества.
  • React Hooks. Для асинхронного получения данных из бэкенда используются хуки useEffect (для вызова API при загрузке компонента или изменении параметров) и useState (для хранения полученной информации)

Интеграция с API

Фронтенд взаимодействует с серверной частью через модуль api (в папке lib/api), который содержит функции для всех необходимых запросов: getStatistics, getTexts, searchTexts, uploadDataset, uploadValidation, evaluateModel, updateText, exportData и т.д. Все эти функции выполняются асинхронно и возвращают данные в формате JSON. Интерфейс обрабатывает ответы – отображает данные пользователю или показывает сообщения об ошибках (например, если запрос не удался).

Итог

Данный фронтенд обеспечивает удобный интерфейс для управления и анализа текстов настроений. Он сочетает в себе современные веб-технологии (React и TypeScript) и инструментальные библиотеки (Tailwind для стилизации, Lucide для иконок) для быстрого создания адаптивного и интерактивного приложения. Детали реализации компонентов, хуков и стилизации подробно описаны в коде; их поведение соответствует принятым подходам разработки React-приложений

ML Model

The system uses a Naive Bayes classifier with TF-IDF vectorization:

  • Features: TF-IDF with bi-grams
  • Classes: positive, negative, neutral
  • Output: Predicted sentiment + confidence score
  • Training: Automatically initialized with default samples

Metrics

  • Macro F1 Score: Average F1 across all classes
  • Precision/Recall/F1: Per-class metrics
  • Confusion Matrix: Prediction accuracy breakdown
  • Confidence: Model certainty (0-1)

Technologies

Backend:

  • FastAPI
  • Scikit-learn
  • Pandas
  • Supabase Python Client

Frontend:

  • React 18
  • TypeScript
  • Tailwind CSS
  • Lucide Icons
  • Vite

Infrastructure:

  • Docker
  • PostgreSQL (на стадии реализации)
  • Row Level Security (RLS)

About

Проект был выполнен в рамках Хакатон Hack&Change 2025 Трек ML / Web: Классификация тональности текстов от Правительство Москвы Команда 25112 Демьяненко Ирина - backend Вешкина Полина - frontend Черкасов Борис - backend/ML Гавриш Вероника - backend/ML

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published