欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 23:19:29
...

项目背景

安然曾是 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': 'aaa@qq.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 value
for 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 feature
The naive_bayes's recall is: 0.871794871795 
The naive_bayes's precision is : 0.871794871795
The Decession_tree's recall is: 0.897435897436 
The Decession_tree's precision is : 0.897435897436

### orignal feature
The naive_bayes's recall is: 0.871794871795 
The naive_bayes's precision is : 0.871794871795
The Decession_tree's recall is: 0.846153846154 
The Decession_tree's precision is : 0.846153846154

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

选择最佳特征

选择和调整算法

选择算法

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

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

from sklearn.naive_bayes import GaussianNB
clf = 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 " % recall
print "The naive_bayes's precision is : %s" % precision

该模型预测的准确率为0.85

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

from sklearn import tree
from sklearn.model_selection import GridSearchCV
trees = 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 " % recall
print "The Decession_tree's precision is : %s" % precision

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

调整算法

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

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

使用 GridSearchCV 进行参数调整

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

from sklearn import tree
from sklearn.model_selection import GridSearchCV
trees = 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_split
features_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