数据集来源于Kaggle https://www.kaggle.com/mlg-ulb/creditcardfraud, 用于预测信用卡用户是否会落入诈骗组,这里发一个中文版本的存稿
首先导入数据
dat = pd.read_csv("E:/study/machine learning/credit card fraud/creditcard.csv")
df = pd.DataFrame(dat)
df.describe()
Time V1 V2 ... Amount Class Amount_log
0 0.0 -1.359807 -0.072781 ... 149.62 0 5.008166
1 0.0 1.191857 0.266151 ... 2.69 0 0.993252
2 1.0 -1.358354 -1.340163 ... 378.66 0 5.936665
3 1.0 -0.966272 -0.185226 ... 123.50 0 4.816322
4 2.0 -1.158233 0.877737 ... 69.99 0 4.248495
5 2.0 -0.425966 0.960523 ... 3.67 0 1.302913
6 4.0 1.229658 0.141004 ... 4.99 0 1.609438
7 7.0 -0.644269 1.417964 ... 40.80 0 3.708927
8 7.0 -0.894286 0.286157 ... 93.20 0 4.534855
9 9.0 -0.338262 1.119593 ... 3.68 0 1.305626
10 10.0 1.449044 -1.176339 ... 7.80 0 2.055405
11 10.0 0.384978 0.616109 ... 9.99 0 2.302585
12 10.0 1.249999 -1.221637 ... 121.50 0 4.79...
数据共31列,除去是否落入诈骗组的“Class”组外,另有时间和消费额,以及用于隐藏用户信息、经过PCA处理过的V1至V28,我们首先来看数据在Class组中的分布
plt.figure(figsize=(7,5))
sns.countplot(df['Class'])
plt.title("Fraud and not Fraud Class Count", fontsize=18)
plt.xlabel("Fraud and not Fraud", fontsize=15)
plt.ylabel("Count", fontsize=15)
plt.show()
我们可以看到,这是一组明显的不平衡数据集,参与过诈骗的用户量远远少没参与过诈骗的用户,这意味着我们在建模前,首先要对数据集的不平衡性进行处理,否则模型会始终倾向于将用户分入非诈骗组。
我们继续分析另外两个明确定义的变量,下图是经过log处理后的消费额Amount与Class的箱型图,从图中我们可以看到,信用卡诈骗用户的消费额范围更广,且IQR明显高于非诈骗用户,但最高的消费额存在于非诈骗组。
df['Amount_log'] = np.log(df['Amount'] + 0.01) # engineer the data for better visualization
plt.figure(figsize=(7,5))
sns.boxplot(x = "Class", y = "Amount_log", data = df)
plt.show()
fraud = df[df["Class"] == 1]
nonfraud = df[df["Class"] == 0]
plt.figure(figsize=(7,20))
plt.subplot(211)
ax1 = sns.scatterplot(x=fraud["Time"],y=fraud["Amount"])
plt.subplot(212)
ax2 = sns.scatterplot(x=nonfraud["Time"],y=nonfraud["Amount"])
plt.show()
在不同的时间维度上,诈骗组和非诈骗组的消费额虽都整体偏低,但分布都非常均匀,该图显示时间与是否诈骗没有什么明显的关系。在之后建模时,我们可以考虑删除时间变量。
我们现在再来看看变量之间的相关性
df_corr = df.corr()
plt.figure(figsize=(7,5))
sns.heatmap(df_corr, cmap="YlGnBu")
plt.title('Heatmap correlation')
plt.show()
从上图我们可以看出,绝大多数的变量之间都没有相关关系,这也侧面证明了变量也确实经过了PCA,不需要再次进行PCA的处理。
首先,我们需要将数据集分为训练集和测试集两部分
X = df.drop(["Amount_log", "Time", "Class"],axis=1)
y = df["Class"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 12)
处理不平衡数据有多种方法,考虑到训练时间问题(因为我们这里选择的是需要较长训练时间的SVM),这里我们选择undersampling,从多数集中抽样,使诈骗组和非诈骗组中的用户量成为同一个量级
undersampling_train = pd.concat([X_train,y_train],axis=1)
undersampling_train_nonfraud = undersampling_train[undersampling_train['Class']==0].sample(300)
undersampling_train_fraud = undersampling_train[undersampling_train['Class']==1]
undersampling_train_total = pd.concat([undersampling_train_nonfraud,undersampling_train_fraud],axis=0)
undersampling_X = undersampling_train_total.drop("Class",axis=1)
undersampling_y = undersampling_train_total["Class"]
我们选择的方法是SVM
def confusion_matrix_1(CM):
fig, ax = plot_confusion_matrix(conf_mat=CM)
plt.title("The Confusion Matrix 1 of Undersampled dataset")
plt.ylabel("Actual")
plt.xlabel("Predicted")
plt.show()
print("The accuracy is "+str((CM[1,1]+CM[0,0])/(CM[0,0] + CM[0,1]+CM[1,0] + CM[1,1])*100) + " %")
print("The recall from the confusion matrix is "+ str(CM[1,1]/(CM[1,0] + CM[1,1])*100) +" %")
ss = SVC(kernel="linear")
ss.fit(undersampling_X,undersampling_y)
y_pred = ss.predict(X_test)
cmss = confusion_matrix(y_test, y_pred)
confusion_matrix_1(cmss)
The accuracy is 95.20519086542512 %
The recall from the confusion matrix is 87.09677419354838 %
95%的正确率看起来还不错,但我们在分析信用卡诈骗问题时,更多是用recall来衡量模型的准确度,而87%的recall看起来就有提升的空间了
我们尝试使用GridSearchCV来调整SVM的参数
turned_parameters = [{'kernel':['linear','rbf','poly'],'gamma':['auto'],'C': [1,10,100,1000]}]
svm = GridSearchCV(SVC(), turned_parameters,cv=5,scoring='recall')
svm.fit(undersampling_X, undersampling_y)
print("Best parameters set found on Training dataset:")
print()
print(svm.best_params_)
由GridSearchCV,最合适的参数为{‘C’: 100, ‘gamma’: ‘auto’, ‘kernel’: ‘rbf’},我们使用这组参数来重新对数据集进行建模
{'C': 100, 'gamma': 'auto', 'kernel': 'rbf'}
The accuracy is 82.39796634925986 %
The recall from the confusion matrix is 94.35483870967742 %