Share |

воскресенье, 5 февраля 2017 г.

Сколько я стою на рынке. Как создавался сервис

Сколько я стою на рынке. Как создавался сервисХочу спецам рассказать, как создавал сервис Сколько я стою на рынке.
Идея очень простая: клиент заполняет анкету, сообщая данные о себе и своей работе, а я в ответ даю отчет, насколько зарплата клиента «в рынке». «В рынке» это среднее значение зарплаты, которое платят на рынке  при данных клиента.

Математика за этим такая: я построил регрессию, где Y выступает актуальная зарплата, а в качестве предикторов – порядка 20 факторов. Полный список их указан здесь Сколько я стою на рынке. И если реальная зарплата клиента ниже прогнозного значения, то он недооценен на рынке, и, наоборот, если зарплата клиента выше прогнозного, то ему платят больше.

Самый важный вопрос

Данные я насобирал сам. Два года назад я начал опрос ключевые факторы текучести персонала, за это время я набрал более 4 000 строк данных. Но модель я пока нарисовал только для коллег HR. У меня есть по HR 1 500 строк с зарплатой. Поэтому самым трудным было насобирать данные. Было и остается самым трудным, поэтому, если вы хотите мне искренне помочь, поучаствуйте сами и пригласите друзей поучаствовать в опросе Ключевые факторытекучести персонала.

Сложности создания модели

Этот раздел для тех, кто сам создает алгоритмы. Для меня проблема была не в настройке модели, не в тюнинге модели, а в создании именно сервиса. В настройке pipeline. Технически теперь это выглядит так: клиент заполняет анкету, я первым кликом загружаю его данные в машину, вторым кликом получаю результат, третьим кликом выгружаю данные в отчет. Но алгоритм в этом момент не просто строит прогноз зарплаты, но проделывает работу по преобразованию исходных, сырых данных опроса.
В этом для меня и кроется различие между статистикой и машинным обучением. Ниже я расскажу про то, какие проблемы я решал.

Dummy coding

Первая проблема, с которой я столкнулся – преобразование категориальных переменных в новом датасете. Уже начинающий аналитик знает, как преобразовывать категориальные переменные с помощью фиктивных переменных.
В итоге из одной переменной мы получаем N-1 фиктивных переменных, где N – число уровней категориальной переменной. В Python для этого существует прекрасная функция pandas.get_dummies()
Это просто на самом деле. Проблема возникает, когда мы на вход натренированной модели подаем новый датасет. Поскольку количество уровней категориальных переменных будет различаться, и будет различаться количество фиктивных переменных.
Т.е. если в датасете, на котором мы тренировали модель, у нас десять регионов, то фиктивных переменных будет девять. А потом мы хотим дать прогноз какому то одному клиенту из Нижнего Новгорода. Применяя dummy coding, мы получим одну фиктивную переменную. И размерность датасета для модели и нового датасета будут различаться. Модель не примет наш новый датасет. Нам нужно получить 9 колонок. Поначалу я хотел в каждый новый датасет добавлять искусственно созданных работников, в данных которых бы содержался полный набор уровней всех категориальных переменных. Но это неудобно!
В итоге решил эту проблему (но не с помощью pandas.get_dummies(), я применил в pipeline DictVectorizer(), хотя готов поспорить, кто -то из спецов по Python попробуют меня убедить, что надо использовать OneHotEncoder(). Можно его использовать, но путь долгий и через жопу), заложив в def fit датасет, на котором тренирую модель, а в def transform новый датасет. Если вы работаете в Python и работаете с class, то понимаете, о чем речь. Сейчас мое решение кажется понятным и простым, но убил я на него кучу времени.
Стаж
Другая проблема заключалась в том, что я создал несколько переменных, которых не было в опросе. Это такие переменные, как стаж и возраст. В опросе есть вопросы «Дата трудоустройства», «Дата увольнения», причем, если респондент работает на момент заполнения анкеты, то Дата увольнения остается пустой. Таким образом, если клиент работает на сегодня, то на входе его дата увольнения заполняется датой заполнения опросника, а потом получается стаж вычитанием из актуальной даты даты трудоустройства. Ну и естественно, применяется scaling, как впрочем, ко всем переменным в алгоритме
Текст
Текстовая переменная в исследовании появилась, когда я понял, что такой вариант работы с функционалом будет идеальным. От всякой классификации функционала работников я отказался изначально. Это была бы портянка на десятки поинтов, причем, даже в этом случае я бы не смог дать 100% совпадения, но, главное, в том же HR никто не занимается одной функцией. Поэтому я сделал просто: предложил респондентам «руками» описать свой функционал.
В работе непосредственно с самим текстом трудности существуют только в настройке правил. Кто-то свою функцию описывает как “c&b”, кто-то как “comp&ben”, я оба вариант привожу к варианту ”compben”. Поэтому работа такая не закончена, я стараюсь каждую новую запись проверить на наличие каких-либо профессионализмов с тем, чтобы привести к едином виду.
Техническая проблема для меня была заставить работать сетку (GridSearchCV) на настройку параметров текста (параметры TfidfVectorizer: ngram_range, max_df, min_df) с параметрами модели. Опытные спецы скажут, а что здесь такого, я отвечу: да, ничего такого нет, когда ты прошел хоть один раз, но первый раз не просто.
Спасибы
  • В первую очередь, спасибо курсу МИФИ на Курсере Обучение наразмеченных данных и преподавателю Эмели Драль, лекции которой были просты, ясны и вкусны из-за наличия кода. Собственно, на лекции Эмели я познакомился с pipeline, поначалу жжужал от этих трехэтажных кодов, не понимал, зачем этот OneHotEncoder нужен, когда есть простой и прекрасный get_dummies. К сожалению, жизнь не такая простая. Кстати говоря, Эмели не дала код по pipeline, который можно было просто брать и применять. Т.е. он сам по себе полезный, но не для моей задачи. Мой код в итоге кардинально отличается от даваемого на курсе лаже в том, как подаются переменные на вход. Но тем не менее, знание бесценно. И бесплатно!
  • Во-вторых, хочу выразить всем чувакам со stackoverflow! Там куча крутых перцев, и я там на сегодня заработал уже больше 300 баловJ И не только вопросами, я уже отвечаю иногда. Это крутой ресурс для развития.
  • В третьих, отдельное спасибо Zac Stewart с постом Usingscikit-learn Pipelines and FeatureUnions. Я не постеснялся задать два вопроса. В итоге сам решил свою проблему, но, тем не менее, без общения и без этого поста я не уверен, что сервис был бы создан. И если вы будете создавать свои pipeline-ы, то вы мимо этого поста не пройдете, на него ссылаются многие другие спецы. 
  • Спасибо Кириллу Захарову - аналитику из Днепра, Украина, который помогал мне создавать опрос.
  • Спасибо Людмиле Роговой, которая терпеливо выслушивала мои размышления по поводу алгоритма.
  • И спасибо рынку, который пинает и заставляет искать решения.

Последнее

Если вы причисляете себя к IT рынку, если вы называете себя IT специалистом (Россия), я сделаю вам отчет (по ссылке шаблон) совершенно бесплатно, если вы корректно и полно пройдете мой опрос. И напишите мне edvb()yandex.ru

Это бесплатно! Можете в качестве благодарности покликать директ рекламу у меня в блоге. 

Комментариев нет:

Отправить комментарий

рек