Skip to content

PasaOpasen/ContentDetector

Repository files navigation

ContentDetector

Detect hard/soft skills from resumes in Russian.

Экспертная система для определения профессиональных/мягких навыков из резюме и вакансий на русском языке.

Ссылка на видео

1

About

Разработанный модуль определяет из текста резюме/вакансий профессиональные навыки (например, 1C, .NET) и "мягкие" навыки (обучаемость, коммуникабельность и т. п.). Алгоритм имеет гибкую структуру концепций и может применяться для самых разных рекомедательных систем.

Наиболее оптимальный сценарий использования этого модуля заключается в том, чтобы произвести анализ созданной вакансии/резюме и предложить пользователю (как работнику, так и работодателю) набор выявленных навыков, а также их редактирование.

О подходе

Над текстом резюме/вакансии проводится очистка: удаляются лишние символы, стоп-слова, ссылки и т. п. Таким образом, текст сводится к набору фраз.

Из этого набора фраз извлекаются униграммы и биграммы, производится их стемминг. Результаты объединяются в облако понятий, откуда извлекаются понятия, представляющие интерес.

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

Как этим пользоваться

Основная функция модуля — get_content_from_text из файла detector.py, принимающая на вход список строк из резюме. Главной её задачей является вывод результата, представимого в виде списка навыков (каждый навык — это просто строка).

lines = ['Программист сайтов Битрикс и Битрикс 24.',
             'Партнёр Битрикс и Битрикс 24, интеграция Битрикс 24.',
             'Создание и продвижение сайтов, интернет магазина, лендинга.',
             'Дизайн, верстка, интернет маркетинг. Seo, топ 10, директ, соц сети.',
             'Так же работаю с любыми другими движками сайтов.',
             'Парсинг сайтов.','Стаж более 15 лет на фрилансе.']
    
print(get_content_from_text(lines))
# ['дизайн', 'дизайн сайтов', 'рекламные сервисы', 'разработка сайтов', 'верстка сайтов', 'оптимизация сайтов', 'Битрикс']
    
lines = ['Профессиональный опыт в сфере IT 10 лет.',
         'Опыт работы и поддержки информационных систем (сервисов) в крупных компаниях, предприятиях и гос. учреждениях.',
         '"KSB" "Россконгресс" "Burger King" "KFC"',
         'Высокая обучаемость.',
         'Грамотная речь, умение вести переговоры.',
         'Знание Английского языка: базовые знания.',
         'Управление персоналом, распределение задач.',
         'Умение самостоятельно проводить анализ и выявлять причины возникновения ошибок, и проблем.',
         'Коммуникабелен, стрессоустойчив. Без вредных привычек.']
    
 print(get_content_from_text(lines))
 # ['информационные системы', 'английский', 'грамотность', 'дружелюбность', 'организованность', 'обучаемость', 
 #'стрессоустойчивость', 'коммуникабельность', 'лидерские качества', 'умение убеждать', 'Burger King', 'KFC', 'KSB']   
    
 lines = ['Студент Тольяттинской Академии управления',
          'В данный момент нахожусь на дистанционной форме обучения, поэтому ищу работу с графиком полной рабочей загруженности.',
          'Имею четкую профессиональную траекторию и всегда ответственно подхожу к выполнению возложенных на меня задач.']
    
 print(get_content_from_text(lines))
 #['обучаемость', 'лидерские качества', 'ответственность']
 

Получение навыков выполяется с помощью двух алгоритмов, которые будем называть основным и дополнительным. На основной алгоритм может приходиться от 60% до 100% результата.

Функция имеет необязательный аргумент use_wiki = True. Его назначение в том, чтобы активировать/дезактивировать поиск неизвестных основному алгоритму понятий с помощью wikipedia API. Это имеет смысл, если в резюме/вакансии преобладают IT-термины, однако поиск может занять несколько секунд. В первом из приведённых примеров с помощью этого дополнительного алгоритма были найдены признаки 'Burger King', 'KFC', 'KSB' (Полученные результаты дополнительного алгоритма записываются в конец списка).

Также функция имеет аргумент vocab = None, что значит — использовать для основого алгоритма словарь по умолчанию. Через этот аргумент можно передать функции любой другой словарь, представляющий собой ассоциативный массив, в котором строке (маркеру) ставится в соотвествие список строк (список навыков).

Строки резюме можно прочесть из текстового файла:

with open("my_resume.txt", 'r', encoding = 'utf-8') as f:
     lines = f.readlines()
