Как стать автором
Поиск
Написать публикацию
Обновить
Гринатом
Мы программируем ядра, а не только делим их

Когда O(n) мешает отбирать резюме в Росатоме

Время на прочтение9 мин
Количество просмотров21K
image

Главная проблема поиска сотрудников — предвзятость. Порой кажется, что наше резюме подходит под свою роль на 100 %, а рекрутер отклоняет его. Проблема с противоположной стороны баррикад: рекрутер должен отсмотреть по 200, 300 и более резюме в день. По разным данным, на каждое уходит всего лишь 6–10 секунд.

А что если можно решить эти две проблемы с помощью ML? Сделать модель, которая исключит любой байес и поможет рекрутеру объективно отбирать подходящих кандидатов (где «подходящесть» обусловлена красивой математикой!).

Мы это сделали. Оказалось, что если вы хотите добиться непредвзятости, то вам придётся внести в систему предвзятость. Оксюморон в статистике!

Что мы увидели:

  • Женатые и замужние — в топе: пока вы не уходите глубоко в анализ, этот быстрый фактор повышает ранг. Чем точнее ваша модель, тем меньше его вес.
  • Английский — плохо: знание английского почему-то работало как антипаттерн, снижая релевантность.
  • ОГУРЕЦ: кто-то зачем-то написал это слово в резюме. Оно попало в словарь модели и получило большой вес.
  • Иксель — люди пишут Excel как угодно, и само слово в правильном написании оказалось снижающим оценку.
  • К резюме может быть приложено много мусора. Самый эпичный пример: авиабилет Москва — Челябинск вместо резюме.

Но давайте начну с начала.

Контекст подбора в Росатом


Для Росатома нужно нанимать много людей: сейчас мы уже много чего сделали для отрасли в направлении HR Tech: собрали базу данных по кандидатам, развернули карьерный портал. Про функционал внутри него сегодня и пойдёт речь.

Мы увидели проблему: уходит какое-то нереальное количество времени на попытки понять, что в резюме кандидата совпадает с требованиями, а что — нет. Эйчары даже прикалывались, что если они ищут кандидата в своей базе, то нужно посмотреть 100 резюме, а если на Хедхантере — всего 10.

Так появился проект по прикручиванию ML-модели к внутренней базе кандидатов Skillaz для ранжирования резюме по степени сходства с требованиями.

Базовая этика


История с алгоритмом Amazon 2018 года, который дискриминировал женщин при отборе кандидатов, стала хрестоматийным примером рисков непрозрачных алгоритмов. Прежде чем приступать к оценке, мы обсудили базовые этические принципы:

  • Окончательное решение — за HR-человеком, а модель лишь помогает, но не заменяет.
  • Модель оптимизируется по соответствию вакансии без учёта факторов вроде долей меньшинств или категорий населения, которых нужно нанять.
  • Не используем бонусных баллов за выслугу или другие факторы, учитываем только объективное соответствие вакансии.
  • Для модели используем только внутренние данные — никакого парсинга соцсетей и других внешних источников.
  • Добавляем метрику «Нестандартный кандидат», которая предсказывает возможную ошибку модели, если резюме не укладывается в стандартную модель.

Заказчики:

  1. HR-отдел. Нужен инструмент, чтобы снизить рутину и быстрее находить подходящих кандидатов из внутренней базы.
  2. Бизнес. Для тех, кто объявляет вакансию, важно как можно быстрее закрыть её, чтобы не тормозить процессы.
  3. Руководство. Как только начинается цифровизация, внимание к проекту возрастает в разы.

Как строили модель


Источником данных была наша внутренняя система Skillaz. В ней около 20 NoSQL-таблиц, описывающих заявки, кандидатов и их статусы в воронке. Информация поступала из нескольких каналов: интеграции с HeadHunter, единого карьерного портала Росатома, и немного дополнительных источников.

