本篇notebook使用SMOTE平衡樣本,Logistic Regression建模,Confusion Matrix與ROC進行模型評估。

該項目使用的數據集是脫敏過的且經過PCA處理的數據,所以我們會先進行數據的探索,然後進行數據的預處理,其中包括不平衡樣本如何處理,我們在這裡會使用SMOTE進行不平衡樣本的處理;接著會應用Logistic Regression來建模並使用confusion matrix和ROC來評估模型。

如果想查看圖表相關的數據可視化及繪製代碼,可以點擊下面的原項目連接,Fork小科的這篇項目,運行本項目後,在想要查看代碼的cell上點擊右上角朝下的小箭頭來展開代碼cell即可查看。

項目連接:信用卡欺詐檢測--邏輯回歸

作者:小科

1. 數據探索

data = pd.read_csv(../input/fraud_detection/creditcardfraud.csv)
data.shape

(284807, 31)

檢查是否有空值

data.isnull().values.sum()

0

查看前四行數據:

正常交易記錄有284315條

異常交易記錄有492條異常交易記錄比率0.173%

1.1.探索交易時間分布

可以看出正常的交易記錄是呈周期性分布的,而異常交易分布較平均。所以可以從交易周期的低頻段入手欺詐交易的檢測。

1.2.探索交易金額分布

2. 數據預處理

2.1. 標準化

鑒於Amount列特徵值的取值範圍相比於PCA處理過的其他28列(V1至V28)特徵值取值範圍相差很大,需統一標準即標準化處理,以此來消除內部構成不同造成的對結果的影響。

from sklearn.preprocessing import StandardScaler

# reshape(-1,1) 將data[Amount]變成只有一列,行數不限定的np.array
data[normAmount] = StandardScaler().fit_transform(data[Amount].values.reshape(-1,1))

print(未標準化的Amount:,data[Amount].values.reshape(-1,1))
print(標準化後的Amount:,StandardScaler().fit_transform(data[Amount].values.reshape(-1,1)))

# 刪除不需要使用到的兩列數據
new_data = data.drop([Time,Amount], axis = 1)
new_data.head()

2.2. 如何平衡樣本

在進行平衡樣本的數據預處理以前,先來談談非平衡樣本的影響以及常用的一些平衡樣本的方法,及適用場景。

不平衡樣本

不平衡的樣本會影響模型的評估效果,嚴重的會帶來過擬合的結果。所以我們需要讓正負樣本在訓練過程中擁有相同話語權或權重。在這裡,稱數據集中樣本較多的一類稱為「大眾類」(majority class),樣本較少的一類稱為「小眾類」(minority class)。

對於不平衡樣本的處理做法總結如下[1]:

常規做法是進行上採樣下採樣,也就是下採樣(Undersampling,欠採樣)大眾類,上採樣(Oversampling,過採樣)小眾類。但是這樣也會有相應的弊端出現。因為上採樣是複製多份小眾類,也就是下採樣是選取部分大眾類,所以上採樣中小眾類會反覆出現一些樣本,這會導致過擬合;下採樣會由於丟失信息而導致欠擬合。

所以針對下採樣信息丟失的問題,有EasyEnsemble,與BalanceCascade兩種改進方法。

對於上採樣的改進方法,可以通過數據合成方法來基於已有的數據生成更多的樣本,其中數SMOTE最為常見;或者可以通過加權的方式來解決問題,但其難點在於如何合理設置權重。

同樣的,我們可以換一種角度,對於正負樣本極不平衡的情況下,我們也可以視其為異常值檢測(Outlier Detection)或一分類(One Class Learning)問題。經典的工具包有One-class SVM等。

以上方法著重於處理數據,但同時也有適用於不平衡樣本的模型比如XGBoost 。

所以解決不平衡樣本的問題有很多種方法,那如何選擇?

正負樣本都非常之少的情況下, 採用數據合成的方式

負樣本足夠多,正樣本非常之少且比例及其懸殊的情況下, 考慮一分類方法在正負樣本都足夠多且比例不是特別懸殊的情況下, 應該考慮採樣或者加權的方法

想了解更多內容可以參考以下列表:

  • 如何解決機器學習中數據不平衡問題。

2.3.使用SMOTE平衡樣本

X = np.array(new_data.iloc[:, new_data.columns != Class]) # 選取特徵列數據
y = np.array(new_data.iloc[:, new_data.columns == Class]) # 選取類別label
print(X shape:,X.shape,
y shape:,y.shape)

from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split

Step 1. 對所有數據進行訓練集與測試集的切分

訓練集:測試集 = 7:3

Step 2. 先對數據進行上採樣,然後對上採樣後的數據進行訓練集與測試集的切分

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

print(訓練集的交易記錄條數:,X_train.shape[0])
print(測試集的交易記錄條數:,X_test.shape[0])
print(交易記錄總數:,X_train.shape[0] + X_test.shape[0])
print(上採樣前,類別為『1』的共有{}個,類別為『0』的共有{}個。.format(sum(y_train==1),sum(y_train==0)))
print(------------------------)

# 對訓練集進行上採樣處理
smote = SMOTE(random_state=2)
X_train_os,y_train_os = smote.fit_sample(X_train, y_train.ravel()) # ravel(): change the shape of y to (n_samples, )

