信用卡欺詐數(shù)據(jù)分析與建模

數(shù)據(jù)集是來自kaggle上的信用卡進(jìn)行交易的數(shù)據(jù)。此數(shù)據(jù)集顯示兩天內(nèi)發(fā)生的交易,其中284,807筆交易中有492筆被盜刷。數(shù)據(jù)集非常不平衡,被盜刷占所有交易的0.172%。其中數(shù)據(jù)特征v1,v2....v28是某些特征,銀行為了保密,并沒有提供具體代表的內(nèi)容,Class是響應(yīng)變量,如果發(fā)生被盜刷,則取值1,否則為0。Amount為消費(fèi)金額。

首先來看下具體數(shù)據(jù)內(nèi)容:

data = pd.read_csv("/Users/weillschang/Desktop/jupyter notebook/creditcard_fraud/creditcard.csv")
data.head()

輸出內(nèi)容


image

看下class值的分布情況

data['Class'].value_counts()

其中class =0的有284315 class=1也就是屬于欺詐類型的有492,兩者比例超過500,典型的樣本分布不均衡。
對于機(jī)器學(xué)習(xí)常見的分類問題,從訓(xùn)練模型的角度來看,比較理想的情況下是正類和負(fù)類樣本的數(shù)量相差不多,如果某類的樣本數(shù)量很少,那么自然所能提供的信息就很少,用這些不平衡的數(shù)據(jù)訓(xùn)練出來的模型,其預(yù)測結(jié)果偏向訓(xùn)練數(shù)據(jù)數(shù)據(jù)比較多的哪一類,比如在正負(fù)樣本比例為9:1是,當(dāng)預(yù)測精度90%時(shí),即使模型將結(jié)果全部劃分為90%的那一類,其準(zhǔn)確度也有90%,而這是沒有意義的,自然我們需要對其進(jìn)行處理

樣本不均衡問題

對樣本不均衡的處理通常有以下三種方式,這里有參考這個這篇博客數(shù)據(jù)不均衡的處理

  • 欠采樣
    拋棄數(shù)據(jù)集中樣本數(shù)量較多的類別,來緩解不平衡問題,,缺點(diǎn)是會丟失多數(shù)類樣本中的一些重要信息。
  • 過采樣
    對訓(xùn)練集里面過少的樣本進(jìn)行新的數(shù)據(jù)合成,來達(dá)到數(shù)據(jù)平衡的問題,這里比較經(jīng)典的算法是SMOTE算法,他會從相近的幾個樣本中,加入隨機(jī)噪聲,隨機(jī)擾動一個特征,來生成新的數(shù)據(jù)實(shí)例
  • 權(quán)重值的調(diào)整
    也就是說調(diào)整權(quán)重值,將少數(shù)樣本權(quán)重設(shè)置為一個較大權(quán)重,多數(shù)樣本設(shè)置一個較小權(quán)重。

對于信用卡欺詐這個問題,面對不平衡超過500,如果用欠采樣的話,會丟棄20多萬條數(shù)據(jù),這很可能會導(dǎo)致丟失很多重要的信息,而權(quán)重調(diào)整這里也并不容易找到合適的權(quán)重值,這里采用過采樣來合成新數(shù)據(jù)

數(shù)據(jù)預(yù)處理

在合成數(shù)據(jù)之前我們需要對數(shù)據(jù)進(jìn)行常規(guī)的預(yù)處理,將可能的特征屬性進(jìn)行標(biāo)準(zhǔn)化處理,因?yàn)樗惴ǘ技僭O(shè)所有數(shù)據(jù)集的所有特征集中在0附近,并且有相同的方差,如果某個特征方差遠(yuǎn)大于其他特征方差,那么該特征可能在目標(biāo)函數(shù)中占得權(quán)重更大,而且差距太大的話,這會對收斂速度產(chǎn)生很大的影響,甚至可能不收斂,這里采用sk-learn自帶的StandardScaler來進(jìn)行處理

from sklearn.preprocessing import StandardScaler

data['Amount'] = StandardScaler().fit_transform(data['Amount'].reshape(-1, 1))
data.drop(['Time'],axis=1)

處理后該列數(shù)據(jù)會變成均值為0,方差為1的一列數(shù)據(jù)。

使用SMOTE 算法進(jìn)行數(shù)據(jù)合成

SMOTE算法很簡單,可以說是K 近鄰算法的逆操作,以歐氏距離為標(biāo)準(zhǔn)計(jì)算它到少數(shù)類樣本集中所有樣本的距離,得到其k近鄰后,在從其K 近鄰中隨機(jī)選擇N個樣本, 在從這些樣本與原來的樣本之間隨機(jī)構(gòu)建生成一個新的數(shù)據(jù)。
如下:

## 先對數(shù)據(jù)集進(jìn)行分割,按30% 劃分
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
from sklearn.cross_validation import KFold, cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report 
X = data.loc[:, data.columns != 'Class']
Y = data.loc[:, data.columns == 'Class']
features_train, features_test, labels_train, labels_test = train_test_split(X,   Y,   test_size=0.2,  random_state=0)
  ## sample 生成
oversampler=SMOTE(random_state=0)
new_features,new_labels=oversampler.fit_sample(features_train,labels_train)

