美文网首页
从安然公司邮件中发现欺诈证据

从安然公司邮件中发现欺诈证据

作者: 杨小彤 | 来源:发表于2018-03-26 15:29 被阅读0次

    tags: 机器学习 算法

    项目概述

    安然曾是 2000 年美国最大的公司之一。2002 年,由于其存在大量的企业欺诈行为,这个昔日的大集团土崩瓦解。 在随后联邦进行的调查过程中,大量有代表性的保密信息进入了公众的视线,包括成千上万涉及高管的邮件和详细的财务数据。 运用机器学习技能构建一个算法,通过公开的安然财务和邮件数据集,找出有欺诈嫌疑的安然雇员。

    数据

    数据来自于Github,数据中的特征分为三大类,即财务特征、邮件特征和 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,整数)

    接下来要做的是设计特征,选择并调整算法,用以测试和评估识别符

    数据处理

    1.加载.pkl数据集,转换为DataFrame数据格式

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

    数据集特点:
    a)包含21个字段,146条记录,数据点总数是3066个;POI的数量18,非POI数量128,针对此项目目标,此数据集是一个类失衡数据,所以accurary不是一个很好的指标,可以用recall,f1,precision多个测评分数作为选择分类器的指标;
    b)特征数量是20个,'poi'是标签。

    2.处理缺失值

    df.replace('NaN', np.nan, inplace = True) #np.nan是浮点型数据,在pandas中用isnull()判断,numpy中用isnan()判断
    features_nan_count = df.isnull().sum(axis = 0).sort_values(ascending=False) #每一列的缺失值
    person_nan_count = df.isnull().sum(axis = 1).sort_values(ascending=False) #每一行的缺失值
    
    print'缺失值最多的10个特征:'
    print features_nan_count[:10]
    print'缺失值最多的10个人:'
    print person_nan_count[:10]
    

    经统计发现:
    a)存在缺失值最多的人是"LOCKHART EUGENE E" ,缺失20个特征,一共有21个特征;
    b)loan_advances、director_fees、estricted_stock_deferred、deferral_payments、deferred_income、long_term_incentive、bonus、shared_receipt_with_poi、from_poi_to_this_person、from_messages这10个特征含有较多的缺失值,缺失值多于60个。

    处理缺失值:
    a)删除"LOCKHART EUGENE E"这一记录;
    b)缺失值较多的特征也可能是寻找嫌疑人的有利特征,不做删除处理,将缺失值填充为0。

    df = df.fillna(0) 
    df = df.drop(['LOCKHART EUGENE E'], axis=0) 
    

    3.处理异常值

    plt.scatter( df["salary"], df["bonus"])
    plt.xlabel("salary")
    plt.ylabel("bonus")
    plt.show()
    
    工资-奖金散点图

    通过工资奖金散点图,找到出异常点的键'TOTAL',还有一个异常点'THE TRAVEL AGENCY IN THE PARK',不属于人名,将两个异常点记录删除。

    df=df.drop(['TOTAL','THE TRAVEL AGENCY IN THE PARK'], axis=0)
    

    4.构建新特征

    df['from_poi_rate'] = df['from_poi_to_this_person'] / df['to_messages']
    df['from_poi_rate'] = df['from_poi_rate'].fillna(0)
    
    df['sent_to_poi_rate'] = df['from_this_person_to_poi'] / df['from_messages']
    df['sent_to_poi_rate'] = df['sent_to_poi_rate'].fillna(0)
    
    df['shared_with_poi_rate'] = df['shared_receipt_with_poi'] / df['to_messages']
    df['shared_with_poi_rate'] = df['shared_with_poi_rate'].fillna(0)
    

    重新构建了3个认为对找出嫌疑人有利的邮件新特征:a.from_poi_rate',总收件数中来自嫌疑人的邮件数占比;b.'sent_to_poi_rate',总发件数中发送给嫌疑人的邮件数占比;c.'shared_with_poi_rate',总收件数中来自嫌疑人分享的邮件数占比。

    5.删除无用特征

    'email_address'邮箱地址对找出嫌疑人用处不大,从特征中删除。

    6.特征选择

    df_features = df.drop('poi', axis=1)
    df_labels = df['poi']
    k_best = SelectKBest(k='all')
    k_best.fit(df_features, df_labels)
    best_score=pd.DataFrame(k_best.scores_,index=df_features.columns.tolist(),columns=['score'])
    

    使用SelectKBest,返回所有特征跟'poi'标签相关性得分。
    最佳特征:
    score
    exercised_stock_options 24.815080
    total_stock_value 24.182899
    bonus 20.792252
    salary 18.289684
    sent_to_poi_rate 16.409713
    deferred_income 11.458477
    long_term_incentive 9.922186
    restricted_stock 9.212811
    shared_with_poi_rate 9.101269
    total_payments 8.772778
    shared_receipt_with_poi 8.589421
    loan_advances 7.184056
    expenses 6.094173
    from_poi_to_this_person 5.243450
    other 4.187478
    from_poi_rate 3.128092
    from_this_person_to_poi 2.382612
    director_fees 2.126328
    to_messages 1.646341
    deferral_payments 0.224611
    from_messages 0.169701
    restricted_stock_deferred 0.065500

    选取得分最高的前10个特征,但这10个特征中有的特征含有大量的缺失值,去掉缺失值在前10的,最后筛选出6个特征来识别'poi',分别是exercised_stock_options、total_stock_value、sent_to_poi_rate、restricted_stock、shared_with_poi_rate、total_payments。

    df_features = df[['exercised_stock_options', 'total_stock_value','sent_to_poi_rate','restricted_stock', 'shared_with_poi_rate','total_payments']]
    

    选择分类器

    1.分类器
    a)朴素贝叶斯(Naive Bayes Classifier):
    是一种简单有效的常用分类算法,适用于假定所有特征发生概率是独立的,显然我选择的用来标记'poi'的6个特征不太可能相互独立,但也不妨试一下。
    b)支持向量机(Support Vector Machine,SVM):
    对于SVM,存在一个分类面,两个点集到此平面的最小距离最大,两个点集中的边缘点到此平面的距离最大(基于距离分类,需要部署特征缩放),适用于小样本。
    c)决策树 (Decision tree):
    特点是它总是在沿着特征做切分,能够生成清晰的基于特征选择不同预测结果的树状结构。
    d)随机森林 (Random forest):
    是一种集成算法,它首先随机选取不同的特征和训练样本,生成大量的决策树,然后综合这些决策树的结果来进行最终的分类。适用于数据维度相对低(几十维),同时对准确性有较高要求时。
    e)AdaBoost :
    其核心思想是针对同一个训练集训练不同的分类器,即弱分类器,然后把这些弱分类器集合起来,构造一个更强的最终分类器。适用于用于二分类或多分类的应用场景,无脑化,简单,不会过拟合,不用调分类器。

    algorithms = [
        GaussianNB(),
        make_pipeline(MinMaxScaler(), SVC(class_weight='balanced', random_state=42)),
        DecisionTreeClassifier(class_weight='balanced', random_state=42),
        RandomForestClassifier(class_weight='balanced', random_state=42),
        AdaBoostClassifier(base_estimator=DecisionTreeClassifier(class_weight='balanced'), random_state=42)
    ]
    

    2.设置参数

    初始设置参数时,参数差值尽量大一些,自动调参后,结合最优参数值,重新设置参数值。

    params = [
        { },  # GaussianNB
    
        {  # SVC pipeline
            'svc__kernel' : ('linear', 'rbf', 'poly', 'sigmoid'),
            'svc__C' : [0.1, 1.0, 10, 100, 1000, 10000],
            'svc__gamma' : [0.001, 0.01, 0.1, 1.0, 10, 100, 1000, 10000]
        },
        {  # DecisionTreeClassifier
            'criterion' : ('gini', 'entropy'),
            'splitter' : ('best', 'random'),
            'min_samples_split' : [7, 8, 9, 10, 11, 12, 13]
        },
        {  # RandomForestClassifier
            'n_estimators' : [2,3,4,5,6,7,8],
            'criterion' : ('gini', 'entropy'),
            'min_samples_split' : [25,30,35,40,45,50,55]
        },
        {  # AdaBoostClassifier
            'n_estimators' : [5, 10, 50],
            'learning_rate' : [0.001, 0.01, 0.1, 1.0]
        }
    ]
    

    3.使用GridSearchCV自动调参

    划分训练集和测试集,训练集占70%,测试集占30%。

    features_train, features_test, labels_train, labels_test = cross_validation.train_test_split(df_features,df_labels,test_size=0.3, random_state=42)
    

    用F1作为分类型评价标准,自动调参,得到最佳参数后,返回params重新设置参数,再次自动调参,直到得到满意的f1_score。

    dataset = df.to_dict(orient='index') #dataframe数据结构转换为list
    best_estimator = None  
    best_score = 0  
    print('\n\n==============\n选择最好的模型算法(F1得分)::\n')
    warnings.filterwarnings('ignore') # 忽视f1警告错误
    
    for ii, algorithm in enumerate(algorithms):
        model = GridSearchCV(algorithm, params[ii],scoring = 'f1',cv=5) 
        model.fit(features_train,labels_train)
    
        best_estimator_ii = model.best_estimator_
        best_score_ii = model.best_score_
    
        print('------------\nF1 Score:',best_score_ii,'\n')
    
        test_classifier(best_estimator_ii, dataset, feature_list)
    
        if best_score_ii > best_score:
            best_estimator = best_estimator_ii
            best_score = best_score_ii
    
    clf =  best_estimator
    

    朴素叶贝斯:
    Accuracy: 0.85673 Precision: 0.43270 Recall: 0.23950 F1: 0.30834

    支持向量机:
    Accuracy: 0.80047 Precision: 0.31508 Recall: 0.42300 F1: 0.36115

    决策树:
    Accuracy: 0.78600 Precision: 0.23254 Recall: 0.26300 F1: 0.24683

    随机森林:
    Accuracy: 0.77247 Precision: 0.31155 Recall: 0.58400 F1: 0.40633

    adaboost:
    Accuracy: 0.79200 Precision: 0.18820 Recall: 0.16900 F1: 0.17808

    得分最高的分类器:
    随机森林

    交叉检验

    使用cross_val_score的方法对数据集进行多次分割(CV=5),多次验证,最后取平均得分,大大降低随机性对数据分布的影响。

    f1 = cross_val_score(clf, features_train,labels_train, cv=5,scoring='f1')
    acc = cross_val_score(clf, features_train,labels_train, cv=5, scoring='accuracy')
    pre = cross_val_score(clf, features_train,labels_train, cv=5, scoring='precision')
    rec = cross_val_score(clf, features_train,labels_train, cv=5, scoring='recall')
    test_set = (f1.mean(),acc.mean(),pre.mean(),rec.mean())
    

    检验结果:
    F1=0.537979797979798
    accuracy=0.79864661654135338
    precision=0.46547619047619049
    recall=0.83333333333333326
    召回率大于0.3,交叉验证成功。

    ※评估度量理解
    True Positive(TP):做出Positive的判定,而且判定是正确的。
    此项目中的含义:POI判定为POI的数量
    False Positive(TP):做出Positive的判定,而且判定是错误的。
    此项目中的含义:非POI判定为POI的数量
    True Negative(TN):做出Negative的判定,而且判定是正确的。
    此项目中的含义:非POI判定为非POI的数量
    False Negative(FN):做出Negative的判定,而且判定是错误的。
    此项目中的含义:POI判定为非POI的数量

    precision= TP / (TP + FP)
    精确率:找到的POI数量占判定为POI数量的比例
    recall= TP / (TP + FN)
    召回率:找到的POI数占总POI数量的比例
    accurary= (TP + TN) / (TP + FP + TN + FN)
    准确率:所有正确分类数量在总数的比例
    F1 Score = P*R/2(P+R)
    F1:精确率和召回率的平均效果

    输出

    dataset = df.to_dict(orient='index')
    feature_list = ['poi'] + df_features.columns.tolist()
    dump_classifier_and_data(clf, dataset, feature_list )
    

    相关文章

      网友评论

          本文标题:从安然公司邮件中发现欺诈证据

          本文链接:https://www.haomeiwen.com/subject/wlercftx.html