背景介紹
數(shù)據(jù)集包含歐洲持卡人于2013年9月通過信用卡進行的交易痪寻。這個數(shù)據(jù)集顯示了兩天內(nèi)發(fā)生的交易击困,在284,807筆交易中我們有492筆詐騙做盅。
數(shù)據(jù)集非常不平衡最冰,正面類(欺詐)占所有交易的0.172%。
數(shù)據(jù)集只包含數(shù)值輸入變量猖闪,這是PCA變換的結(jié)果鲜棠。不幸的是,由于保密問題培慌,我們無法提供有關(guān)數(shù)據(jù)的原始特征和更多背景信息豁陆。特征V1,V2吵护,... V28是使用PCA獲得的主要組件盒音,沒有用PCA轉(zhuǎn)換的特征是“時間”和“金額”表鳍。“時間”包含數(shù)據(jù)集中每個事務和第一個事務之間經(jīng)過的秒數(shù)祥诽∑┦ィ“金額”是交易額,此特征可用于基于樣本的成本靈敏度學習原押。特征'類'是響應變量胁镐,如果發(fā)生欺詐偎血,則取值1诸衔,否則為0。
初步分析
我們的目的是通過訓練得到一個模型颇玷,這個模型通過特征變量能識別出該筆交易是否發(fā)生欺詐笨农。
由背景介紹可知:
- 正反樣本分布極度不平衡,可能對預測存在影響帖渠,需要衡量采用過采樣還是下采樣來解決這個問題谒亦。
- 數(shù)據(jù)集已經(jīng)過PCA變換,相對干凈空郊,可以將重點放在建模分析上份招。
- 一般評價模型我們用的準確度,但是結(jié)合實際業(yè)務狞甚,在準確度很高的情況下可能FN很高但是TP很低锁摔,翻譯一下就是欺詐識別能力不怎么樣,這不符合我們的預期哼审。我們希望考察模型的欺詐識別能力谐腰,同時也兼顧模型的準確度,所以我們考慮用Recall指標:TP/(TP+FN)
數(shù)據(jù)預處理
由于數(shù)據(jù)已相對干凈涩盾,這里我們著重考慮樣本平衡問題十气。
首先還是處理一下“amount”變量,做一個變換讓變量值落在[-1,1]的區(qū)間內(nèi)春霍。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import StandardScaler
data = pd.read_csv("creditcard.csv")
data['normAmount'] = StandardScaler().fit_transform(data['Amount'].reshape(-1, 1))
data = data.drop(['Time','Amount'],axis=1)
現(xiàn)在我們考慮樣本平衡問題砸西,要讓樣本平衡很容易想到的一個方法就是反面樣本集中抽取和正面樣本集數(shù)量一致的樣本形成新的反面樣本集,也就是所謂的下采樣方法址儒。
number_records_fraud = len(data[data.Class == 1])#欺詐樣本數(shù)量
fraud_indices = np.array(data[data.Class == 1].index)#欺詐樣本索引
normal_indices = data[data.Class == 0].index#正常樣本索引
random_normal_indices = np.random.choice(normal_indices, number_records_fraud, replace = False)#從正常樣本中采樣芹枷,第二個參數(shù)表示采樣數(shù)量
random_normal_indices = np.array(random_normal_indices)
under_sample_indices = np.concatenate([fraud_indices,random_normal_indices])#合并正常樣本和欺詐樣本形成新的數(shù)據(jù)集索引
# 根據(jù)索引形成下采樣數(shù)據(jù)集
under_sample_data = data.iloc[under_sample_indices,:]
print(u"正常樣本比例: ", len(under_sample_data[under_sample_data.Class == 0])/len(under_sample_data))
print(u"欺詐樣本比例: ", len(under_sample_data[under_sample_data.Class == 1])/len(under_sample_data))
print(u"下采樣總樣本數(shù): ", len(under_sample_data))
#正常樣本比例: 0.5
#欺詐樣本比例: 0.5
#總樣本數(shù): 984
下采樣已經(jīng)完成,可以切分數(shù)據(jù)集準備建模了离福。
X_undersample = under_sample_data.loc[:, under_sample_data.columns != 'Class']
y_undersample = under_sample_data.loc[:, under_sample_data.columns == 'Class']
X_train_undersample, X_test_undersample, y_train_undersample, y_test_undersample = train_test_split(X_undersample,y_undersample ,test_size = 0.3 ,random_state = 0)
建模分析
分類算法我們先考慮業(yè)界的流行算法——邏輯回歸杖狼。
確定特征、確定模型之后妖爷,我們還需要考慮的就是模型的參數(shù)蝶涩。利用交叉驗證法我們來選一下邏輯回歸的正則化懲罰力度參數(shù)理朋。
#邏輯回歸的參數(shù)選擇
def printing_Kfold_scores(x_train_data,y_train_data):
fold = KFold(5,shuffle=False)
# 待選參數(shù)數(shù)組
c_param_range = [0.01,0.1,1,10,100]
results = pd.DataFrame(index = range(len(c_param_range),1), columns = ['C_parameter','Mean recall score'])
results['C_parameter'] = c_param_range
# k-fold 后, indices[0]作為訓練集, 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.split(x_train_data,y_train_data),start=1):
lr = LogisticRegression(C = c_param, penalty = 'l1')
lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel())
y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)
#計算recall值
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('')
#選出分數(shù)最高的參數(shù)C
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
這組參數(shù)中c=0.01時表現(xiàn)最好绿聘,下采樣測試集上的recall為 0.938775510204嗽上,暫取c=0.01。(PS:0.01不是最佳參數(shù)熄攘,只是這一組中表現(xiàn)最好的)
看看模型在整個測試集上的表現(xiàn)兽愤。
import itertools
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.3, random_state = 0)
#建模預測
lr = LogisticRegression(C = best_c, penalty = 'l1')
lr.fit(X_train,y_train.values.ravel())
y_pred= lr.predict(X_test.values)
#混肴矩陣
cnf_matrix = confusion_matrix(y_test,y_pred)
np.set_printoptions(precision=2)
print("基于測試集的Recall: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
# 圖形化
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
recall為0.925170068027,表現(xiàn)良好挪圾。
所謂沒有對比就沒有傷害浅萧,我們來看看沒有經(jīng)過下采樣處理的情況。
將輸入改成整個數(shù)據(jù)集哲思,再做一次參數(shù)選擇洼畅。
best_c = printing_Kfold_scores(X_train,y_train)
這回選的是10,且recall為0.61847902217棚赔。
結(jié)果說明下采樣處理能夠顯著提高欺詐識別能力帝簇。
過采樣
上文提到除了下采樣我們還可以采用過采樣,也就是我們構(gòu)造數(shù)據(jù)使欺詐樣本和正常樣本保持平衡靠益。
這里我們采用過采樣中的經(jīng)典算法SMOTE丧肴。
oversampler=SMOTE(random_state=0)
X_oversample,y_oversample=oversampler.fit_sample(X_train,y_train)
X_oversample = pd.DataFrame(X_oversample)
y_oversample = pd.DataFrame(y_oversample)
best_c = printing_Kfold_scores(X_oversample,y_oversample)
lr = LogisticRegression(C = best_c, penalty = 'l1')
lr.fit(X_oversample,y_oversample.values.ravel())
y_pred = lr.predict(X_test.values)
cnf_matrix = confusion_matrix(y_test,y_pred)
np.set_printoptions(precision=2)
print("基于測試集的Recall: ", cnf_matrix[1,1]/(cnf_matrix[1,0]+cnf_matrix[1,1]))
class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix
, classes=class_names
, title='Confusion matrix')
plt.show()
c選擇100,recall為0.918367346939胧后。
小結(jié)
對比下采樣和過采樣芋浮,兩者的recall指標相差不遠,但是下采樣的誤殺率明顯高于過采樣绩卤,因此在處理樣本不平衡問題時途样,SMOTE是被廣泛采用用的手段之一。
綜上濒憋,在解決欺詐檢測類問題時何暇,樣本不平衡問題可能是我們無法避免的問題,一方面欺詐本就屬于不常見樣本凛驮,缺乏歷史數(shù)據(jù)裆站,和安全類軟件的病毒檢測處于類似的境地;另一方面黔夭,參考安全問題宏胯,我們目前解決的還是根據(jù)歷史經(jīng)驗解決欺詐問題,面對越來越復雜的環(huán)境本姥,我們可能需要更多的預防手段肩袍,僅僅依賴歷史數(shù)據(jù)可能還不夠。