print(上採樣後,訓練集的交易記錄條數:, len(X_train_os))
print(其中,訓練集X的shape:,X_train_os.shape,,y的shape:,y_train_os.shape)
print(交易記錄總數:,X_train_os.shape[0] + X_test.shape[0])
print(上採樣後,類別為『1』的共有{}個,類別為『0』的共有{}個。.format(sum(y_train_os==1),sum(y_train_os==0)))

訓練集的交易記錄條數: 199364

測試集的交易記錄條數: 85443交易記錄總數: 284807

上採樣前,類別為『1』的共有[345]個,類別為『0』的共有[199019]個。

------------------------上採樣後,訓練集的交易記錄條數: 398038其中,訓練集X的shape: (398038, 29) ,y的shape: (398038,)交易記錄總數: 483481上採樣後,類別為『1』的共有199019個,類別為『0』的共有199019個。

3. 建模與模型評估

from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix,roc_curve, auc, recall_score, classification_report

# 定義正則化權重參數,用以控制過擬合
paramaters = {C:np.linspace(1,10, num=10)} # generate sequnce: start = 1, stop = 10
paramaters
# C_param_range = [0.01,0.1,1,10,100]

lr = LogisticRegression()
# 5 folds, 3 jobs run in parallel
lr_clf = GridSearchCV(lr, paramaters, cv=5, n_jobs=3, verbose=5)
lr_clf.fit(X_train_os, y_train_os.ravel())

print(最好的參數:,lr_clf.best_params_)

lr1 = LogisticRegression(C=4, penalty=l1,verbose=5)
lr1.fit(X_train_os, y_train_os.ravel())

混淆矩陣的繪圖function

import itertools

def plot_confusion_matrix(cm, classes,
title=Confusion matrix,
cmap=plt.cm.Blues):
"""
This function prints and plots the confusion matrix.
Normalization can be applied by setting `normalize=True`.
"""
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)

關於模型評估

由於我們是要盡量將所有信用卡欺詐的數據找出來,所以有個很重要的衡量標準: 召回率: ~Recall=TPTP+FN 也就是說,假設1000條信用卡交易記錄中,有10條是欺詐交易,如果最後識別出4條,那麼召回率就為~4/10=0.4

# 對原訓練集X進行預測
y_train_pre = lr1.predict(X_train)

# 訓練集的混淆矩陣
cnf_matrix_train = confusion_matrix(y_train, y_train_pre)

print("Recall metric in the train dataset: {}%".format(100*cnf_matrix_train[1,1]/(cnf_matrix_train[1,0]+cnf_matrix_train[1,1])))

class_names = [0,1]
plt.figure()
plot_confusion_matrix(cnf_matrix_train , classes=class_names, title=Confusion matrix)
plt.show()

# 對原測試集進行預測
y_pre = lr1.predict(X_test)

# 測試集的混淆矩陣
cnf_matrix = confusion_matrix(y_test, y_pre)

print("Recall metric in the test dataset: {}%".format(100*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()

用上採樣處理後的訓練集訓練模型

model = lr1.fit(X_train_os, y_train_os.ravel())

# decision_function(): Predict confidence scores for samples X_test.
y_pred_sample_score = model.decision_function(X_test)
y_pred_sample_score

ROC 評估

FPR,TPR:

FPR(False Positive Rate,假陽性率), TPR(True Positive Rate,真陽性率)分別對應ROC曲線的橫坐標與縱坐標
  • 真陽性率(靈敏度)= 召回率(Recall):如果一個實例類別是positive,分類器預測結果的類別也是positive的比例。這個指標也叫敏感度(sensitivity)或召回率(recall),描述了分類器對positive類別的敏感程度。~TPR=TP/(TP+FN )
  • 假陽性率 = 錯檢率(fallout):如果一個實例類別是negative,分類器預測結果的類別是positive的比例。這個指標也叫錯檢率(fallout)。~FPR=FP/(FP+TN)

fpr, tpr, thresholds = roc_curve(y_test, y_pred_sample_score)

roc_auc = auc(fpr, tpr)
print(準確率:,roc_auc)

# Plot ROC

plt.title(Receiver Operating Characteristic)
plt.plot(fpr, tpr, b,label=AUC = %0.3f% roc_auc)
plt.legend(loc=lower right) # 設置legend的位置
plt.plot([0,1],[0,1],r--) # red, --
plt.xlim([-0.1,1.0])
plt.ylim([-0.1,1.01])
plt.ylabel(True Positive Rate)
plt.xlabel(False Positive Rate)
plt.show()

從圖可以看出,ROC曲線非常接近左上角,即AUC(Area Under Curve)面積很大,說明準確性很高,模型很好。最靠近左上角的ROC曲線的點是錯誤最少的最好閾值(thresholds),其假陽性與假陰性的總數最少。

參考:

  • 非平衡數據處理方式與評估
  • Smote with imbalance data

上述文中所有代碼部分都可以使用在線數據分析協作工具K-Lab復現。K-Lab提供基於Jupyter Notebook的在線數據分析服務,涵蓋Python&R等主流編程語言,實現在線數據處理、模型搭建、代碼調試、撰寫分析報告等數據分析全過程。點擊鏈接即可一鍵fork!

推薦閱讀:

相关文章