机器学习安然数据集分析报告

来源:互联网 发布:西安交通大学宿舍网络 编辑:程序博客网 时间:2024/04/19 12:45

项目背景

安然曾是 2000 年美国最大的公司之一。辉煌时期,市值高达700亿美元。2002 年,由于其存在大量的企业欺诈行为,这个昔日的大集团以极快的速度土崩瓦解。 在随后联邦进行的调查过程中,大量有代表性的保密信息进入了公众的视线,包括成千上万涉及高管的邮件和详细的财务数据。 你将在此项目中扮演侦探,运用你的新技能,根据安然丑闻中公开的财务和邮件数据来构建相关人士识别符。利用机器学习算法进行数据分析,从邮件和财务数据中找出犯罪嫌疑人。

数据集初步探索

加载数据集

首先我们加载数据集

with open("final_project_dataset.pkl", "r") as data_file:    data_dict = pickle.load(data_file)

电子邮件和财务 (E+F) 数据字典被存储在 pickle 文件中,该文件可直接存储和加载 python 对象,非常方便。

数据集初步分析

分析发现我们读取的数据数据实际上是以字典形式进行。随机取一个字典,其存储结构如下所示:

{METTS MARK:{'salary': 365788, 'to_messages': 807, 'deferral_payments': 'NaN', 'total_payments': 1061827, 'exercised_stock_options': 'NaN', 'bonus': 600000, 'restricted_stock': 585062, 'shared_receipt_with_poi': 702, 'restricted_stock_deferred': 'NaN', 'total_stock_value': 585062, 'expenses': 94299, 'loan_advances': 'NaN', 'from_messages': 29, 'other': 1740, 'from_this_person_to_poi': 1, 'poi': False, 'director_fees': 'NaN', 'deferred_income': 'NaN', 'long_term_incentive': 'NaN', 'email_address': 'mark.metts@enron.com', 'from_poi_to_this_person': 38}}

在预处理此项目时,我们已将安然邮件和财务数据与字典结合在一起,字典中的每对键值对应一个人。 字典键是人名,值是另一个字典(包含此人的所有特征名和对应的值)。 数据中的特征分为三大类,即财务特征、邮件特征和 POI 标签。

财务特征 : [‘salary’, ‘deferral_payments’, ‘total_payments’, ‘loan_advances’, ‘bonus’, ‘restricted_stock_deferred’, ‘deferred_income’, ‘total_stock_value’, ‘expenses’, ‘exercised_stock_options’, ‘other’, ‘long_term_incentive’, ‘restricted_stock’, ‘director_fees’] (单位均是美元)

邮件特征 : [‘to_messages’, ‘email_address’, ‘from_poi_to_this_person’, ‘from_messages’, ‘from_this_person_to_poi’, ‘shared_receipt_with_poi’] (单位通常是电子邮件的数量,明显的例外是 ‘email_address’,这是一个字符串)

POI 标签 : [‘poi’] (boolean,整数)

数据特征

分析这个数据字典,我们发现每个人一共有20个特征可以用于分析,其中的poi不是特征,而是label/target

数据点总数

分析整个数据字典,我们发现一共有146个数据点(人)。

POI统计

POI( Person of interest )是嫌疑犯的意思,数据集有一个标签(label)就是嫌疑犯,所以我们只需要统计
data_dict[preson name][‘poi’] == 1
的数量就可以了。

统计发现有18个。

缺失的特征

数据集并不是每个特征后都有明确的值,有很多信息的特征是缺失的。对于salary特征,很多人的信息就是NaN。146个数据点(人)中,只有95个人有salary的具体信息。有111个人有邮箱地址,其他人的邮箱地址信息为NaN。

异常值调查和处理

我们在分析财务数据salary和bounds之间的关系时发现了一个极为异常额异常值,如下图所示

这里写图片描述

明显在右上角有一个极为异常的点,奖金和薪水远远高于其他人。我们通过代码寻找一下奖金和薪水都极高的人,看是否还有其他的异常值。

data_dict = sorted(data_dict.items(), key = lambda x : x[1]["salary"] ,reverse=True)for x in data_dict :    if x[1]['salary'] > 1000000 and x[1]['bonus'] > 5000000 :        print x[0], x[1]['salary'], x[1]['bonus']

审查发现一共有三个异常值。第一个为TOTAL,很明显不是一个人名,而且薪水和奖金都极度异常,我们将他作为真正的异常值删除掉。

data_dict = dict(data_dict)data_dict.pop('TOTAL', '52')

剩下两个分别是SKILLING JEFFREY K和LAY KENNETH L。他们分别是安然公司的CEO和董事长,他们是整个安然欺诈事件中最大的嫌疑犯。他们能有这么高的薪水和奖金也就不足为奇了。他们不是真的异常值,因此不对他们进行处理。

优化特征选择

创建新的特征

我们有特征to_messages和from_poi_to_this_person这两个特征,因此我想我们可以建立一个新的特征命名为 to_poi_ratio,其值为from_poi_to_this_person和to_messages的比值,比值越大也就意味着这个人收到的邮件中来自嫌疑人的邮件越多,往往也就意味着这个人和嫌疑人的关系越密切,很有可能这个人也是一个嫌疑人。具体创立代码如下:

def poi_email_ratio(from_poi_to_this_person, to_messages):    if from_poi_to_this_person or to_messages == 'NaN':        to_poi_ratio = 0    else:        to_poi_ratio = float(from_poi_to_this_person)/to_messages    return to_poi_ratio# create new key and valuefor key in my_dataset:    my_dataset[key]['to_poi_ratio'] = poi_email_ratio(my_dataset[key]['from_poi_to_this_person'],  my_dataset[key]['to_messages'])

