1. 程式人生 > >機器學習案例實戰:信用卡欺詐檢測

機器學習案例實戰:信用卡欺詐檢測

故事背景

原始資料為個人交易記錄,但是考慮資料本身的隱私性,已經對原始資料進行了類似PCA的處理,現在已經把特徵資料提取好了,接下來的目的就是如何建立模型使得檢測的效果達到最好,這裡我們雖然不需要對資料做特徵提取的操作,但是面對的挑戰還是蠻大的。

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.linear_model import LogisticRegression
from
sklearn.cross_validation import KFold, cross_val_score from sklearn.metrics import confusion_matrix,recall_score,classification_report

資料分析與建模可不是體力活,時間就是金錢我的朋友(魔獸玩家都懂的!)如果你用Python來把玩資料,那麼這些就是你的核武器啦。簡單介紹一下這幾位朋友!
Numpy-科學計算庫 主要用來做矩陣運算,什麼?你不知道哪裡會用到矩陣,那麼這樣想吧,咱們的資料就是行(樣本)和列(特徵)組成的,那麼資料本身不就是一個矩陣嘛。

Pandas-資料分析處理庫 很多小夥伴都在說用python處理資料很容易,那麼容易在哪呢?其實有了pandas很複雜的操作我們也可以一行程式碼去解決掉!
Matplotlib-視覺化庫 無論是分析還是建模,光靠好記性可不行,很有必要把結果和過程視覺化的展示出來。

Scikit-Learn-機器學習庫 非常實用的機器學習演算法庫,這裡麵包含了基本你覺得你能用上所有機器學習演算法啦。但還遠不止如此,還有很多預處理和評估的模組等你來挖掘的!

data = pd.read_csv("creditcard.csv")
data.head()

圖片描述

首先我們用pandas將資料讀進來並顯示最開始的5行,看見木有!用pandas讀取資料就是這麼簡單!這裡的資料為了考慮使用者隱私等,已經通過PCA處理過了,現在大家只需要把資料當成是處理好的特徵就好啦!

接下來我們核心的目的就是去檢測在資料樣本中哪些是具有欺詐行為的!

count_classes = pd.value_counts(data['Class'
], sort = True).sort_index() count_classes.plot(kind = 'bar') plt.title("Fraud class histogram") plt.xlabel("Class") plt.ylabel("Frequency")

圖片描述

千萬不要著急去用機器學習演算法建模做這個分類問題。首先我們來觀察一下資料的分佈情況,在資料樣本中有明確的label列指定了class為0代表正常情況,class為1代表發生了欺詐行為的樣本。從上圖中可以看出來。。。等等,你不是說有兩種情況嗎,為啥圖上只有class為0的樣本啊?再仔細看看,納尼。。。class為1的並不是木有,而是太少了,少到基本看不出來了,那麼此時我們面對一個新的挑戰,樣本極度不均衡,接下來我們首先要解決這個問題,這個很常見也是很頭疼的問題。

這裡我們提出兩種解決方案 也是資料分析中最常用的兩種方法,下采樣過取樣

先挑個軟柿子捏,下采樣比較簡單實現,咱們就先搞定第一種方案!下采樣的意思就是說,不是兩類資料不均衡嗎,那我讓你們同樣少(也就是1有多少個 0就消減成多少個),這樣不就均衡了嗎?

X = data.ix[:, data.columns != 'Class']
y = data.ix[:, data.columns == 'Class']

# Number of data points in the minority class
number_records_fraud = len(data[data.Class == 1])
fraud_indices = np.array(data[data.Class == 1].index)

# Picking the indices of the normal classes
normal_indices = data[data.Class == 0].index

# Out of the indices we picked, randomly select "x" number (number_records_fraud)
random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)
random_normal_indices = np.array(random_normal_indices)

# Appending the 2 indices
under_sample_indices = np.concatenate([fraud_indices,random_normal_indices])

# Under sample dataset
under_sample_data = data.iloc[under_sample_indices,:]

