Pull to refresh

Парсинг резюме

Reading time 7 min
Views 23K
Те кто сталкивался с задачами автоматизированного анализа резюме, представляют современное состояние дел в этой области — существующие парсеры в основном ограничиваются выделением контактных данных и ещё нескольких полей, таких как «должность» и «город».

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

Живой пример (кусок XML результата анализа резюме от одного из лидеров области Sovren):

    <EmployerOrg>
        <EmployerOrgName>ООО Звезда-DSME</EmployerOrgName>
        <PositionHistory positionType="directHire">
            <Title>Ведущий специалист отдела развития информационных систем</Title>
            <OrgName>
                <OrganizationName>ООО Звезда-DSME</OrganizationName>
             </OrgName>

Парсер Sovren прекрасно справился с выделением полей. Ребята не зря занимаются этим делом без малого 20 лет!

Но что дальше делать с «Ведущий специалист отдела развития информационных систем»? Как понять, что же это за должность, насколько опыт работы этого человека релевантен для той или иной вакансии?

Если ваша задача — поиск сотрудника под требования вакансии или наоборот — вакансии под опыт и пожелания кандидата, то поиск ключевых слов, сравнение bag of words дают посредственные результаты. Для объектов, которым соответствует множество возможных синонимичных наименований такой подход не сработает.

Для начала нужно нормализовать наименования, превратить «специалистов в чём-либо» в программистов, сисадминов и прочих отоларингологов.

Для этого придётся составить базу знаний, таксономию объектов. Причём специфика такова, что недостаточно описать, например только строителей — люди меняют области деятельности и в резюме строителя могут встречаться и другие, не связанные со строительством места работы.

И если таксономия будет описывать только строительство — в текстах, относящихся к другим областям деятельности, будут ложные срабатывания. В строительстве «архитектор» — это одно, а в IT — совсем другое. «Операция», «Акция», «Объект» и множество словосочетаний, содержащих эти слова — примеры неоднозначностей, которые необходимо разрешать.

Простая нормализация тоже не спасёт отца русской демократии. Фантазия людей пишущих резюме и составляющих штатные расписания не перестаёт удивлять. К сожалению для нас, разработчиков, это значит, что в общем случае из строки описывающей объект идентифицировать этот объект нельзя. То есть вы конечно можете попытаться обучить какой-нибудь классификатор, скармливая ему поле и желаемую должность.
И он даже будет работать. На «бухгалтерах», «секретарях», «программистах».
Только вот в резюме люди пишут «специалист отдела N» и понять, бухгалтер он или секретарь, можно лишь по контексту, набору выполняемых обязанностей.

Казалось бы — хорошо, учтём контекст, пусть классификатор обучается ещё и на обязанностях. Так, да не так — при определении набора обязанностей та же проблема: неоднозначность трактовки, всякие непонятные анафоры).

Мы решили применить вероятностный (байесовский) подход:

Анализируя исходный текст, для всех строк (например, «архитектор», «работа
с клиентами») мы определяем множество всех возможных трактовок
(например, для «архитектора» это будет «архитектор зданий», «архитектор программного
обеспечения» и т.д.). В результате получается набор множеств трактовок. Затем
ищем такое сочетание трактовок из всех множеств, чтобы его правдоподобность
была максимальной.

Например:

  • 2007-2009 ООО Umbrella corp. Старший менеджер. Работа с ключевыми клиентами, поиск новых клиентов, переговоры, оформление сделок;

  • 2001-2007 ООО Рога и копыта. Менеджер по работе с клиентами. Консультирование покупателей по всем возникающим вопросам, оформление покупок, наличный и безналичный расчёт.

В обоих местах работы используется слово «менеджер» для обозначения совсем разных должностей. По контексту можно понять, что в первом случае должность действительно менеджерская, а во втором правильнее было бы назваться продавцом.

Для того, чтобы выбрать между «менеджером по работе с клиентами» и «продавцом» мы оцениваем правдоподобность сочетания этих должностей с найденными в этом месте работы навыками. При этом навыки могут таким же образом выбираться из нескольких вариантов, поэтому задача состоит в выборе наиболее правдоподобного сочетания из множества найденных в тексте объектов.

Количество объектов разных типов (навыки, должности, отрасли, города и пр.) очень большое (сотни тысяч в нашей базе знаний), поэтому пространство, которому принадлежат резюме очень-очень многомерное. Для обучения большинства machine learning алгоритмов понадобится астрономическое количество примеров.

Мы решили резать. То есть кардинально сокращать количество параметров и использовать результат обучения только там, где у нас есть достаточное количество примеров.

Для начала мы стали собирать статистику по сочетаниям кортежей признаков, например: должность-отрасль, должность-отдел, должность-навык. На основе этой статистики мы оцениваем правдоподобность новых, ещё не встречаемых ранее, сочетаний
объектов и выбираем лучшее сочетание.

В примере выше набор навыков склоняет парсер в сторону менеджера в первом случае и в сторону продавца во втором.

Использование простых счётчиков и оценки вероятности по Байесу позволяет получать хорошие результаты при небольшом количестве примеров. В нашей базе знаний сейчас около ста тысяч размеченных специалистами вакансий и резюме, и это позволяет разрешать большинство неоднозначностей для распространённых объектов.

На выходе мы получаем JSON объект, описывающий вакансию или резюме в терминах нашей базы знаний, а не в тех, которые придумал соискатель или работодатель.

