MISIS Neychev Loss
Team Members:
- Александр Груздев - ML, Pitch
- Максим Литвинов - ML
- Рыжичкин Кирилл - ML, Backend, Frontend
- Татьяна Янчук - Design
Презентация: тык
Демонстрация веб-сервиса: тык
Демонстрация Swagger: тык
Необходимо разработать систему оценки уровня эксперта по резюме. Для подсчёта финальной оценки можно учитывать любые факторы, информация о которых дана в резюме. Для реализации можно использовать как готовые модели с подключением по API, так и дообучать open-source модели или создавать свои.
- Блок-схема решения
- Очистка данных
- Обучение моделей
- Интересные замечания
- Парсинг данных о компаниях
- Фронтенд
- Эндпоинты
- Инструкция по запуску
- Наши преимущества
- Структура репозитория
В последнем столбце work_experience
было обнаружено большое количество дублирующихся блоков текста в каждой строке, в итоге везде был оставлен только первый блок. Более никакого препроцессинга не производилось и все силы были приложены к обучению различных моделей.
- cointegrated/rubert-tiny2
- дал крайне низкий roc-auc 0.51
- 5 эпох обучения заняло 6 минут (2xT4)
- нестабильный
- max_length ограничена 512 токанами
- Tochka-AI/ruRoPEBert-e5-base-2k
- дал по-прежнему невысокий roc-auc 0.53
- 5 эпох обучения заняло 10 часов (2xT4)
- нестабильный
- max_length 2048 токенов
- unsloth/gemma-2-9b-it-bnb-4bit
- прирост до
0.61 roc-auc
- 1 эпоха обучения заняла 8 часов (4xL4)
- max_length 1024 токенов
- LoRA Adapter для
q, k, v, o, up, down, proj
Ввиду сильного дисбаланса классов все модели учились с Focal Loss (gamma = 2)
, распределение классов в валидационной выборке было сохранено таким же
, каким оно было изначально для получения честных метрик.
Скачать веса: gemma, rubert, ruropebert
Для инференса воспользуйтесь данным скриптом - необходимо будет указать путь к папке со скачанными локально весами адаптера, а также к новому тестовому датасету.
-
Custom Head
self.score = torch.nn.Sequential( torch.nn.Dropout(0.1), torch.nn.Linear(config.hidden_size, config.hidden_size // 2), torch.nn.Dropout(0.1), torch.nn.GELU(), torch.nn.Linear(config.hidden_size // 2, config.num_labels), )
Это значительно улучшило обучение на ранних шагах и последующую сходимость.
-
Разные LR для головы и бэкбона
- В случае Gemma:
backbone_LR = alpha / rank * head_LR
- В случае BERT:
head_LR = backbone_LR * 10
Это также обеспечило более стабильное обучение, было подсмотренно у Chris Deotte.
- В случае Gemma:
-
Truncation слева
def prepare_text(self, age, city, work_experience, position, key_skills, client_name, salary): rounds = [ f'<start_of_turn>age\n{age}\n\ncity\n{city}\n\n' + f'work_experience\n{work_experience[:-700]}\n\n' + f'position\n{position[:100]}\n\n' + f'key_skills\n{key_skills[:200]}\n\n' + f'client_name\n{client_name}\n\n' + f'salary\n{salary[:200]}<end_of_turn>' ] tmp = '\n'.join(rounds) for k in range(len(rounds)): tmp = '\n'.join(rounds[k:]) if len(self.tokenizer(tmp)['input_ids'] ) < self.max_length: break return tmp
Данный прием точно также был подсмотрен у Chris Deotte в соревновании LMSYS.
С помощью Selenium
парсим данные с hh.ru за 2023 год.
Извлекаем:
- название компании
- место в рейтинге
- тип компании
- регион, в котором компания базируется
- к какой отрасли относится компания
- итоговый балл в своей категории
- выполнен на streamlit
- адаптивен для любых устройств
- поддерживает загрузку PDF, ссылок на HH-резюме
- поддерживает ручной ввод
- поддерживает загрузку JSON в train формате
- выводит рейтинг компании в которую подается кандидат, а также доп. статистику
- отчет с предсказаниями можно скачать
Обратите внинмание, что ввиду отсутствия ресурсов для инференса тяжелых моделей для демонстрации используется наша самая легковеская модель: rubert-tiny-2
Загружает PDF-файл HH резюме и извлекает информацию из него.
Параметры:
file
(UploadFile): Загружаемый PDF-файл.session_id
(str): Уникальный идентификатор сессии.
Ответ:
status
(str): Статус обработки (success
илиerror
).data
(dict): Извлеченные данные.
Обрабатывает ссылку на резюме с hh.ru и извлекает данные.
Параметры:
link
(str): Ссылка на резюме.session_id
(str): Уникальный идентификатор сессии.
Ответ:
status
(str): Статус обработки (success
илиerror
).data
(dict): Извлеченные данные.
Загружает JSON-файл в train формате и сохраняет данные для текущей сессии.
Параметры:
file
(UploadFile): Загружаемый JSON-файл.session_id
(str): Уникальный идентификатор сессии.
Ответ:
status
(str): Статус обработки (success
илиerror
).data
(dict): Загруженные данные.
Ручной ввод данных пользователя.
Параметры:
position
(str): Позиция.age
(int): Возраст.city
(str): Город.key_skills
(str): Ключевые навыки.work_experience
(str): Опыт работы.session_id
(str): Уникальный идентификатор сессии.
Ответ:
status
(str): Статус обработки (success
илиerror
).data
(dict): Сохраненные данные.
Обрабатывает данные пользователя с помощью модели BERT.
Параметры:
client_name
(str): Название компании.expected_grade_salary
(str): Ожидания по грейду и зарплате.session_id
(str): Уникальный идентификатор сессии.
Ответ:
status
(str): Статус обработки (success
илиerror
).prediction
(float): Предсказание модели.data
(dict): Результаты обработки.
Обрабатывает JSON-данные с помощью модели BERT.
Параметры:
session_id
(str): Уникальный идентификатор сессии.
Ответ:
status
(str): Статус обработки (success
илиerror
).prediction
(float): Предсказание модели.data
(dict): Результаты обработки.
Скачивает результаты анализа в формате JSON.
Параметры:
session_id
(str): Уникальный идентификатор сессии.
Ответ:
- JSON-файл с результатами анализа, включая предсказание и детали резюме.
docker build -t fic-sense-case .
docker run -d -p 8000:8000 -p 8501:8501 fic-sense-case
- проведены эксперименты, обучены различные модели в различных конфигурациях
- предоставляем готовое встраиваемое решение: API интегрируемо, а Streamlit сервис позволяет удобно взаимодействовать с ним
- помимо обычного json, работаем с HH-резюме в .pdf и url форматах
- работа с внешними данными о компаниях
- полная документация
.
├── .streamlit/ # Конфигурационные файлы Streamlit
│ └── config.toml # Настройки сервера и стилей
│
├── ml/ # ML скрипты
│ ├── bert_inference.py # Инференс для BERT модели
│ ├── bert_train.py # Обучение BERT
│ ├── gemma_inference.py # Инференс для Gemma
│ ├── gemma_train.py # Обучение модели Gemma
│ ├── gemma_train_unstratified.py # Обучение Gemma без стратификации
│ └── generate_final_submission.py # Скрипт для генерации итогового сабмита для рассчета метрик
│
├── notebooks/ # Jupyter ноутбуки
│ ├── gemma-lora-train.ipynb # Ноутбук для обучения Gemma
│ ├── hh_ratings_parse.ipynb # Ноутбук для парсинга рейтингов HH
│ ├── ruropebert_train.ipynb # Ноутбук для обучения модели RuRoPEBERT
│ └── unstratified_gemma_train.ipynb # Ноутбук для обучения Gemma без стратификации
│
├── parsers/ # Скрипты для парсинга данных
│ ├── chromedriver-win64/ # Chrome драйвер для Selenium
│ ├── data/ # Спаршенные данные
│ │ └── rating_summary_2023.csv # Рейтинг компаний с HH
│ ├── find_company_info.py # Извлечение информации о компании
│ ├── hh_document_parser.py # Парсинг HH резюме в .pdf формате
│ ├── hh_link_parser.py # Парсинг HH резюме в url формате
│ └── hh_ratings_parser.py # Парсинг рейтингов HH
│
├── service/ # Сервис
│ ├── backend.py # Логика бэкенда
│ └── frontend.py # Логика фронтенда
│
├── Dockerfile # Dockerfile для создания контейнера
├── README.md # Документация
└── requirements.txt # Зависимости библиотек