X_undersample = under_sample_data.ix[:, under_sample_data.columns != 'Class']
y_undersample = under_sample_data.ix[:, under_sample_data.columns == 'Class']

# Showing ratio
print("Percentage of normal transactions: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data))
print("Percentage of fraud transactions: ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data))
print("Total number of transactions in resampled data: ", len(under_sample_data))

Percentage of normal transactions:  0.5
Percentage of fraud transactions:  0.5
Total number of transactions in resampled data:  984

很簡單的實現方法,在屬於0的資料中,進行隨機的選擇,就選跟class為1的那類樣本一樣多就好了,那麼現在我們已經得到了兩組都是非常少的資料,接下來就可以建模啦!不過在建立任何一個機器學習模型之前不要忘了一個常規的操作,就是要把資料集切分成訓練集測試集,這樣會使得後續驗證的結果更為靠譜。

def printing_Kfold_scores(x_train_data,y_train_data):
    fold = KFold(len(y_train_data),5,shuffle=False) 

    # Different C parameters
    c_param_range = [0.01,0.1,1,10,100]

    results_table = pd.DataFrame(index = range(len(c_param_range),2), columns = ['C_parameter','Mean recall score'])
    results_table['C_parameter'] = c_param_range

    # the k-fold will give 2 lists: train_indices = indices[0], test_indices = indices[1]
    j = 0
    for c_param in c_param_range:
        print('-------------------------------------------')
        print('C parameter: ', c_param)
        print('-------------------------------------------')
        print('')

        recall_accs = []
        for iteration, indices in enumerate(fold,start=1):

            # Call the logistic regression model with a certain C parameter
            lr = LogisticRegression(C = c_param, penalty = 'l1')

            # Use the training data to fit the model. In this case, we use the portion of the fold to train the model
            # with indices[0]. We then predict on the portion assigned as the 'test cross validation' with indices[1]
            lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel())

            # Predict values using the test indices in the training data
            y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)

            # Calculate the recall score and append it to a list for recall scores representing the current c_parameter
            recall_acc = recall_score(y_train_data.iloc[indices[1],:].values,y_pred_undersample)
            recall_accs.append(recall_acc)
            print('Iteration ', iteration,': recall score = ', recall_acc)

        # The mean value of those recall scores is the metric we want to save and get hold of.
        results_table.ix[j,'Mean recall score'] = np.mean(recall_accs)
        j += 1
        print('')
        print('Mean recall score ', np.mean(recall_accs))
        print('')

    best_c = results_table.loc[results_table['Mean recall score'].idxmax()]['C_parameter']

    # Finally, we can check which C parameter is the best amongst the chosen.
    print('*********************************************************************************')
    print('Best model to choose from cross validation is with C parameter = ', best_c)
    print('*********************************************************************************')

    return best_c
best_c = printing_Kfold_scores(X_train_undersample,y_train_undersample)

上述程式碼中做了一件非常常規的事情,就是對於一個模型,咱們再選擇一個演算法的時候伴隨著很多的引數要調節,那麼如何找到最合適的引數可不是一件簡單的事,依靠經驗值並不是十分靠譜,通常情況下我們需要大量的實驗也就是不斷去嘗試最終得出這些合適的引數。

不同C引數對應的最終模型效果:

C parameter: 0.01

Iteration 1 : recall score = 0.958904109589
Iteration 2 : recall score = 0.917808219178
Iteration 3 : recall score = 1.0
Iteration 4 : recall score = 0.972972972973
Iteration 5 : recall score = 0.954545454545

Mean recall score 0.960846151257

C parameter: 0.1

Iteration 1 : recall score = 0.835616438356
Iteration 2 : recall score = 0.86301369863
Iteration 3 : recall score = 0.915254237288
Iteration 4 : recall score = 0.932432432432
Iteration 5 : recall score = 0.878787878788

Mean recall score 0.885020937099

C parameter: 1

Iteration 1 : recall score = 0.835616438356
Iteration 2 : recall score = 0.86301369863
Iteration 3 : recall score = 0.966101694915
Iteration 4 : recall score = 0.945945945946
Iteration 5 : recall score = 0.893939393939

