zoukankan      html  css  js  c++  java
  • 逻辑回归之信用卡

    背景:有人利用信用卡欺诈,数据给出了28W多个样本,每一个样本有20多个因素数据和最终是否欺诈的结论。

     1 import numpy as np
     2 import pandas as pd
     3 import matplotlib.pyplot as plt
     4 #导入相关库文件
     5 
     6 data = pd.read_csv('creditcard.csv')  #读取文件
     7 print(data.shape)  #打印下文件内容是几行几列
     8 count_class = pd.value_counts(data['Class'],sort = True).sort_index()
     9 #pd.value_counts()对数据集中的['Class']列进行数学统计,统计其中每一类数据出现的个数。
    10 #sort_index()是根据索引进行排序,在这个数据集中好像没有什么意义
    11 print (count_class)
     1 from sklearn.preprocessing import StandardScaler
     2 # 导入sklearn数据预处理模块,sklearn是机器学期很重的一个库,有时间还是需要着重学习一下
     3 data['normAmount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1,1))   # -1表示系统自动计算得到的行,1表示1列
     4 #StandardScaler().fit_transform是sklearn里可将数据标准化的函数,和scale()的功能类似,都可以将数据标准化,具体可以百度
     5 #https://blog.csdn.net/weixin_38278334/article/details/82971752    .fit_transform的应用
     6 #.values是将前边的字典转成数组,也就是转成list。.reshape(-1,1)是将前边的.list转成n行,1列,-1是不指定行数,自动计算得出n
     7 #再将reshape之后的list赋值给data['normAmount'],等于为数据集多增加了一列。
     8 data = data.drop(['Time','Amount'],axis = 1)
     9 #观察数据发现,“Time”这一列没有什么作用,所以删除,“Amount”每个样本之间差距比较大,应将其标准化,
    10 #结合上一行,实际就是把['Amount']一列进行标准化,并改名为normAmount。

     通过观察数据发现,Class=0的样本有284315个,Class=1的样本有492个,典型的样本不均衡现象。处理方法有两种:过采样和下采样。过采样:增加Class=1的样本数量,增加至和Class=0一样多;下采样:减少Class=0的样本数量,减少至和Class=1的数量相同。

     

     1 X = data.loc[:,data.columns != 'Class']  #选取非“Class”列,目的是将Class视为标签
     2 #注意.loc和.iloc的区别。iloc主要使用数字来索引数据,不能使用字符型的标签来索引数据。
     3 #loc只能使用字符型标签来索引数据,不能使用数字来索引数据。特殊情况:当dataframe的行标签或列标签为数字时,loc就可以来索引
     4 #如果对数据很熟悉
     5 y = data.loc[:,data.columns == 'Class']    # y 为标签,即类别
     6 number_records_fraud = len(data[data.Class==1])  #统计异常值的个数
     7 #print (number_records_fraud)  # 492 个
     8 fraud_indices = np.array(data[data.Class == 1].index)
     9 #fraud_indices值是取原数据所有Class=1的样本的索引,并转为array的形式。
    10 #fraud_indices是所有Class=1的行索引
    11 normal_indices = data[data.Class == 0].index   # 记录正常值的索引
    12 normal_indices = np.array(normal_indices)
    13 #normal_indices是取原数据所有Class=0的样本的索引,并转为array的格式。
    14 random_normal_indices =np.random.choice(normal_indices,number_records_fraud,replace = False)
    15 #目的是从正常值的索引中,选择和异常值相等个数的样本。
    16 #np.random.choice()功能是在第一个参数normal_indices内随机去第二个参数number_records_fraud个数据,number_records_fraud是Class=1的样本数量
    17 #random_normal_indices = np.array(random_normal_indices)
    18 under_sample_indices = np.concatenate([fraud_indices,random_normal_indices])
    19 #np.concatenate是将两个array类型拼接成一个array,类似于append,但是适合大批量数据拼接.注意参数外有一个括号,可以help查询使用方法。
    20 under_sample_data = data.iloc[under_sample_indices,:]
    21 #under_sample_indices是组合好的新的数据的索引,是一个array,iloc是根据索引找到该数据
    22 #under_sample_data最终的值是组合好的数据的新的数据集,是一个数据集
    23 X_undersample = under_sample_data.iloc[:, under_sample_data.columns != 'Class']  # 下采样后的训练集
    24 #找到新数据集under_sample_data中Class=1的所有列
    25 y_undersample = under_sample_data.iloc[:, under_sample_data.columns == 'Class']  # 下采样后的标签
    26 #找到新数据集under_sample_data中Class=0的所有列
    27 print(len(under_sample_data[under_sample_data.Class == 1]) / len(under_sample_data))  # 正负样本的比例都是 0.5
    28 #under_sample_data.Class == 1是个布尔值的array,under_sample_data[]取到under_sample_data中Class=1的所有数据

    其实不难发现,上边的代码很多,其实就作了一件事,下采样。随机取Class=0中的若干个样本(数量等于Class=1的数量),重新组合数据。

     1 from sklearn.model_selection import train_test_split
     2 #新版本的要用sklearn.model_selection,网上很多用的是老版本的库,会报错
     3 # from sklearn.cross_validation import train_test_split
     4 # ModuleNotFoundError: No module named 'sklearn.cross_validation'
     5 X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_state = 0)
     6 #将原始数据按照参数test_size比例切割为训练集和测试集。
     7 X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(
     8     X_undersample, y_undersample, test_size=0.3, random_state=0)
     9 #将下采样后的数据按照参数test_size比例切割为训练集和测试集。
    10 #这是数据分析预测很重要的一部。将数据分为两部分,第一部分是预测模型用的,第二部分是模型确定后,用来拟合数据,评估模型好坏的。

    下一部分代码是比较核心的一部分,用sklearn库里的函数来建立模型。

     1 def printing_Kfold_scores(x_train_data,y_train_data):
     2     fold = KFold(5,shuffle=False)
     3     #KFold就是建立一个交叉验证的模型。没有将数据集代入参数。5的意思是切成5部分,其中4部分和1部分各位1组。
     4     c_param_range = [0.01,0.1,1,10,100]
     5     #惩罚项,在逻辑回归中可以设置惩罚项,分为l1和l2两种。简单来说,l1可以防止过拟合现象(过拟合就是训练接表现很好,但是测试集表现很差)
     6     #l2可以突出某一个参数的权重。惩罚项是需要人为设定的,这里设置多个惩罚项参数,可以对比哪一更好。
     7     results_table = pd.DataFrame(index = range(len(c_param_range),2),columns = ['C_parameter','Mean recall score'])
     8     #建立一个新的DataFrame,行索引是[0,1,2,3,4],列是['C_parameter','Mean recall score'],
     9     #'C_parameter'储存惩罚项参数,'Mean recall score'储存在这一惩罚项参数下的平均recall值
    10     #recall值反应预测是否准确。例如10个病人,预测出来了8个,recall=80%
    11     #其实index = range(len(c_param_range),2)并没有什么用,因为具体有多少行是由c_param_range内容确定的。
    12     results_table['C_parameter'] = c_param_range
    13     j = 0
    14     for c_param in c_param_range:
    15         #此循环的内容是惩罚项参数,也就是计算一种交叉验证情况下的,5种惩罚项参数的recall值
    16         print('------------------------------------------')
    17         print('C parameter:', c_param)
    18         print('-------------------------------------------')
    19         print('')
    20         recall_accs = []#建立新的array,后后续计算每一种惩罚项参数下的recall做准备
    21         for iteration, indices in enumerate(fold.split(x_train_data),start=1):
    22             #iteration=[1,2,3,4,5],enumerate有一个参数(start=1),从1开始数
    23             lr = LogisticRegression(C=c_param, penalty='l1', solver='liblinear', max_iter=10000)
    24             # 使用特定的C参数调用逻辑回归模型
    25             # 参数 solver=’liblinear’ 消除警告
    26             # 出现警告:模型未能收敛 ,请增加收敛次数
    27             #  ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
    28             #  "the number of iterations.", ConvergenceWarning)
    29             #  增加参数 max_iter 默认1000
    30             lr.fit(x_train_data.iloc[indices[0], :], y_train_data.iloc[indices[0], :].values.ravel())
    31             #例如,交叉验证,分为5组数据[0,1,2,3,4],可以分为[0,1,2,3]和[4],[4,1,2,3]和[0],[0,4,2,3]和[1],[0,1,4,3]和[2],[0,1,2,4]和[3]
    32             #indices[0]是[[0,1,2,3],[4,1,2,3],[0,4,2,3],[0,1,4,3]],indices[1]是[[4],[0],[1],[2]]
    33             #注意,0、1、2、3、4只是索引,KFold分的是索引!
    34             # Use the training data to fit the model. In this case, we use the portion
    35             # of the fold to train the model with indices[0], We then predict on the
    36             # portion assigned as the 'test cross validation' with indices[1]
    37 
    38             y_pred_undersample = lr.predict(x_train_data.iloc[indices[1], :].values)
    39             #利用模型预测数据。输入是x_train_data.iloc[indices[1],也就是前边分好的,在交叉验证里每组数据的第二部分。
    40             #predict返回的是一个大小为n的一维数组,第i个预测样本的标签;
    41             # Predict values using the test indices in the training data
    42 
    43             # Calculate the recall score and append it to a list for recall scores
    44             # representing the current c_parameter
    45             recall_acc = recall_score(y_train_data.iloc[indices[1], :].values, y_pred_undersample)
    46             #计算召回率,y_train_data.iloc[indices[1], :]是真实的数据,y_pred_undersample是预测的数据
    47             recall_accs.append(recall_acc)
    48             #把刚刚计算完的一种交叉验证情况下的召回率,储存,一会要计算5种交叉验证的召回率的均值
    49             print('Iteration ', iteration, ': recall score = ', recall_acc)
    50             # the mean value of those recall scores is the metric we want to save and get
    51             # hold of.
    52         results_table.loc[j, 'Mean recall score'] = np.mean(recall_accs)
    53         j += 1
    54         print('')
    55         print('Mean recall score ', np.mean(recall_accs))
    56         print('')
    57     best_c = results_table.loc[results_table['Mean recall score'].astype('float64').idxmax()]['C_parameter']
    58     #best_c内容是所有的惩罚项参数中,recall值最大的参数。
    59     return best_c

    将前边下采样的数据集代入printing_Kfold_scores函数,计算打印先关数据。需要注意,results_table['Mean recall score']是一个object,需要转为浮点型,才能比大小。

    best_c = printing_Kfold_scores(X_train_undersample,y_train_undersample)

    打印结果

    ------------------------------------------
    C parameter: 0.01
    -------------------------------------------
    
    Iteration  1 : recall score =  0.9726027397260274
    Iteration  2 : recall score =  0.9315068493150684
    Iteration  3 : recall score =  1.0
    Iteration  4 : recall score =  0.9594594594594594
    Iteration  5 : recall score =  0.9848484848484849
    
    Mean recall score  0.9696835066698082
    
    ------------------------------------------
    C parameter: 0.1
    -------------------------------------------
    
    Iteration  1 : recall score =  0.8493150684931506
    Iteration  2 : recall score =  0.863013698630137
    Iteration  3 : recall score =  0.9491525423728814
    Iteration  4 : recall score =  0.9459459459459459
    Iteration  5 : recall score =  0.8939393939393939
    
    Mean recall score  0.9002733298763017
    
    ------------------------------------------
    C parameter: 1
    -------------------------------------------
    
    Iteration  1 : recall score =  0.863013698630137
    Iteration  2 : recall score =  0.9041095890410958
    Iteration  3 : recall score =  0.9830508474576272
    Iteration  4 : recall score =  0.9459459459459459
    Iteration  5 : recall score =  0.9242424242424242
    
    Mean recall score  0.924072501063446
    
    ------------------------------------------
    C parameter: 10
    -------------------------------------------
    
    Iteration  1 : recall score =  0.9178082191780822
    Iteration  2 : recall score =  0.8904109589041096
    Iteration  3 : recall score =  0.9830508474576272
    Iteration  4 : recall score =  0.9324324324324325
    Iteration  5 : recall score =  0.9242424242424242
    
    Mean recall score  0.929588976442935
    
    ------------------------------------------
    C parameter: 100
    -------------------------------------------
    
    Iteration  1 : recall score =  0.9178082191780822
    Iteration  2 : recall score =  0.8904109589041096
    Iteration  3 : recall score =  0.9830508474576272
    Iteration  4 : recall score =  0.9594594594594594
    Iteration  5 : recall score =  0.9242424242424242
    
    Mean recall score  0.9349943818483405

    很容易发现,惩罚项参数=0.01的时候,recall值是最低的,但是这并不代表最好的。

    下边从混淆矩阵的角度,说明问题。 

     1 import seaborn as sns  #导入seaboard库
     2 def plot_confusion_matrix(cnf_matrix):  #定义画图函数
     3     sns.heatmap(cnf_matrix, fmt="d", annot=True, cmap='Blues')
     4     ##cnf_matrix是计算好的混淆矩阵,没有fmt="d"热力图中会出现科学计数法e,cmap设置颜色
     5     plt.ylabel("True label")  #纵坐标label
     6     plt.xlabel("Predicted label")#横坐标label
     7 
     8 lr = LogisticRegression(C=best_c, penalty='l1', solver='liblinear')
     9 #用刚才惩罚项中recall值最大的参数
    10 lr.fit(X_train_undersample, y_train_undersample.values.ravel())
    11 #用下采样的训练数据,建立模型。其实是把刚才的工作又做了一遍。刚才是通过交叉验证,计算出哪一个惩罚项参数的recall值最大,现在确定你了,又重新算
    12 y_pred_undersample = lr.predict(X_test_undersample.values)
    13 #用建立好的模型,预测y
    14 y_pred_undersample = y_pred_undersample.tolist()
    15 #转成list,因为cnf_matrix函数参数要求是list
    16 y_test_undersample = y_test_undersample['Class'].tolist()
    17 #同上
    18 cnf_matrix = confusion_matrix(y_test_undersample, y_pred_undersample)
    19 #cnf_matrix返回值是一个二维数组,也就是说是个ndarray类型的
    20 print("Recall metric in the testing dataset:",
    21       cnf_matrix[1, 1] / (cnf_matrix[1, 0] + cnf_matrix[1, 1]))
    22 #打印recall值
    23 plot_confusion_matrix(cnf_matrix)
    24 #画出混淆矩阵,原来博主写的看不太懂,我觉得用heatmap更加简单些
    25 plt.show()
    26 #打印

    输出:

     

     召回率也还可以,0.93多,但是由于采取的是下采样,样本数量是总样本的一部分,具有片面性。另外,发现有8个人,真实为1,预测为0,等于说没预测到。

    best_c = printing_Kfold_scores(X_train, y_train)

    输出如下:

    ------------------------------------------
    C parameter: 0.1
    -------------------------------------------
    
    Iteration  1 : recall score =  0.5671641791044776
    Iteration  2 : recall score =  0.6164383561643836
    Iteration  3 : recall score =  0.6833333333333333
    Iteration  4 : recall score =  0.5846153846153846
    Iteration  5 : recall score =  0.525
    
    Mean recall score  0.5953102506435158
    
    ------------------------------------------
    C parameter: 1
    -------------------------------------------
    
    Iteration  1 : recall score =  0.5522388059701493
    Iteration  2 : recall score =  0.6164383561643836
    Iteration  3 : recall score =  0.7166666666666667
    Iteration  4 : recall score =  0.6153846153846154
    Iteration  5 : recall score =  0.5625
    
    Mean recall score  0.612645688837163
    
    ------------------------------------------
    C parameter: 10
    -------------------------------------------
    
    Iteration  1 : recall score =  0.5522388059701493
    Iteration  2 : recall score =  0.6164383561643836
    Iteration  3 : recall score =  0.7333333333333333
    Iteration  4 : recall score =  0.6153846153846154
    Iteration  5 : recall score =  0.575
    
    Mean recall score  0.6184790221704963
    
    ------------------------------------------
    C parameter: 100
    -------------------------------------------
    
    Iteration  1 : recall score =  0.5522388059701493
    Iteration  2 : recall score =  0.6164383561643836
    Iteration  3 : recall score =  0.7333333333333333
    Iteration  4 : recall score =  0.6153846153846154
    Iteration  5 : recall score =  0.575
    
    Mean recall score  0.6184790221704963
    
    
    Process finished with exit code 0

    发现虽然下采样模型的recall比较高,但是用下采样的模型预测原始数据,recall才0.61多,所以存在局限性。

    #再绘制混淆矩阵看看
    lr = LogisticRegression(C=best_c, penalty='l1', solver='liblinear')
    lr.fit(X_train, y_train.values.ravel())
    y_pred = lr.predict(X_test.values)
    y_pred = y_pred.tolist()
    y_test = y_test['Class'].tolist()
    cnf_matrix = confusion_matrix(y_test, y_pred)
    print("Recall metric in the testing dataset:",
          cnf_matrix[1, 1] / (cnf_matrix[1, 0] + cnf_matrix[1, 1]))
    #打印recall值
    print('cnf_matrix[1, 1]=',cnf_matrix[1, 1],'cnf_matrix[1, 0]=',cnf_matrix[1, 0],'cnf_matrix[1, 1]=',cnf_matrix[1, 1])
    plot_confusion_matrix(cnf_matrix)
    plt.show()

    输出混淆矩阵:

     发现问题,如果从准确率的角度看,很高,但是从recall值看,很低,recall才0.61多。真实值为1的,预测值为0的,有56个人,这些人是检测不出来的。印证了刚才所说的下采样的片面性。

     从另外一个角度讨论,如果修改预测0或1的阈值,结果怎么样?lr.predict函数默认阈值是0.5,如果修改阈值,需要使用lr.predict_proba函数。

    lr = LogisticRegression(C=0.01, penalty='l1', solver='liblinear')
    lr.fit(X_train_undersample, y_train_undersample.values.ravel())
    y_pred_undersample_proba = lr.predict_proba(X_test_undersample.values)
    #predict返回的是0或1,predict_proba返回的是0或1的概率值,所以y_pred_undersample_proba.shape=(296, 2)
    #第0列是预测值为0的概率,第1列是预测值为1的概率。在这个背景下,预测结果只有0和1,在其他背景下,可能有0、1、2等等。
    #所以每一行相加的概率值为1
    thresholds = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]  # 阈值列表
    #目的是画出,不同阈值下的recall值和图像。比如阈值=0.1,大于0.1的意思是有10%的概率认为是确诊,就归为确定病例。
    plt.figure(figsize=(10, 10)) #设定图的长宽
    j = 1
    for i in thresholds:  #遍历几种情况下的阈值
        y_test_predictions_high_recall = y_pred_undersample_proba[:, 1] > i
        # 如果预测的概率值大于阈值,就返回True,所以y_test_predictions_high_recall的值是一个bool型的list
        ax = plt.subplot(3, 3, j)  #设定子图画板
        ax.set_title('Threshold >= %s' % i)
        j += 1
        cnf_matrix = confusion_matrix(y_test_undersample,
                                      y_test_predictions_high_recall)
        print("Recall metric in the testing dataset:",
              cnf_matrix[1, 1] / (cnf_matrix[1, 0] + cnf_matrix[1, 1]))
        plot_confusion_matrix(cnf_matrix)
    plt.show()
    
    
    输出图像:

      很容易看出来,阈值是0.5或0.6的情况下,recall值会好点。

    再换种角度分析问题,既然下采样预测原始数据的效果不好,采用过采样呢?过采样就要增加样本数量,也就是Class=1的样本数量。

    from imblearn.over_sampling import SMOTE
    #导入SMOTE算法,第一次用需要安装,在anaconda Prompt下pip install imblearn就可以
    credit_cards = pd.read_csv('creditcard.csv')
    columns = credit_cards.columns
    features_columns = columns.delete(len(columns) - 1)
    features = credit_cards[features_columns]
    labels = credit_cards['Class']
    features['normAmount'] = StandardScaler().fit_transform(features['Amount'].values.reshape(-1,1))
    print(features)
    features = features.drop(['Time','Amount'],axis = 1)
    print(features)
    features_train, features_test, labels_train, labels_test = train_test_split(
        features, labels, test_size=0.2, random_state=0)
    oversampler = SMOTE(random_state=0)
    os_features,os_labels = oversampler.fit_sample(features_train,labels_train)
    os_features = pd.DataFrame(os_features)
    os_labels = pd.DataFrame(os_labels)

    注意SMOTE.fit_sample(features_train,labels_train)

    features_train:特征集
    labels_train:标签集

    该函数自动判断哪个是少数不平衡的类别,所以输入时将整体的训练集输入即可,不用将少数集预先提出。

    输出:

    ------------------------------------------
    C parameter: 0.01
    -------------------------------------------
    
    Iteration  1 : recall score =  0.8838709677419355
    Iteration  2 : recall score =  0.8947368421052632
    Iteration  3 : recall score =  0.9668916675888016
    Iteration  4 : recall score =  0.9561886547740737
    Iteration  5 : recall score =  0.9569360635737132
    
    Mean recall score  0.9317248391567574
    
    ------------------------------------------
    C parameter: 0.1
    -------------------------------------------
    
    Iteration  1 : recall score =  0.8838709677419355
    Iteration  2 : recall score =  0.8947368421052632
    Iteration  3 : recall score =  0.9688170853159235
    Iteration  4 : recall score =  0.9586836812081644
    Iteration  5 : recall score =  0.9592002725843858
    
    Mean recall score  0.9330617697911343
    
    ------------------------------------------
    C parameter: 1
    -------------------------------------------
    
    Iteration  1 : recall score =  0.8838709677419355
    Iteration  2 : recall score =  0.8947368421052632
    Iteration  3 : recall score =  0.9687064291247095
    Iteration  4 : recall score =  0.9590683769138612
    Iteration  5 : recall score =  0.9597498378782383
    
    Mean recall score  0.9332264907528016
    
    ------------------------------------------
    C parameter: 10
    -------------------------------------------
    
    Iteration  1 : recall score =  0.8838709677419355
    Iteration  2 : recall score =  0.8947368421052632
    Iteration  3 : recall score =  0.9689498727453801
    Iteration  4 : recall score =  0.9591343247491234
    Iteration  5 : recall score =  0.959486046537189
    
    Mean recall score  0.9332356107757782
    
    ------------------------------------------
    C parameter: 100
    -------------------------------------------
    
    Iteration  1 : recall score =  0.8838709677419355
    Iteration  2 : recall score =  0.8947368421052632
    Iteration  3 : recall score =  0.9689941352218656
    Iteration  4 : recall score =  0.9588815247139513
    Iteration  5 : recall score =  0.9595629856783284
    
    Mean recall score  0.9332092910922688

    从结果来看,每个惩罚系数都是差不多的。

    lr = LogisticRegression(C = best, penalty = 'l1', solver='liblinear')
    lr.fit(os_features,os_labels.values.ravel())
    y_pred = lr.predict(features_test.values)
    cnf_matrix = confusion_matrix(labels_test,y_pred)
    plot_confusion_matrix(cnf_matrix)
    plt.show()

    输出的混淆矩阵:

     从输出的混淆矩阵来看,效果不错,至少recall=95/(95+6)=0.94多,只有6个人患病,但是没有预测到。比下采样好多了。

    总结:除了学会处理样本不均衡的方法外,对于应用来说,不用过于关注算法具体如何实现的,只需要知道算法调用的函数名称、参数配置就可以。但是如果是写论文来说,想优化算法,就需要搞清楚算法的本质,用自己的代码去实现算法,再做优化。

    本博客参考的是唐宇迪的机器学期视频,以及https://www.cnblogs.com/xiaoyh/p/11209909.html朋友的博客。

  • 相关阅读:
    第一次冲刺结果演示 一组评审总结
    检查博客情况
    第十次站立会议
    第九次站立会议
    暑期实训day4
    暑期实训day3
    暑期实训day2.1——一发空格引发的血案
    暑期实训day2
    暑期实训day1
    黑板模式
  • 原文地址:https://www.cnblogs.com/lgwdx/p/13428325.html
Copyright © 2011-2022 走看看