В 2000 году Enron была одной из крупнейших компаний США. К 2002 году он обанкротился из-за широко распространенного корпоративного мошенничества. В результате последовавшего судебного разбирательства электронные письма и финансовая информация многих сотрудников были обнародованы.

Цель проекта

Этот проект направлен на классификацию бывших сотрудников Enron как «лиц, представляющих интерес», или людей, которых власти должны опросить в ходе расследования, на основе набора данных, описанного выше.

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

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

Этот проект был выполнен в рамках моей программы Data Analyst Nano Degree от Udacity.

Полный код проекта доступен в моем репозитории github: https://github.com/Jacobdudee/EnronModel/blob/master/

Обзор набора данных

Функции

Существует сочетание электронной почты и финансовых функций:

  • 'poi' [целевая переменная]
  • 'оплата труда'
  • «отсрочка_платежей»
  • «всего_платежей»
  • «кредит_авансы»
  • «бонус» - «restricted_stock_deferred»
  • «отложенный_доход»
  • 'total_stock_value'
  • 'расходы'
  • «исполненные_фондовые_опционы»
  • «long_term_incentive»
  • «restricted_stock»
  • 'director_fees'
  • «от_пои_до_этого_человека»
  • «от_этого_человека_к_пои»
  • «общая_квитанция_с_пои»
  • «to_messages»
  • «от_сообщений»

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

Обнаружение выбросов

Был один выброс с названием «ВСЕГО». Поскольку финансовые данные поступают из электронной таблицы, вероятно, это была просто какая-то причуда. Он был удален.

Отсутствующие функции

Давайте посмотрим на частоту ненулевых значений для каждой функции:

salary 94 0.65
to_messages 86 0.59
deferral_payments 38 0.26
total_payments 124 0.86
exercised_stock_options 101 0.7
bonus 81 0.56
restricted_stock 109 0.75
shared_receipt_with_poi 86 0.59
restricted_stock_deferred 17 0.12
total_stock_value 125 0.86
expenses 94 0.65
loan_advances 3 0.02
from_messages 86 0.59
other 92 0.63
from_this_person_to_poi 86 0.59
director_fees 16 0.11
deferred_income 48 0.33
long_term_incentive 65 0.45
email_address 111 0.77
from_poi_to_this_person 86 0.59

У многих функций здесь действительно много пропущенных значений (> 50%). Зарплата, to_messages, total_payments и общая стоимость акций — вот некоторые из наиболее полных функций.

Есть некоторые функции, по которым почти нет данных, например, кредит_авансы или директор_комиссия. В любом случае качество данных в этом наборе данных не близко к идеальному.

Исследование данных

Сколько лиц, представляющих интерес, в этом наборе данных?

number of poi: 18

сколько людей в этом наборе данных?

Number of points in dataset: 146

сколько функций на человека?

Number of features/person: 21

Какова взаимосвязь между окладом и долгосрочной мотивацией?

Предварительная обработка и выбор признаков

Разработка функций

Как обсуждалось ранее в разделе обзора, я решил создать 2 новые функции:

  1. perc_from_poi: процент электронных писем, которые человек получил от пои.

2. perc_to_poi: процент электронных писем, отправленных человеком в poi.

Логика этих функций заключается в том, что POI могли отправлять друг другу много сообщений, обсуждая мошеннические действия. Эти функции предназначены для количественной оценки этого в обобщенном виде, и, как мы надеемся, имеют более высокую прогностическую силу, чем количество сообщений из POI без всего контекста. Это было рассчитано по следующим формулам:

  • perc_from_poi = from_poi_to_this_person/from_messages
  • perc_to_poi = to_poi_from_this_person/to_messages

Есть некоторые опасения по поводу использования функций perc_from/to_poi, так как это может привести к утечке или намеку на информацию из будущего (независимо от того, идентифицирован ли кто-то как POI).

Масштабирование функций

Для данных не было выполнено масштабирование признаков.

Выбор функции

Как только эти две новые функции были добавлены к нашим данным, у нас теперь было 23 функции. При небольшом количестве POI в наборе данных (18 из 145, ~ 12%) создание модели со всеми 23 функциями созрело для переобучения. Один из способов описать переоснащение — это когда модель улавливает шум в наборе данных при обучении, поэтому при введении новой информации она теряет предсказательную силу. Когда модель имеет большее количество функций, обязательно будет больше шума. Таким образом, нам нужно уменьшить количество признаков в наборе данных, что, в свою очередь, уменьшит вероятность переобучения.

Чтобы добиться этого с помощью этого набора данных, я буду использовать объект k-best из модуля предварительной обработки scikit-learn. Этот класс оценивает функции вашего набора данных и позволяет вам выбрать k наилучшего количества функций. В этом случае я выберу 7 лучших функций. Во время тестирования использование 10 лучших функций дало наилучшую производительность.

Помимо использования 10 основных функций, я использовал 5 и 7. При использовании 5 основных функций производительность по всем направлениям «Точность», «Точность» и «Отзыв» была ниже:

AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None, learning_rate=1, n_estimators=35, random_state=None) Accuracy: 0.83992	Precision: 0.47150	Recall: 0.33500

Лучшие 10 функций имеют гораздо лучшую производительность:

AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None, learning_rate=1, n_estimators=35, random_state=None) Accuracy: 0.88040	Precision: 0.56527	Recall: 0.44600

Но в конечном итоге использование топ-7 обеспечило наилучшую производительность в целом, как вы увидите в результатах ниже.

Используемые функции

Самыми сильными характеристиками были реализованные опционы на акции, общая стоимость акций, бонусы, зарплата и процент электронных писем для пои. Следующие функции были выбраны kbest:

['loan_advances', 'restricted_stock_deferred', 'deferred_income', 'expenses', 'exercised_stock_options', 'long_term_incentive', 'perc_from_poi']

Моделирование

Методология

Настройка гиперпараметров

Гиперпараметры — это различные ручки, которые вы можете поворачивать для алгоритма машинного обучения.

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

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

Поиск по сетке — очень мощный метод, так как он систематически работает с несколькими комбинациями настроек параметров, выполняя перекрестную проверку, чтобы определить, какая настройка дает наилучшие характеристики.

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

Оценка модели

При построении модели классификации точность не всегда может быть лучшим показателем для проверки модели, особенно если у вас несбалансированный целевой класс. В этом случае у нас есть несбалансированный целевой класс, всего 18 poi из 141 выборки. Здесь вступают в действие память и точность.

Отзыв — это вероятность того, что алгоритм правильно идентифицирует класс при условии, что класс является предсказанным классом, в этом случае правильно классифицирует все POI. Точность — это скорость угадывания целевого класса при условии, что это действительно целевой класс. В данном случае это скорость правильного угадывания пои.

Цель этого проекта — получить модель с точностью и метриками отзыва выше 0,3.

Проверка

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

Из-за небольшого размера набора данных (и целевого класса) нам нужна стратегия, которая максимально использует данные. Это означает, что стратегия kfold здесь имеет смысл. Тем не менее, вы также хотите сохранить процент целевого класса, чтобы не было фолдов с 1 или 2 пои, а некоторых с 10.

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

Результаты

Лучшая модель

Наилучшие результаты были получены при использовании классификатора AdaBoost со скоростью обучения, алгоритмом и количеством оценок, настроенных с помощью поиска по сетке. Окончательные гиперпараметры были:

- a learning rate of 1, possible params: [0.1,0.25,0.5,0.75,1] 
- 35 estimators, possible params: [25,35,40,50,75,100] 
- SAMME.R algorithm, possible params: [SAMME.R, SAMME] 
AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None, learning_rate=1, n_estimators=35, random_state=None) 
Accuracy: 0.88364	Precision: 0.62408	Recall: 0.46650	
F1: 0.53391	F2: 0.49131 
Total predictions: 14000	True positives: 933	
False positives: 562	False negatives: 1067	
True negatives: 11438

С точностью 88%, точностью и отзывом выше 0,3 у нас есть победитель.

Модель была права в 88% случаев, а когда она догадывалась, что кто-то представляет интерес, она оказывалась права немногим менее чем в половине случаев (47%). Однако он был лучше при выявлении лиц, не представляющих интереса, с правильными отрицательными прогнозами в 91,7%.

При использовании эта модель может предсказать, не является ли кто-то POI, с гораздо большей скоростью, чем прогнозирование того, является ли кто-то POI.

('Rate Identified non-poi: ', 0.915)

Другие модели:

Я протестировал еще 3 модели, каждая из которых была выбрана из 10 тысяч лучших функций.

Первоначально я использовал гауссовую наивную байесовскую модель, но точность и полнота были слишком низкими для этого упражнения, почти не достигнув желаемого порога 0,3:

GaussianNB(priors=None) Accuracy: 0.79500	Precision: 0.29142	Recall: 0.37550	F1: 0.32816	F2: 0.35502 Total predictions: 15000	True positives: 751	False positives: 1826	False negatives: 1249	True negatives: 11174

Затем я использовал классификатор случайного леса. Я настроил max_depth, max_features, min_samples_split и n_estimators с помощью поиска по сетке для отзыва. Хотя точность была выше, чем у наивного байесовского метода, он дал не очень хорошие результаты для припоминания:

RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini', max_depth=15, max_features=5, max_leaf_nodes=None, min_impurity_split=1e-07, min_samples_leaf=1, min_samples_split=15, min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1, oob_score=False, random_state=None, verbose=0, warm_start=False) Accuracy: 0.86007	Precision: 0.43391	Recall: 0.16250	
F1: 0.23645	F2: 0.18574 Total predictions: 15000	True positives: 325	False positives: 424	False negatives: 1675	True negatives: 12576

Затем я использовал логистическую регрессию, настроенную для C (параметр регуляризации):

LogisticRegression(C=10, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False) Accuracy: 0.67673	Precision: 0.07312	Recall: 0.12200
F1: 0.09144	F2: 0.10761 Total predictions: 15000	True positives: 244	False positives: 3093	False negatives: 1756	True negatives: 9907

Вывод

Был создан классификатор AdaBoost, который смог предсказать интересующего человека в наборе данных Enron с точностью 88%, точностью 56,5% и полнотой 44,6%.

Мы смогли предсказать, является ли сотрудник в какой-то степени интересным лицом. Модель была права в 88% случаев, но когда она догадывалась, что кто-то представляет интерес, она оказывалась права чуть меньше чем в половине случаев (46%).

использованная литература

Документация по обучению Scikit: http://scikit-learn.org/

Как создать пользовательскую легенду в matplotlib https://matplotlib.org/users/legend_guide.html

Идеи по настройке дерева решений: http://chrisstrelioff.ws/sandbox/2015/06/25/decision_trees_in_python_again_cross_validation.html

Несколько советов о том, как настроить gridsearch для результатов: https://www.kaggle.com/kevinarvai/fine-tuning-a-classifier-in-scikit-learn

Документальный фильм об Enron с предысторией и полезной информацией: https://en.wikipedia.org/wiki/Enron:_The_Smartest_Guys_in_the_Room

Программа Udacity Data Analyst nano Degree для поддержки кода и уроков: https://www.udacity.com/course/data-analyst-nanodegree--nd002

Первоначально опубликовано на gist.github.com.