Mean recall score 0.900923434357

C parameter: 10

Iteration 1 : recall score = 0.849315068493
Iteration 2 : recall score = 0.86301369863
Iteration 3 : recall score = 0.966101694915
Iteration 4 : recall score = 0.959459459459
Iteration 5 : recall score = 0.893939393939

Mean recall score 0.906365863087

C parameter: 100

Iteration 1 : recall score = 0.86301369863
Iteration 2 : recall score = 0.86301369863
Iteration 3 : recall score = 0.966101694915
Iteration 4 : recall score = 0.959459459459
Iteration 5 : recall score = 0.893939393939

Mean recall score 0.909105589115

Best model to choose from cross validation is with C parameter = 0.01

在使用機器學習演算法的時候,很重要的一部就是引數的調節,在這裡我們選擇使用最經典的分類演算法,邏輯迴歸!千萬別把邏輯迴歸當成是迴歸演算法,它就是最實用的二分類演算法!這裡我們需要考慮的c引數就是正則化懲罰項的力度,那麼如何選擇到最好的引數呢?這裡我們就需要交叉驗證啦,然後用不同的C引數去跑相同的資料,目的就是去看看啥樣的C引數能夠使得最終模型的效果最好!可以到不同的引數對最終的結果產生的影響還是蠻大的,這裡最好的方法就是用驗證集去尋找了!

模型已經造出來了,那麼怎麼評判哪個模型好,哪個模型不好呢?我們這裡需要好好想一想!

一般都是用精度來衡量,也就是常說的準確率,但是我們來想一想,我們的目的是什麼呢?是不是要檢測出來那些異常的樣本呀!換個例子來說,假如現在醫院給了我們一個任務要檢測出來1000個病人中,有癌症的那些人。那麼假設資料集中1000個人中有990個無癌症,只有10個有癌症,我們需要把這10個人檢測出來。假設我們用精度來衡量,那麼即便這10個人沒檢測出來,也是有 990/1000 也就是99%的精度,但是這個模型卻沒任何價值!這點是非常重要的,因為不同的評估方法會得出不同的答案,一定要根據問題的本質,去選擇最合適的評估方法。

同樣的道理,這裡我們採用recall來計算模型的好壞,也就是說那些異常的樣本我們的檢測到了多少,這也是咱們最初的目的!這裡通常用混淆矩陣來展示。