Это представление можно использовать для точного поиска по параметрам, оценки («скоринга») резюме соискателя или сопоставления пар «резюме-вакансия».

Мы сделали простой интерфейс, в котором можно загрузить резюме (doc, docx, pdf (не картинкой) и другие форматы) и получить его представление в JSON. Только не забывайте про 152ФЗ! Не надо эксперементировать с резюме с реальными персональными данными :)

Например вот такое резюме:

Скрытый текст
Пупкин Василий Львович
г. Омск
тел +7923123321123

Ответственный и трудолюбивый менеджер по продажам.

Опыт работы

  • 2001-2002: ООО Бытдомуправ. Уборщик территории во дворе. Особенно добросовестно выполнял работы по уборке снега.
  • С 12.03.2005 по 30.01.2007 Магазин «Надежда». Старший киоскёр по продаже колбасы. Увеличил продажи колбасы на 146% за 2 месяца.
  • 2002-2003: ООО Кузремонт Жестянщик-кузовщик в автомастерской. Переделывал «Запорожцы» в «Мерседесы».

Дополнительно
Физически сильный, умный, красивый. Есть собственный автомобиль Range Rover Sport и права категории B.

Превращается в следующий JSON:

Скрытый текст
{
  "url": null,
  "name": "Менеджер по продажам, г. Омск",
  "skill_ids": [
    {
      "cv_skill_id": 5109999,
      "skill_name": "ответственность",
      "skill_id": 91,
      "skill_level_id": 1,
      "skill_level_name": "Базовый"
    },
    {
      "cv_skill_id": 5110000,
      "skill_name": "трудолюбие",
      "skill_id": 596,
      "skill_level_id": 1,
      "skill_level_name": "Базовый"
    },
    {
      "cv_skill_id": 5109998,
      "skill_name": "осуществление уборки",
      "skill_id": 1474,
      "skill_level_id": 1,
      "skill_level_name": "Базовый"
    },
    {
      "cv_skill_id": 5109997,
      "skill_name": "наличие автомобиля",
      "skill_id": 2688,
      "skill_level_id": 2,
      "skill_level_name": "Средний"
    }
  ],
  "description": "Описание отсутствует",
  "ts": "2016-09-14 06:00:51.136898",
  "jobs": [
    {
      "employer_id": null,
      "description": ": ООО Бытдомуправ. Уборщик территории во дворе. Особенно добросовестно выполнял работы по уборке снега",
      "department_id": null,
      "company_size_id": null,
      "industry_id": null,
      "start_date": "2001-01-01",
      "cv_job_id": 1812412,
      "company_size_name": null,
      "employer_name": null,
      "job_id": 336,
      "department_name": null,
      "industry_name": null,
      "end_date": "2002-01-01",
      "job_name": "Дворник"
    },
    {
      "employer_id": null,
      "description": ": ООО Кузремонт Жестянщик-кузовщик в автомастерской. Переделывал \"Запорожцы\" в \"Мерседесы\". Дополнительно Физически сильный, умный, красивый. Есть собственный автомобиль Range Rover Sport и права категории B",
      "department_id": null,
      "company_size_id": null,
      "industry_id": null,
      "start_date": "2002-01-01",
      "cv_job_id": 1812414,
      "company_size_name": null,
      "employer_name": null,
      "job_id": 268,
      "department_name": null,
      "industry_name": null,
      "end_date": "2003-01-01",
      "job_name": "Рабочий автосервиса"
    },
    {
      "employer_id": null,
      "description": "С 12.03.2005 по 30.01.2007 Магазин \"Надежда\". Старший киоскёр по продаже колбасы. Увеличил продажи колбасы на 146% за 2 месяца",
      "department_id": null,
      "company_size_id": null,
      "industry_id": 39,
      "start_date": "2005-03-12",
      "cv_job_id": 1812413,
      "company_size_name": null,
      "employer_name": null,
      "job_id": 354,
      "department_name": null,
      "industry_name": "Розничная торговля",
      "end_date": "2007-01-30",
      "job_name": "Продавец"
    }
  ],
  "cv_file_id": 16598,
  "favorite_industries": [
    {
      "name": "Розничная торговля",
      "industry_id": 39
    }
  ],
  "wage_min": null,
  "cv_id": 1698916,
  "favorite_areas_data": [
    [
      {
        "id": 198830,
        "name": "Российская Федерация",
        "level": 1
      },
      {
        "id": 10005,
        "name": "Сибирский федеральный округ",
        "level": 2
      },
      {
        "id": 88,
        "name": "Омская область",
        "level": 3
      },
      {
        "id": 727,
        "name": "г. Омск",
        "level": 4
      }
    ]
  ],
  "certificate_ids": [
    {
      "certificate_name": "Водительское удостоверение категории B",
      "certificate_id": 118,
      "cv_certificate_id": 604445
    }
  ],
  "cv_owner": "own",
  "favorite_jobs": [
    {
      "name": "Менеджер по продажам",
      "job_id": 112
    }
  ],
  "cv_status_id": 2,
  "filename": "test_resume.odt"
}

Извлечение персональных данных в парсере отключено, не ищите их в JSON.

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

Интересно, какие методики используете или использовали бы Вы? Особенно интересно, использует ли кто-нибудь семантический подход а-ля Compreno от ABBYY?
Tags:
Hubs:
+13
Comments 7
Comments Comments 7

Articles