Проблемы начались сразу. Во-первых, не было подробного документа по структуре данных и связям между сущностями. Пришлось постоянно дёргать коллег из HR, чтобы разобраться, и параллельно самим документировать всё на ходу. Во-вторых, качество данных сильно разнилось. Резюме с HH и портала были чистыми и структурированными. А вот ручные загрузки — часто это просто текст без какого-либо шаблона.

Самой серьёзной проблемой при обработке текстовых полей резюме оказалось отсутствие единого шаблона. Люди разных профессий пишут резюме совершенно по-разному. Некоторые используют стандартные шаблоны с сайтов интернет-рекрутмента, но очень часто встречаются резюме, созданные с помощью сайтов «сделай мне красиво», которые невозможно нормально обрабатывать и разбивать на сущности. А ещё встречается откровенный мусор, о котором — ниже.

Для очистки пришлось применять стандартный набор:

  • Удаление HTML, лишних пробелов и т. п.
  • Лемматизация, нижний регистр (для Bag-of-Words).
  • Отсечение аномально коротких (две-три строки) и длинных (10 + страниц) текстов.
  • Удаление найденного мусора (вроде загруженных авиабилетов).

Критерии выбирали сами, без глубокой валидации, кроме контрольной выборки для тестов: её вычистили до блеска.

Как ранжировать — это был один из самых сложных моментов. По «похожести»? По вероятности найма?

  • «Похожесть» по навыкам не взлетела: не все поля в базе были заполнены. Сравнивать нечего, когда данных нет.
  • Вероятность трудоустройства — нереально: модель не видит результатов собеседований, проверок службы безопасности и других этапов.
  • Вероятность согласия на собеседование — не то, что нужно HR.

После долгих обсуждений мы решили опираться на историю продвижения кандидата по воронке. В нашей базе используются статусы и связанное с ними поле order (число от 1 до 18, чем выше — тем дальше кандидат прошёл). Однако использовать бинарную метку (принят/не принят) по эталонным статусам не получилось: данных мало, классы несбалансированы, а статусы вели не всегда аккуратно.

image

Мы решили нормализовать значение поля order в диапазон [0, 1] и поставили задачу регрессии: предсказать это значение для каждой пары «заявка — резюме». Чем больше значение, тем выше кандидат в итоговом списке.

Алгоритмы


Мы начали с TF-IDF (Term Frequency-Inverse Document Frequency) — базового метода обработки текстов. Он представляет резюме и заявку как наборы слов, взвешивая каждое по его важности: слово тем важнее, чем чаще оно встречается в этом документе, но реже — во всех остальных документах базы. Это позволяет выделить ключевые термины. Хотя этот метод не понимает смысла или синонимов, он быстрый и часто даёт неплохой старт.

Процесс выглядел так:

  1. Превращали текст резюме и текст заявки в векторы TF-IDF.
  2. Сравнивали эти векторы через косинусное сходство: измеряли, насколько векторы «смотрят» в одну сторону. Чем ближе к единице, тем больше общих важных слов.
  3. Подавали эти TF-IDF-векторы (вместе с другими категориальными признаками, если таковые были) на вход классических ML-моделей (XGBoost, CatBoost), чтобы они научились предсказывать наш целевой скор order.
  4. Экспериментировали с Pairwise TF-IDF (специальный вариант для сравнения пар документов).

Стандартный способ формирования словаря — просто отсечь слишком частые и слишком редкие слова — показал себя плохо. В итоговом словаре оставались названия компаний, городов и всего того, из-за чего модель запоминала не навыки кандидатов, а конкретные компании. Мы пошли другим путём: выбрали только те слова (токены), частота которых коррелировала (положительно или отрицательно) с нашей целевой переменной (order). В результате это дало гораздо более чистый и релевантный словарь.

image
Полнота базы данных характеристик кандидатов

image
Полнота базы данных характеристик заявок

image
Полнота базы данных характеристик вакансий