def plot_confusion_matrix(cm, classes,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    """
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=0)
    plt.yticks(tick_marks, classes)

    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')

import itertools
lr = LogisticRegression(C = best_c, penalty = 'l1')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred_undersample = lr.predict(X_test_undersample.values)

# Compute confusion matrix
cnf_matrix = confusion_matrix(y_test_undersample,y_pred_undersample)
np.set_printoptions(precision=2)

print("Recall metric in the testing dataset: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))

# Plot non-normalized confusion matrix
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
                      , classes=class_names
                      , title='Confusion matrix')
plt.show()

Recall metric in the testing dataset:  0.931972789116

圖片描述

這個圖就非常漂亮了!(並不是說畫的好而是展示的很直接)從圖中可以清晰的看到原始資料中樣本的分佈以及我們的模型的預測結果,那麼recall是怎麼算出來的呢?就是用我們的檢測到的個數(137)去除以總共異常樣本的個數(10+137),用這個數值來去評估我們的模型。利用混淆矩陣我們可以很直觀的考察模型的精度以及recall,也是非常推薦大家在評估模型的時候不妨把這個圖亮出來可以幫助咱們很直觀的看清楚現在模型的效果以及存在的問題。

lr = LogisticRegression(C = best_c, penalty = 'l1')
lr.fit(X_train_undersample,y_train_undersample.values.ravel())
y_pred = lr.predict(X_test.values)

# Compute confusion matrix
cnf_matrix = confusion_matrix(y_test,y_pred)
np.set_printoptions(precision=2)

print("Recall metric in the testing dataset: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))

# Plot non-normalized confusion matrix
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
                      , classes=class_names
                      , title='Confusion matrix')
plt.show()

圖片描述

這可還木有完事,我們剛才只是在下采樣的資料集中去進行測試的,那麼這份測試還不能完全可信,因為它並不是原始的測試集,我們需要在原始的,大量的測試集中再次去衡量當前模型的效果。可以看到效果其實還不錯,但是哪塊有些問題呢,是不是我們誤殺了很多呀,有些樣本並不是異常的,但是並我們錯誤的當成了異常的,這個現象其實就是下采樣策略本身的一個缺陷。

對於邏輯迴歸演算法來說,我們還可以指定這樣一個閾值,也就是說最終結果的概率是大於多少我們把它當成是正或者負樣本。不用的閾值會對結果產生很大的影響。

圖片描述

上圖中我們可以看到不用的閾值產生的影響還是蠻大的,閾值較小,意味著我們的模型非常嚴格寧肯錯殺也不肯放過,這樣會使得絕大多數樣本都被當成了異常的樣本,recall很高,精度稍低 當閾值較大的時候我們的模型就稍微寬鬆些啦,這個時候會導致recall很低,精度稍高,綜上當我們使用邏輯迴歸演算法的時候,還需要根據實際的應用場景來選擇一個最恰當的閾值!

說完了下采樣策略,我們繼續嘮一下過取樣策略,跟下采樣相反,現在咱們的策略是要讓class為0和1的樣本一樣多,也就是我們需要去進行資料的生成啦!

圖片描述

SMOTE演算法是用的非常廣泛的資料生成策略,流程可以參考上圖,還是非常簡單的,下面我們使用現成的庫來幫助我們完成過取樣資料生成策略。

import pandas as pd
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split

credit_cards=pd.read_csv('creditcard.csv')

columns=credit_cards.columns
# The labels are in the last column ('Class'). Simply remove it to obtain features columns
features_columns=columns.delete(len(columns)-1)

features=credit_cards[features_columns]
labels=credit_cards['Class']

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)

很簡單的幾步操作我們就完成過取樣策略,那麼現在正負樣本就是一樣多的啦,都有那麼20多W個,現在我們再通過混淆矩陣來看一下,邏輯迴歸應用於過取樣樣本的效果。資料增強的應用面已經非常廣了,對於很多機器學習或者深度學習問題,這已經成為了一個常規套路啦!

圖片描述

我們對比一下下采樣和過取樣的效果,可以說recall的效果都不錯,都可以檢測到異常樣本,但是下采樣是不是誤殺的比較少呀,所以如果我們可以進行資料生成,那麼在處理樣本資料不均衡的情況下,過取樣是一個可以嘗試的方案!

總結:

對於一個機器學習案例來說,一份資料肯定伴隨著很多的挑戰和問題,那麼最為重要的就是我們該怎麼解決這一系列的問題,大牛們不見得程式碼寫的比咱們強但是他們卻很清楚如何去解決問題。今天我們講述了一個以檢測任務為背景的案例,其中涉及到如何處理樣本不均衡問題,以及模型評估選擇的方法,最後給出了邏輯迴歸在不用閾值下的結果。這裡也是希望大家可以通過案例多多積攢經驗,早日成為技術大牛。

瞭解更多請移步唐宇迪老師技術直播《Python資料分析與機器學習實戰》,線上直播,獨家體系!基於真實資料集,全程程式碼實戰,使用python庫快速處理分析,實戰機器學習經典演算法!

推薦理由: 用Python資料分析最流行的庫Numpy,Pandas,Matplotlib, Scikit-learn結合真實資料集展開視覺化特徵分析與機器學習建模和評估。每次課程涉及一個完整的案例,基於案例講解python庫的使用以及如何建立機器學習模型,對涉及到的機器學習演算法給出最通俗易懂的解釋,幫助大家快速掌握經典機器學習演算法,並應用在實際的案例中。點此報名!

圖片描述