本篇notebook使用SMOTE平衡樣本,Logistic Regression建模,Confusion Matrix與ROC進行模型評估。
該項目使用的數據集是脫敏過的且經過PCA處理的數據,所以我們會先進行數據的探索,然後進行數據的預處理,其中包括不平衡樣本如何處理,我們在這裡會使用SMOTE進行不平衡樣本的處理;接著會應用Logistic Regression來建模並使用confusion matrix和ROC來評估模型。
如果想查看圖表相關的數據可視化及繪製代碼,可以點擊下面的原項目連接,Fork小科的這篇項目,運行本項目後,在想要查看代碼的cell上點擊右上角朝下的小箭頭來展開代碼cell即可查看。
項目連接:信用卡欺詐檢測--邏輯回歸 作者:小科
項目連接:信用卡欺詐檢測--邏輯回歸
作者:小科
data = pd.read_csv(../input/fraud_detection/creditcardfraud.csv) data.shape
(284807, 31)
檢查是否有空值
data.isnull().values.sum()
0
查看前四行數據:
正常交易記錄有284315條
可以看出正常的交易記錄是呈周期性分布的,而異常交易分布較平均。所以可以從交易周期的低頻段入手欺詐交易的檢測。
鑒於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()
在進行平衡樣本的數據預處理以前,先來談談非平衡樣本的影響以及常用的一些平衡樣本的方法,及適用場景。
不平衡樣本
不平衡的樣本會影響模型的評估效果,嚴重的會帶來過擬合的結果。所以我們需要讓正負樣本在訓練過程中擁有相同話語權或權重。在這裡,稱數據集中樣本較多的一類稱為「大眾類」(majority class),樣本較少的一類稱為「小眾類」(minority class)。
對於不平衡樣本的處理做法總結如下[1]:
常規做法是進行上採樣與下採樣,也就是下採樣(Undersampling,欠採樣)大眾類,上採樣(Oversampling,過採樣)小眾類。但是這樣也會有相應的弊端出現。因為上採樣是複製多份小眾類,也就是下採樣是選取部分大眾類,所以上採樣中小眾類會反覆出現一些樣本,這會導致過擬合;下採樣會由於丟失信息而導致欠擬合。
所以針對下採樣信息丟失的問題,有EasyEnsemble,與BalanceCascade兩種改進方法。
對於上採樣的改進方法,可以通過數據合成方法來基於已有的數據生成更多的樣本,其中數SMOTE最為常見;或者可以通過加權的方式來解決問題,但其難點在於如何合理設置權重。
同樣的,我們可以換一種角度,對於正負樣本極不平衡的情況下,我們也可以視其為異常值檢測(Outlier Detection)或一分類(One Class Learning)問題。經典的工具包有One-class SVM等。
以上方法著重於處理數據,但同時也有適用於不平衡樣本的模型比如XGBoost 。
所以解決不平衡樣本的問題有很多種方法,那如何選擇?
在正負樣本都非常之少的情況下, 採用數據合成的方式在負樣本足夠多,正樣本非常之少且比例及其懸殊的情況下, 考慮一分類方法在正負樣本都足夠多且比例不是特別懸殊的情況下, 應該考慮採樣或者加權的方法
在正負樣本都非常之少的情況下, 採用數據合成的方式
想了解更多內容可以參考以下列表:
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
上採樣前,類別為『1』的共有[345]個,類別為『0』的共有[199019]個。
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, 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),其假陽性與假陰性的總數最少。
參考:
上述文中所有代碼部分都可以使用在線數據分析協作工具K-Lab復現。K-Lab提供基於Jupyter Notebook的在線數據分析服務,涵蓋Python&R等主流編程語言,實現在線數據處理、模型搭建、代碼調試、撰寫分析報告等數據分析全過程。點擊鏈接即可一鍵fork!
推薦閱讀: