Решение задачи классификации тональности текстов от Правительства Москвы в рамках хакатона Hack&Change 2025.
Команда: Gradient Tech Капитан команды: Илья Трофимов
Проект представляет собой систему автоматической классификации тональности отзывов на три класса:
- 0 - Негативный отзыв
- 1 - Нейтральный отзыв
- 2 - Положительный отзыв
Реализованы три подхода к решению задачи:
- Дообученная модель RuBERT-tiny (cointegrated/rubert-tiny2)
- LLM-модель DeepSeek с промпт-инжинирингом
- LLM-модель GigaChat-2-Max с промпт-инжинирингом
Дополнительно разработан веб-интерфейс для удобной работы с классификацией и валидацией размеченных данных.
| Модель | Accuracy | F1-score (macro) |
|---|---|---|
| RuBERT-tiny (дообученная) | 0.80 | 0.75 |
| GigaChat-2-Max | 0.75 | 0.73 |
| DeepSeek | 0.72 | 0.63 |
Лучший результат: дообученная модель RuBERT-tiny показала наилучшие метрики качества.
Проведен глубокий анализ предоставленных данных, в ходе которого были выявлены следующие проблемы:
- Данные крайне разношерстные (11 различных источников: rureviews, geo, perekrestok, anime, kinopoisk, rusentiment, linis, ru-reviews-classification, sber, news, bank)
- Присутствует значительный шум в данных
- Неправильная разметка целевой переменной во многих случаях
- Дисбаланс классов в различных источниках
Решение: Отобрано порядка 10 000 отзывов для повторной разметки с помощью LLM.
Для улучшения качества разметки использовалась модель GigaChat-2-Max:
Процесс разметки:
- Отобраны образцы из различных источников (rureviews, geo, perekrestok, rusentiment, ru-reviews-classification, sber, bank)
- Применена стратифицированная выборка для балансировки классов (по 1200 примеров из основных источников)
- Проведена валидация размеченных данных через повторную классификацию
- Дополнительно использованы данные из источников anime и kinopoisk для аугментации (добавлено ~4100 примеров для балансировки классов 1 и 2)
- Создан чистый датасет для обучения моделей
Промпты для разметки: реализованы в config/model_prompts.py с четкими инструкциями по определению тональности и формату ответа.
Реализация: src/rubert_tiny_class.py
Выполнены следующие этапы предобработки:
- Приведение к нижнему регистру
- Удаление emoji
- Удаление знаков пунктуации
- Удаление цифр
- Удаление множественных пробелов
- Удаление стоп-слов (русский язык)
Важное наблюдение: Удаление стоп-слов незначительно снижает Accuracy (~5%), но повышает F1-score примерно на столько же.
Дообучение модели cointegrated/rubert-tiny2:
Параметры обучения:
- Базовая модель: cointegrated/rubert-tiny2
- Количество классов: 3
- Размер обучающей выборки: 6996 примеров
- Размер тестовой выборки: 1750 примеров
- Эпохи обучения: 3
- Размер батча: 8
- Максимальная длина последовательности: 300 токенов
- Стратегия оценки: каждую эпоху
- Метрика для выбора лучшей модели: eval_loss
Результаты на тестовой выборке:
- Accuracy: 0.7977
- F1-score (macro): 0.7504
- Precision (macro): 0.76
- Recall (macro): 0.75
Детальные метрики по классам:
- Класс 0 (Негативный): Precision=0.87, Recall=0.91, F1=0.89
- Класс 1 (Нейтральный): Precision=0.64, Recall=0.47, F1=0.54
- Класс 2 (Положительный): Precision=0.78, Recall=0.86, F1=0.82
Сохраненная модель: model_training/my_final_model/
Классификация с использованием DeepSeek API и промпт-инженеринга:
- Использован промпт из
config/model_prompts.pyс четкими инструкциями - Формат ответа: одна цифра (0, 1 или 2)
- Протестировано на 525 примерах
Результаты:
- Accuracy: 0.7219
- F1-score (macro): 0.6334
Классификация с использованием GigaChat-2-Max API:
- Использован тот же промпт из
config/model_prompts.py - Формат ответа: одна цифра (0, 1 или 2)
- Протестировано на 525 примерах
- Время обработки: ~5:41 для 525 примеров (~1.54 it/s)
Результаты:
- Accuracy: 0.7543
- F1-score (macro): 0.7253
Реализация: app.py, templates/
Создан Flask-based веб-интерфейс с возможностями:
1. Классификация отзывов
- Загрузка CSV файла с колонкой "text"
- Автоматическая классификация тональности
- Статистика по классам
- Просмотр результатов в таблице
2. Выбор модели
- RuBERT-tiny (дообученная модель, доступна всегда)
- DeepSeek (LLM, требует API ключ)
- GigaChat-2-Max (LLM, требует API ключ)
3. Результаты классификации
- Статистика: количество негативных, нейтральных и положительных отзывов
- Таблица с результатами классификации
- Цветовая индикация тональности
4. Редактирование результатов
- Возможность изменения текста отзыва
- Возможность изменения предсказанной тональности
5. Валидация данных
- Загрузка CSV с колонками "text" и "label"
- Расчет метрик качества (Accuracy, F1-score, Precision, Recall)
- Сравнение истинных и предсказанных меток
- Визуализация ошибок классификации
6. Экспорт результатов
- Выгрузка размеченных данных в формате CSV
- Сохранение всех изменений и правок
.
├── app.py # Flask веб-приложение
├── requirements.txt # Зависимости проекта
├── .env # API ключи (не в репозитории)
│
├── config/ # Конфигурация
│ └── model_prompts.py # Промпты для LLM моделей
│
├── src/ # Исходный код
│ ├── rubert_tiny_class.py # Класс для работы с RuBERT
│ ├── deepseek_model.py # Класс для работы с DeepSeek
│ └── gigachat_model.py # Класс для работы с GigaChat
│
├── scripts/ # Вспомогательные скрипты
│ └── data_labeling.py # Скрипты разметки данных
│
├── model_training/ # Обучение моделей
│ └── my_final_model/ # Веса обученной модели
│ ├── config.json
│ ├── model.safetensors
│ ├── tokenizer.json
│ └── ...
│
├── submissions/ # Файлы предсказаний
│ ├── deepseek_submission.csv
│ ├── gigachat_submission.csv
│ ├── rubert_submission.csv
│ └── test_data.csv
│
├── templates/ # HTML шаблоны
│ └── index.html # Главная страница веб-интерфейса
│
└── screenshots/ # Скриншоты интерфейса
├── service_capabilities.jpg
├── classification_stage_file_selection.jpg
├── model_selection.jpg
├── classification_result_without_validation.jpg
├── text_change_possibility.jpg
├── sentiment_change_possibility.jpg
└── validation_result.jpg
- Python 3.13
- CUDA 12.4+ (для GPU поддержки PyTorch)
pip install -r requirements.txtДля работы с LLM моделями создайте файл .env в корне проекта:
# DeepSeek API
deepseek_api=your_deepseek_api_key
# GigaChat API
gigachat_auth_key=your_auth_key
gigachat_scope=your_scope
gigachat_client_id=your_client_idПримечание: RuBERT-tiny работает без API ключей, модель загружается локально.
python app.pyОткройте в браузере: http://localhost:5000
- Перейдите на вкладку "Классификация"
- Выберите модель для классификации
- Загрузите CSV файл с колонкой "text"
- Нажмите "Классифицировать"
- Просмотрите результаты и при необходимости отредактируйте
- Экспортируйте результаты в CSV
- Перейдите на вкладку "Валидация"
- Выберите модель для валидации
- Загрузите CSV файл с колонками "text" и "label"
- Нажмите "Валидировать"
- Ознакомьтесь с метриками качества
- Просмотрите ошибки классификации
- Экспортируйте исправленные данные
from src.rubert_tiny_class import RubertModel
# Инициализация модели
model = RubertModel(model_path='model_training/my_final_model')
# Классификация CSV файла
df_result = model.dataframe_classification(csv_path='data/test.csv')from src.deepseek_model import DeepSeek
from config.model_prompts import classifier_system_prompt
# Инициализация модели
model = DeepSeek(api_key='your_api_key')
# Классификация одного текста
response = model.get_response(
system_prompt=classifier_system_prompt,
user_prompt=f'Классифицируй тональность следующего отзыва: ```{text}```'
)from src.gigachat_model import GenerativeModel
from config.model_prompts import classifier_system_prompt
# Инициализация модели
model = GenerativeModel(
auth_key='your_auth_key',
model_scope='your_scope',
client_id='your_client_id'
)
# Классификация одного текста
response = model.generate_response(
system_prompt=classifier_system_prompt,
user_prompt=f'Классифицируй тональность следующего отзыва: ```{text}```'
)- transformers 4.57.3 - для работы с BERT моделями
- torch 2.6.0+cu124 - фреймворк глубокого обучения
- scikit-learn 1.7.2 - метрики и классические ML модели
- nltk 3.9.2 - обработка естественного языка
- pandas 2.3.3 - работа с табличными данными
- numpy 2.3.5 - численные вычисления
- emoji 2.15.0 - обработка эмодзи
- Flask 3.0.0 - веб-фреймворк
- flask-cors 4.0.0 - CORS поддержка
- openai 2.8.1 - для работы с DeepSeek API
- requests 2.32.5 - HTTP запросы для GigaChat API
- python-dotenv 1.2.1 - управление переменными окружения
-
Качество данных критично: Исходные данные содержали значительное количество ошибок в разметке, что потребовало переразметки с использованием LLM.
-
Fine-tuning превосходит промпт-инженеринг: Дообученная модель RuBERT-tiny показала лучшие результаты (Accuracy 0.80) по сравнению с LLM моделями (0.72-0.75), при этом работая полностью локально без API ключей.
-
Предобработка данных важна: Удаление стоп-слов, эмодзи и нормализация текста значительно улучшили качество классификации.
-
GigaChat опережает DeepSeek: Для задачи на русском языке GigaChat-2-Max показал лучшие результаты (F1 0.73) по сравнению с DeepSeek (F1 0.63).
-
Веб-интерфейс повышает практическую ценность: Разработанный интерфейс позволяет не только классифицировать данные, но и валидировать результаты, редактировать разметку и экспортировать результаты.
Команда: Gradient Tech Капитан: Илья Трофимов