测试新特征是否会对分类算法的结果产生影响的代码如下:

### 添加新特征之后的数据集data = featureFormat(my_dataset, features_list, sort_keys = True)labels, features = targetFeatureSplit(data)### 未添加新特征的数据集data = featureFormat(data_dict, features_list, sort_keys = True)labels, features = targetFeatureSplit(data)

我们分别运行这两段代码,比较结果就可以知道新特征是否会对分类算法产生影响了。运行结果分别如下:

### new featureThe naive_bayes's recall is: 0.871794871795 The naive_bayes's precision is : 0.871794871795The Decession_tree's recall is: 0.897435897436 The Decession_tree's precision is : 0.897435897436### orignal featureThe naive_bayes's recall is: 0.871794871795 The naive_bayes's precision is : 0.871794871795The Decession_tree's recall is: 0.846153846154 The Decession_tree's precision is : 0.846153846154

通过对比发现添加新特征对于朴素贝叶斯的结果完全没有任何影响,而对于决策树算法有一定影响。添加新特征之后决策树算法的准确率提高了。

选择最佳特征

选择和调整算法

选择算法

我们这里邮件信息的学习过程实际上是一个监督学习的过程,我们这里分别使用朴素贝叶斯和决策树来对模型进行训练和评估。因为这个数据集很不平衡(imbalance), 也就说明accuracy并不是很好的评估指标,因此我们选择precision和recall来作为模型的评估指标。

朴素贝叶斯的机器学习模型建立如下:

from sklearn.naive_bayes import GaussianNBclf = GaussianNB()clf.fit(features_train, labels_train)y_pred = clf.predict(features_test)recall = recall_score(labels_test, y_pred, average='micro')precision = precision_score(labels_test, y_pred, average='micro')print "The naive_bayes's recall is: %s " % recallprint "The naive_bayes's precision is : %s" % precision

该模型预测的准确率为0.85

决策树的机器学习模型建立如下:

from sklearn import treefrom sklearn.model_selection import GridSearchCVtrees = tree.DecisionTreeClassifier()parameters = {'min_samples_split' : range(5,80,5), 'splitter' : ('best', 'random')}clf = GridSearchCV(trees, parameters)clf.fit(features_train, labels_train)y_pred = clf.predict(features_test)recall = recall_score(labels_test, y_pred, average='micro')precision = precision_score(labels_test, y_pred, average='micro')print "The Decession_tree's recall is: %s " % recallprint "The Decession_tree's precision is : %s" % precision

决策树训练模型涉及到较多的参数,要想得到更好的训练效果,对于参数的调整是绝对必要的。

调整算法

使用决策树有一个缺点就是容易过拟合,因此我们发应当尽可能的合理调整参数以达到最好的训练效果。

决策树有一个参数为min_samples_split,用于设置最小分割数。另外我们还可以调整splitter参数,该参数可以设置分割方法,有两种:一种是’best’策略,用于选择最好的分割,另一种是’random’策略,用于选择最好的随机分割。通常情况下当样本量比较小的时候我们采取’best’策略进行分割,而当样本量比较大的时候,我们采取‘random’的效果会更好。

使用 GridSearchCV 进行参数调整

GridSearchCV 用于系统地遍历多种参数组合,通过交叉验证确定最佳效果参数。它的好处是,只需增加几行代码,就能遍历多种组合。当然与此对应的是机器学习过程所消耗的时间会相对较多。下面我们用GridSearchCV对决策树参数进行调整:

from sklearn import treefrom sklearn.model_selection import GridSearchCVtrees = tree.DecisionTreeClassifier()parameters = {'min_samples_split' : range(5,80,5), 'splitter' : ('best', 'random')}clf = GridSearchCV(trees, parameters)clf.fit(features_train, labels_train)print clf.score(features_test, labels_test)

经测试发现准确率为0.91,高于朴素贝叶斯的0.87。这里我们使用决策树效果更好。

验证和评估

我们分别使用精确度和召回率这两个指标来评估模型的好坏。精确度概括的说可以是:猜对真的/(猜对真的+误以为真的)。召回率概括的说可以是猜对真的/(猜对真的+误以为假的)。我们分别对两个算法模型进行评估

验证及其重要性

验证是用于评估模型好坏的一个重要方法,我们通常将数据集分为训练集和测试集就是为了验证的方便。前者用以建立模型(model),后者则用来评估该模型对未知样本进行预测时的泛化能力。我们需要在测试集上进行验证,来确定训练集是否“过拟合”或者“欠拟合”。不同的数据集和训练集的划分方法,也会对验证的效果产生一定影响。

训练集和数据集的拆分

我使用如下方法拆分训练集和数据集的

from sklearn.model_selection import train_test_splitfeatures_train, features_test, labels_train, labels_test = train_test_split(            features, labels, test_size=0.3, random_state=42)

数据集的70%作为训练集,30%作为测试集

参考资料

我在此确认,所提交的项目为我的工作成果,其中引用的信息出自网站、书籍、论坛、博客文章和 GitHub 代码库等。下面列出我在完成项目过程中所参考的资料:

Recall和Precision的理解 http://blog.csdn.net/Relocy/article/details/51453950

Precision-Recall metric: http://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html

recall score :http://scikit-learn.org/stable/modules/generated/sklearn.metrics.recall_score.html

交叉验证:http://blog.csdn.net/cherdw/article/details/54986863

原创粉丝点击