print(get_content_from_text(lines, use_wiki = False))

#['научная деятельность', 'Adobe Illustrator', 'TypeScript', 'mathcad', 'Microsoft Office', 'plotly', 'Keras', 'верстка сайтов', 'HTML', 'пакет Adobe', 'грамотность',
#'программирование', 'Java', 'SQL', 'Data Science', 'LINQ', 'математические пакеты', 'Python', 'D', 'Scikit-learn', 'Delphi', 'статистика', 'Visual Studio', 'C++', 'фарси',
#'высшая математика', 'английский', 'LaTeX', '.NET Framework', 'Windows Forms', 'чистый код', 'документация', 'разработка сайтов', 'гибкость', 'Developing', 'базы данных',
#'Windows', 'JavaScript', '.NET', 'R (programming language)', 'Desktop', 'Microsoft Excel', 'git', 'тестирование кода', 'Numpy', 'обучаемость', 'ООП', 'Pandas', 
#'Microsoft Word', 'Машинное обучение'

Выше считывается файл с моим резюме (источник).

Как это работает

Задача выполняется в два этапа:

  1. Очистка текста резюме/вакансии и создание n-грамм;
  2. Преобразование n-грамм в набор признаков с использованием одного или двух алгоритмов в зависимости от значения аргумента use_wiki.

Преобразование текста в набор n-грамм

На вход функции подается список строк (вместе — текст). Для получение результата, функция должна проделать следующие шаги:

  • Для каждой строки удаляются пробельные символы по краям, удаляются пустые строки

  • Из текста удаляются все ссылки, адреса электронной почты, имена телеграм-ботов и т. п.

  • Удаляются (заменяются пробелом) все символы, которые не являются символами алфавита, числами или некоторыми потенциально несущими смысл символами ('.' и ',' говорят о сепарации смысла, '+' может быть частью 'C++', '#' может быть частью 'C#', 'F#')

  • Набор строк увеличивается за счёт разбиения каждой строки по запятым (','). Здесь мы исходим из того, что конструкции, перечисляемые через запятую, не несут особой информации в совокупности — только отдельно друг от друга.

  • Аналогично происходит разбиение по стоп-словам (то есть если в строке есть стоп-слово, оно удаляется, а то, что было слева, и то, что было справа, становятся отдельными строками). Используемые стоп-слова хранятся в файле; сперва использовались только стоп-слова русского и английского языков из NLTK, потом были добавлены основные русские предлоги и русские символы сами по себе. Этот список можно легко расширять и редактировать при необходимости.

  • Как и с запятыми, происходит разбиение по точкам, однако с учётом сокращений: в строках "мат. алгоритмы", "это, это и т. д и т. п." содержится по одному предложению, несмотря на наличие точки.

  • Удаляются конструкции типа "т.", "п.", "1.", "2."

  • Для каждой полученной строки создаются 1- и 2-граммы (ничего не мешает добавить 3-граммы, 4-граммы и т. д., если это целесообразно).

  • Среди полученного множества n-грамм удаляются те, что не содержат алфавитных символов. Считается, что они не несут информации о навыках.

1

Пример

Возьмём следующее резюме:

Портфолио: www.behance.net/umkabear

Showreels: https://vimeo.com/341297601, https://vimeo.com/423526551



Знания пакета Adobe: Photoshop, Illustrator, InDesign.

Поверхностные знания Figma, Adobe After Effect.



Важное: к работе могу приступить с июля-августа. В приоритете вакансии с указанием зп, но готова обсуждать.

После удаления пустых строк и ссылок оно выглядит так:

Портфолио: 
Showreels: , 
Знания пакета Adobe: Photoshop, Illustrator, InDesign.
Поверхностные знания Figma, Adobe After Effect.
Важное: к работе могу приступить с июля-августа. В приоритете вакансии с указанием зп, но готова обсуждать.

После удаления лишних символов и разбиения по запятым:

Портфолио
Showreels 

Знания пакета Adobe Photoshop
 Illustrator
 InDesign.
Поверхностные знания Figma
 Adobe After Effect.
Важное к работе могу приступить с июля августа. В приоритете вакансии с указанием зп
 но готова обсуждать.

После разбиения по точкам и стоп-словам:

Портфолио
Showreels
Знания пакета Adobe Photoshop
Illustrator
InDesign
Поверхностные знания Figma
Adobe After Effect
Важное
работе могу приступить
июля августа
приоритете вакансии
указанием зп
готова обсуждать