這樣數(shù)據(jù)合成之后,二者便會有相同的樣本數(shù)了,接下來便可以進(jìn)行邏輯回歸生成模型

模型的生成與調(diào)參

對于有監(jiān)督學(xué)習(xí)算法,過擬合比欠擬合有時(shí)更難處理,尤其是過多的特征與過少的數(shù)據(jù),最會容易導(dǎo)致過擬合問題
解決過擬合問題,通常有增加數(shù)據(jù)集,和減少模型復(fù)雜度(比如減少學(xué)習(xí)特征,讓某個特征不被模型學(xué)習(xí)到),而正則化則是減少模型復(fù)雜度的一種方法
正則化中我們將保留所有的特征變量,但是會減小特征變量的數(shù)量級(參數(shù)數(shù)值的大小θ(j))。
sk_learn 邏輯回歸算法,提供了c值也就是正則化系數(shù)倒數(shù)供我們選擇,c值越小,對應(yīng)越強(qiáng)的正則化,越強(qiáng)的正則化越能得到一個更簡單的假設(shè)曲線,也就越能減少過擬合的風(fēng)險(xiǎn)(可以,這很“奧體姆剃刀”),下面使用不同的c值進(jìn)行準(zhǔn)確率的計(jì)算

from sklearn.linear_model import LogisticRegression
fold = KFold(len(os_labels),5,shuffle=False) 
c_param_range = [0.01,0.1,1,10,100]
for c_param in c_param_range:
    print('C parameter: ', c_param)
    listacc=[]
    for iteration, indices in enumerate(fold,start=1):
       
        lr = LogisticRegression(C = c_param, penalty = 'l1')
        lr.fit(new_features.iloc[indices[0],:],new_labels.iloc[indices[0],:].values.ravel())
        predicted_data=lr.predict(new_features.iloc[indices[1],:].values)
        recall_acc = recall_score(new_labels.iloc[indices[1],:].values,predicted_data)
        listacc.append(recall_acc)
        print" recall score = {}".format(recall_acc)
    print "mean_acc:{}".format(float(sum(listacc))/len(listacc))

penalty參數(shù)選擇l1正則化范式,比較適合模型的特征非常多,同時(shí)希望將一些不重要的特征系數(shù)歸零,從而讓模型系數(shù)更加稀疏,權(quán)重值更低
這里衡量精度采用recall_score,也就是召回率,而不是準(zhǔn)確率和正確率。

召回率=TP/(TP+FN)
也就是真正為正例的樣本中,正確預(yù)測為正例的比例
這里以欺詐為正例。

也就是說假設(shè)真的欺詐數(shù)目有10個,我們正確預(yù)測到了其中9個,其召回率也就是0.9。
如上圖最終輸出結(jié)果為

.....
('C parameter: ', 0.01)
 recall score = 0.922580645161
 recall score = 0.901315789474
 recall score = 0.931415292686
 recall score = 0.922632197931
 recall score = 0.921346215144
mean_acc:0.919858028079
('C parameter: ', 0.1)
 recall score = 0.922580645161
 recall score = 0.907894736842
 recall score = 0.932123492309
 recall score = 0.924105032919
 recall score = 0.922500302261
mean_acc:0.921840841899
('C parameter: ', 1)
 recall score = 0.916129032258
 recall score = 0.907894736842
 recall score = 0.932167754786
 recall score = 0.924324859037
 recall score = 0.922687154461
mean_acc:0.920640707477
.......

這里選取recall平均值最大的c值,即為0.1
接下來,把預(yù)測結(jié)果的結(jié)果精度顯示在一個混淆矩陣?yán)锩妫聪庐?dāng)前預(yù)測結(jié)果,(繪制混淆矩陣代碼,參考自網(wǎng)上)

from sklearn.cross_validation import KFold, cross_val_score
from sklearn.metrics import confusion_matrix,recall_score,classification_report 
import itertools
from sklearn.linear_model import LogisticRegression
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')
image

單單看召回率還是不錯,可是右上角的還有1045個的誤殺(即將正常的判斷為欺詐),接下來考慮設(shè)置不同的判斷閾值來做更嚴(yán)格的劃分并畫出混淆矩陣,默認(rèn)的是0.5 即將將結(jié)果劃分為超過50%一側(cè)的值。

lr = LogisticRegression(C = 0.1, penalty = 'l1')
lr.fit(new_features,new_labels.values.ravel())
y_pred_proba = lr.predict_proba(features_test.values)
from __future__ import division 
thresholds = [0.6,0.7,0.8,0.9]

plt.figure(figsize=(10,10))

j = 1
for i in thresholds:
    y_test_predictions_high_recall = y_pred_proba[:,1] > i
    
    plt.subplot(3,3,j)
    j += 1
    
    cnf_matrix = confusion_matrix(labels_test,y_test_predictions_high_recall)
    np.set_printoptions(precision=2)

    print "Recall:{}".format(cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
   
    class_names = [0,1]
    plot_confusion_matrix(cnf_matrix
                          , classes=class_names
                          , title='Threshold >= %s'%i) 

結(jié)果如下


image

很明顯 隨著判斷閾值越大(即判斷的要求更嚴(yán)格),召回率是下降趨勢的,但是誤殺的概率則明顯下降,綜合來看0.8附近是個相對比較合理的值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容