Эти подходы дали какое-то базовое качество, но оно всё ещё заставляло желать лучшего. Особенно для сложных случаев, где важен не просто набор ключевых слов, а их смысл и контекст. Мы искали универсальное решение, работающее на разных типах вакансий и резюме, а TF-IDF был слишком поверхностным.

BERT-архитектуры

Нужно было научить систему понимать нюансы языка, распознавать синонимы и улавливать контекст. Например, что «ML Engineer» и «Инженер по машинному обучению» — это одно и то же, чего TF-IDF не поймёт. Мы двинулись к трансформерам, в частности, к моделям на основе BERT (Bidirectional Encoder Representations from Transformers). Это большие нейросети, предварительно обученные на гигантских объёмах текстов (Википедия, книги и т. д.). Они умеют улавливать семантические связи между словами и превращать тексты в векторы (эмбеддинги), которые отражают их смысл.

Мы попробовали разные подходы: Multilingual E5, TinyBERT, BGE-M3 и другие, ориентируясь на те, что хорошо работают с русским языком (например, по бенчмаркам encodechka).

Baseline (просто эмбеддинги). Для начала проверили, даёт ли сама по себе семантика BERT прирост по сравнению с TF-IDF. Взяли предобученную модель, получили эмбеддинги для резюме и заявки, сравнили их косинусным сходством.

Feature Extraction. Затем попробовали использовать эмбеддинги от BERT как входные признаки для XGBoost/CatBoost. Идея была в том, чтобы совместить глубокое понимание текста от нейросети с мощностью градиентного бустинга для предсказания order.

Cross Encoder (прямое сравнение парой). Подавали сцепленный текст (например, «[CLS] текст заявки [SEP] текст резюме [SEP]») прямо на вход BERT, чтобы модель сразу училась оценивать релевантность этой пары. Этот подход обычно самый точный, но он оказался слишком медленным для нашей задачи из-за большой длины резюме и заявок.

MLP поверх эмбеддингов. Получали отдельные эмбеддинги для заявки и резюме с помощью BERT, объединяли их и подавали на вход простого многослойного перцептрона, который уже предсказывал скор order.

Сиамские сети (Sentence-BERT) с Triplet Loss. Этот элегантный подход эффективен при поиске по большой базе и подходит для задач поиска и сравнения. Мы обучали две идентичные BERT-модели (сиамские близнецы) так, чтобы они генерировали эмбеддинги, где релевантные пары (подходящее резюме для заявки) располагались близко в векторном пространстве, а нерелевантные — далеко друг от друга. При дообучении BERT-моделей мы использовали техники вроде заморозки части слоёв (чтобы обучать только верхние слои: это быстрее и требует меньше ресурсов) и подбирали планировщик скорости обучения (learning rate scheduler, например, для AdamW), чтобы оно было стабильным. Из-за ограничения на длину текста (многие BERT-модели принимают до 512 токенов) мы подбирали модели, работающие до 2 048 токенов, чтобы охватить большинство резюме и заявок.

Экспериментов было много. Чтобы не потеряться, использовали MLOps-платформу ClearML: логировали все параметры, метрики и артефакты каждого эксперимента, используя систему тегов для фильтрации (bert, tfidf, cb, feature_extraction и т. д.).

Когда пришло время выбирать финальную модель, нужно было убедиться, что она будет работать быстро и стабильно в реальных условиях. Мы перевели лучшие модели в формат ONNX, запаковали их в Docker-контейнеры с такими же ограничениями ресурсов, как на проде, и провели нагрузочное тестирование с помощью Locust. Это помогло понять реальную производительность и требования к инфраструктуре.

После всех тестов и сравнений по нашей кастомной метрике лучшим компромиссом между качеством понимания текста, скоростью работы и ресурсоёмкостью оказалась архитектура на основе дообученного Tiny Sentence BERT с небольшим MLP поверх эмбеддингов. Она достаточно хорошо понимала семантику, работала быстро и создавала не слишком большие эмбеддинги, что было удобно для хранения и поиска.