В результате получены n-граммы:

готова обсуждать
пакета Adobe
вакансии
Adobe After
Знания
зп
приступить
Photoshop
обсуждать
After
работе могу
знания Figma
Adobe Photoshop
Figma
июля
Знания пакета
Поверхностные
знания
приоритете вакансии
Adobe
могу приступить
августа
Поверхностные знания
Важное
могу
InDesign
приоритете
готова
After Effect
пакета
Effect
работе
указанием зп
июля августа
Illustrator
Showreels
указанием
Портфолио

Данный процесс для большого резюме можно посмотреть в этом файле.

Алгоритм на основе графов

Концепция графа

Для определения смысла n-граммы используется ориентированный несвязный граф понятий. Он похож на дерево, но является несвязным, поскольку многие пары навыков никак не связаны друг с другом (например, 1С и пунктуальность). При этом узлы этого графа могут иметь нескольких родителей: например, известный фреймворк tensorflow означает не только машинное обучение, но и язык Python как таковой.

Отметим, что набор фраз (строк), которые тесно связаны друг с другом, являются либо значимыми (навыками, которые можно выводить), либо незначимыми (маркерами, вариациями навыков, которые связаны с конкретными значимыми признаками). Например, "Microsoft Office" — это значимый признак (навык), который является родителем "ms office" и "майкрософт офис", поэтому, если мы встречаем "ms office" внутри n-граммы, это означает "Microsoft Office".

Фрагмент такого графа понятий представлен на рисунке:

1

Это часть реального графа, с которым работает алгоритм. Цельный граф представлен по ссылке. Записывается этот граф при помощи такого файла. Для удобного расширения графа можно разработать язык программирования с умным компилятором, подсветкой синтаксиса и т. п.

Данный граф — абстракция над некоторой базой данных, хранящей взаимосвязи между понятиями (навыками и их маркерами). При реальной работе не происходит поиска на графе с рекурсиями и т. п. — вся эта работа выполняется при "компиляции" графа и её результаты кешируются в файл с ассоциативным массивом, а уже этот массив передаётся функции get_content_from_text через аргумент voc. Благодаря этому функция обрабатывает резюме очень быстро.

Практическое применение

На практике используется очень простой алгоритм, который действует по следующему принципу:

  • Для каждой n-граммы проводится стемминг, то есть выделение основы, с учётом того, что n-грамма может быть как словом, так и группой слов.

  • Все результаты стемминга объединяются во множество. Таким образом, получается некий мешок информации о вакансии/резюме.

  • Остаётся узнать, есть ли в этом мешке навыки (или маркеры), которые представляют интерес для нашей задачи. То есть является ли основа (результат стемминга) какого-либо узла графа подмножеством этого мешка.

  • Если произошло совпадение с каким-то навыком, этот навык выводится. Если произошло совпадение с каким-то маркером, этот маркер приведёт к одному или нескольким навыкам, которые будут получены.

1

Примечание: ввиду использования стемминга нет необходимость записывать/запоминать все вариации слова с учётом падажей и частей речи. Слова, связанные с английским языком, из фраз "хорошо знаю английскИЙ", "читаю статьи на англискОМ", "владею англискИМ языком" будут сведены к одной и той же основе "англиск" и отнесены к навыку владения английским. Используется стемминг вместо лемматизации, поскольку стемминг значительно быстрее и более качественно приводит к основе; при лемматизации же для слов "общаться" и "общение" основа будет совершенно разная, так как это разные части речи.

Запасной алгоритм

Помимо основного алгоритма с графом, на котором сделан акцент, имеется вспомогательный алгоритм, использующий API Википедии.

Идея заключается в том, чтобы для незнакомых основному алгоритму понятий (предположительно IT-терминов) искать совпадения с темами из Википедии, используя разные вариации расстояния Левенштейна. Если некоторые условия выполнены, будет выведен результат.

Данный процесс хорошо кешируется, поэтому периодически можно видеть, есть ли какие-то популярные темы, не охваченные основным алгоритмом (если тема популярна и не охватывается основным алгоритмом, ответ будет искать вспомогательный), и добавлять их в граф. Таким образом, в процессе работы системы будет происходить её обучение. Это особенно удобно, потому что сам основной алгоритм устроен так, что с обновлением данных становится только лучше. Кроме того, кеширование гарантирует, что уже увиденные запросы не будут требовать сетевых ресурсов при повторении, поэтому вся система будет работать быстро.

Почему не нейронная сеть?

Для нейронных сетей задача выделения из текста навыков определённой тематики (в данном случае, профессиональных навыков) пока едва ли разрешима. Это задача многоклассовой классификации со множественным выходом, где количество классов может достигать нескольких сотен (насколько я знаю, сейчас есть сети, поддерживающие только десяток классов). Исходя из вышесказанного выделим причины против использования нейронной сети:

  • Для достижения приемлемых результатов потребуется огромный объём размеченных данных, потребуется группа людей, которые должны будут рассмотреть не меньше чем 10 000 резюме и каждому резюме поставить в соотвествие набор навыков, а также договориться, какие именно навыки нужны и какими именно словами их выражать.

  • Даже если такой датасет будет создан, а нейронная сеть правильно составлена и обучена (что совсем непросто сделать), всё, к чему эта нейронная сеть прийдет, это выявление зависимости между такими-то словами и таким понятием и между таким понятием и таким понятием. То есть то, что должна будет обнаружить нейронная сеть, это то, что мы и так можем записать в наш граф, потому что мы это уже знаем благодаря нашей биологии и нашему опыту. Хочу отметить, что создавать нейронную сеть не обязательно, если мы можем сразу записать результаты на основе наших знаний, что будет быстрее, выгоднее и даст лучшее качество.

Перспективы развития

Для улучшения этого проекта можно проделать следующие шаги:

  • разработать систему, упрощающую процесс разработки графа понятий; в перспективе это — умный компилятор, указывающий на ошибки и неоднозначности, подсветка синтаксиса, компоновка файлов (можно создавать один файл с профессиональными навыками, другой — с мягкими, а при компиляции они объединятся в один граф). Вдобавок, есть несколько идей, как упростить сам подбор понятий; например, можно произвести анализ значимостей для n-грамм и работать с наиболее значимыми из них.

  • выдавать признаки, разбитые по категориям (вместо просто набора признаков), чтобы пользователю было проще обнаруживать признаки интересующих категорий

  • определять специализацию резюме с помощью нейронных сетей (IT, сфера услуг и т. п.)

  • определять опыт с помощью нейронных сетей

  • поскольку используется граф понятий, можно множеством способов определить степень близости между резюме и вакансией, за счёт чего давать рекомендации человеку как по поводу наилучшей работы, так и по поводу смежных навыков, за счёт обретения которых тот станет значительно востребованнее

Необходимые ресурсы:

  • Система уже сейчас выдает хорошие результаты, для расширения проекта требуются специалисты (3-5 человек), которые разбираются в специфике рынка труда в представляющих интерес областях (например, общепит, документооборот, строительство). Их задачей будет помощь в расширении графа понятий, которое приведет к более масштабной структуре проекта.

  • Для создания крупной системы (если окажется, что небольшой системы будет недостаточно) потребуется создать небольшой «язык программирования» и некоторые средства работы с ним. Здесь нужны 1-2 человека, имеющих опыт с редакторами кода и создании интерпретаторов/компиляторов.

Дополнительные ресурсы:

  • Для определения опыта человека через нейронные сети нужен достаточно крупный (не меньше 10 000 наблюдений) набор размеченных резюме вида "резюме/вакансия" --> категория опыта (неизвестно, нет опыта, до 3 лет, от 3 до 6 и т. п.)

О команде

Краснодарская команда разработчиков «АК». Состав;

  • Дмитрий Пасько: капитан команды, 22 года, Data Scientist, разработчик Python / R / C#, специализируется на реализации мат. алгоритмов, рефакторинге и ускорении кода, анализе данных и машинном обучении. Окончил бакалавриат КубГУ (математика и компьютерные науки). GitHub: https://github.com/PasaOpasen

  • Илья Малахов, 23 года. Закончил бакалавриат КубГУ (математика и компьютерные науки). Работает Python-инженером в компании «ООО НДФЛКА», специализируется на распознавании и классификации документов фирмы. По результатам его работ опубликованы тезисы докладов в 2019 и 2020 годах. в трудах всероссийской научной конференции «Прикладная математика XXI века: современные проблемы математики, информатики и моделирования».

  • Александр Петрушин, 22 года, Full-stack Engineer. Закончил бакалавриат Крымского Федерального Университета (Прикладная математика). На данный момент обучается в магистратуре КубГУ по направлению «Математика и компьютерные науки» и работает в компании Ronas IT в г. Краснодар. GitHub: https://github.com/al-petrushin