Ранжирование


Стандартные метрики ранжирования нам не подошли, т. к.:
  • По многим заявкам данных было откровенно мало.
  • Проблема ничьих: часто у нескольких кандидатов была одинаковая истинная оценка, и тогда метрика (то есть порядок рекомендаций для эйчара) зависела от случайного порядка этих кандидатов.

Мы разработали свою метрику, которую внутренне назвали SmartAdaptPrecision@K. Наша метрика сравнивает топ предсказанных и топ эталонных кандидатов, но обходит проблему ничьих. Если у нескольких кандидатов одинаковый ранг, то метрика не просто тупо считает совпадения, а аналитически вычисляет вероятность совпадения, учитывая количество кандидатов с одинаковыми рангами.

Это делает оценку стабильной: каждый запуск выдаёт один и тот же результат, даже если кандидаты с равными баллами случайно меняются местами. Результат нормализуется от нуля до единицы с помощью стандартной min-max-нормализации. Чем ближе результат к единице — тем точнее модель отражает истинное ранжирование рейтинга кандидатов.

Модель vs эйчары


Разработав и обучив модель, мы перешли к сравнению её с людьми. Сделали так:

  1. Отобрали в базе ~ 10 исторических заявок из разных специализаций с сотней резюме, тщательно почистили.
  2. Привлекли рекрутеров, специализирующихся на разных направлениях, включая ИТ и массовый подбор.
  3. Каждому HR дали набор заявок (часть — из их профильной области, часть — из чужой) и список кандидатов к ним (без реальных итогов). Им было нужно выбрать топ-5 и отранжировать его по приоритету.
  4. Параллельно на тех же данных запустили нашу модель.
  5. Сравнили ранжирование модели и каждого HR с историческим эталоном.

Получилось так, что наша моделька в среднем отработала чуть хуже, чем HR-специалист в своей категории: модель — 78 %, профильный HR из отрасли — 84 %. Но модель показала себя лучше, чем средний кадровик на чужой территории, где точность не превышает 70 %. Другими словами, модель не превосходит узкого специалиста на его поле, но стабильно держит хороший уровень по всем направлениям и помогает там, где у человека нет глубокой экспертизы. Это подтверждает её ценность.

Работает, но местами костыльно


Сейчас HR-рутина автоматизирована, и система исправно выдаёт рекомендацию в виде ранга подходящих кандидатов из внутренней базы. Сейчас модель пока стоит «немножко сбоку» от основной базы данных, из-за этого пользоваться ею не очень удобно. Но полноценная интеграция — впереди.

Мы собираем обратную связь от HR. Массовую аналитику и ROI будем считать после полной интеграции, когда системой начнут активно пользоваться. Но первые отзывы — позитивные: рекрутеры отмечают, что стало проще находить релевантных кандидатов и сократилось время подбора — не кардинально, но ощутимо. Точные цифры обязательно приведём в следующей статье, когда отработаем на больших данных.

Теперь думаем над тем, как всё это улучшить. Вот наш ту-ду-лист:

  • Полноценная интеграция модели в основную HR-систему.
  • Улучшение качества исторических данных и процессов.
  • Тест LLM для анализа текстов резюме и вакансий — если пройдём по экономике и безопасности.
  • Расчёт реальной экономии времени и денег после внедрения.
  • Отладка метрик, по которым HR-специалисты смогут понимать, почему модель выдала те или иные рекомендации.

Если у вас был опыт внедрения похожих систем — расскажите: что ждёт нас дальше? Что у вас сработало, а что — нет? Какие подводные камни появились после интеграции? Мы будем рады обсудить это в комментариях.
Теги:
Хабы:
Всего голосов 32: ↑28 и ↓4+31
Комментарии51

Публикации

Информация

Сайт
greenatom.ru
Дата регистрации
Численность
5 001–10 000 человек