背景:本文数据样本来源于2013年9月欧洲持卡人在两天内进行的284808笔信用卡交易,其中有493笔欺诈交易。由于保密问题,只包含作为PCA转化结果的数字输入变量,特征V1,V2,… V28是使用PCA获得的主要组件,交易金额是常量,是否欺诈(class)是分类变量。
目的:得到较为准确的欺诈检测方法
过程:使用python pandas,numpy,matplotlib,以及机器学习库中的逻辑回归LogisticRegression
1,获取数据
获取数据之后先不着急选用模型,先观察数据集
数据集一共31列 ,其中
time:交易时长
V1-V28:特征集
amount:交易金额
class:0表示正常样本,1表示异常样本
amount 列没有经过脱敏,先对amount进行数据规范化处理,得到nomamount,以及删除对模型没有印象的数据
数据处理完成,接下来查看数据是否有缺失值,如果有,则需要判断使用中位数还是均值等填充,以便接下来的模型训练
数据无缺失值,不需要进行缺失值处理,查看样本分布规则
正常样本跟异常样本数据存在很大的不均衡,对后续模型评估会产生很大影响,对样本不均衡的处理方法有两种方案(也许还有,只是我还没学到):
1,下采样:将数据变得同样少,缺点:误杀值也会变的很大
2,过采样:将数据变得同样多,缺点:容易导致召回率变低
这里我们采用两6种方法,具体查看两种方法的区别
下采样:
下采样完成,开始交叉验证:数据划分
训练集,测试集,其中训练集包含有验证集,在选用模型划分数据可以体现
使用K折交叉验证,这里对比了L1正则化跟L2正则化,结果一致
# 建立模型,进行验证,求出最优参数解
from sklearn.model_selection import KFold, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, recall_score, classification_report
def printing_Kfold_scores(x_train_data,y_train_data):
fold = KFold(5,shuffle=False)
# 会得到一个可迭代对象(可以用 for 循环遍历取出),可以遍历5次,每次遍历出来的会是一个2值列表,
# 存放每一次的训练集和验证集的索引
# Different C parameters
c_param_range = [0.01,0.1,1,10,100]
results_table = pd.DataFrame(index = range(len(c_param_range),2), columns = ['C_parameter','Mean recall score'])
results_table['C_parameter'] = c_param_range
# the k-fold will give 2 lists: train_indices = indices[0], test_indices = 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),start=1):
# enumerate 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
# iteration 表示第几次循环,indices 是一个列表里面有两个元素,indices[0]表示训练集索引,indices[1] 表示验证集索引
# Call the logistic regression model with a certain C parameter
lr = LogisticRegression(C = c_param, penalty = 'l1', solver = 'liblinear')
# Use the training data to fit the model. In this case, we use the portion of the fold to train the model
# with indices[0]. We then predict on the portion assigned as the 'test cross validation' with indices[1]
lr.fit(x_train_data.iloc[indices[0],:],y_train_data.iloc[indices[0],:].values.ravel())
# Predict values using the test indices in the training data
y_pred_undersample = lr.predict(x_train_data.iloc[indices[1],:].values)
# Calculate the recall score and append it to a list for recall scores representing the current c_parameter
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.loc[j,'Mean recall score'] = np.mean(recall_accs)
j += 1
print('')
print('Mean recall score ', np.mean(recall_accs))
print('')
best_c_l1 = results_table.loc[results_table['Mean recall score'].astype(float).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_l1)
print('*********************************************************************************')
return best_c_l1, results_table
把l1换成l2查看两种正则化惩罚项结果
两种模型最优参数C均为0.01,但是l2评分明显小于l1,下面使用该参数进行模型评估
这里选用混淆矩阵评估模型,(还有ROC曲线,精确率等常用模型)
# 下采样混淆矩阵
lr = LogisticRegression(C = 0.01, penalty = 'l1', solver = 'liblinear')
lr.fit(x_train_undersample, y_train_undersample.values.ravel())
y_pred_undersample = lr.predict(x_test_undersample.values)
cnf_matrix = confusion_matrix(y_test_undersample, y_pred_undersample)
np.set_printoptions(precision = 2)
print('Recall matric in the testiing datasets:', cnf_matrix[1,1]/(cnf_matrix[1,0] + cnf_matrix[1,1]))
plt.figure()
plt.imshow(cnf_matrix, interpolation = 'nearest', cmap = plt.cm.Blues)
plt.title('Confusion Mtric')
plt.colorbar()
tick_marks = np.arange(2)
plt.xticks(tick_marks, [0,1], rotation = 0)
plt.yticks(tick_marks,[0,1])
thresh = cnf_matrix.max() / 2.
for i, j in itertools.product(range(cnf_matrix.shape[0]), range(cnf_matrix.shape[1])):
plt.text(j, i, cnf_matrix[i, j],
horizontalalignment="center",
color="white" if cnf_matrix[i, j] > thresh else "black")
plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')
这里可以看到召回率大约在0.94左右,我们再看看原数据的混淆矩阵,将上面的训练模型数据改成原数据即可
召回率明显下降很多,只有0.55左右,召回率偏低,但是误杀数并不多,在来看看使用原数据测试集对下采样进行预测
这里可以看到,召回率0.91左右,但是误杀数高达10544,也就是说预测出了135个结果,误杀了10544个,接下来,我们需要查看阈值对混淆矩阵的影响,系统默认的阈值是0.5,意思是对所有概率或评分在0.5以上的,分类到1,0.5一下的,分类到0,所以我们需要根据实际需求设置阈值
这里给一点小建议,凡是出现两次或以上次数的代码,都将它打包成函数调用(教训)
def plot_confusion_matric(cm,classes,title = 'Confusion matric', cmap = plt.cm.Blues):
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.xlabel('True label')
plt.ylabel('Predicted label')
混淆矩阵生成函数
# 不同阈值对混淆矩阵的影响
lr = LogisticRegression(C = 0.01, penalty = 'l1', solver = 'liblinear')
lr.fit(x_train_undersample, y_train_undersample.values.ravel())
y_pred_undersample = lr.predict_proba(x_test_undersample.values)
thresholds = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
plt.figure(figsize = (10,10))
j = 1
for i in thresholds:
y_test_pred_hight_recall = y_pred_undersample[:,1] > i
plt.subplot(3,3,j)
j += 1
cnf_matrix = confusion_matrix(y_test_undersample, y_test_pred_hight_recall)
np.set_printoptions(precision = 2)
print('Recall matric in the testiing datasets:', cnf_matrix[1,1]/(cnf_matrix[1,0] + cnf_matrix[1,1]))
class_name = [0,1]
plot_confusion_matric(cnf_matrix, classes = class_name, title = 'Thresholds >= %s'%i)
这里可以看到不同阈值对召回率的影响,具体阈值跟召回率的选择,需要根据具体情景选择,下面我们在看看使用过采样处理数据
过采样的原理:在少数样本T中,找到特征向量Xi的K个近邻,在这K个中随机选择一个,重复这以过程,直到达到目标数N为止,可以说是基于“差值”合成新样本的算法
导入相关包,因为过采样跟下采样处理原理不一样,所以需要重新加载一遍数据
开始过采样,交叉验证,除了过采样之外,验证及混淆矩阵跟下采样差不多,这里就不多累赘了
混淆矩阵中,跟图14对比,可以明显的看到误杀值下降很大,说明过采样明显减少了误杀值,所以在数据不均衡的情况下,较经常使用过采样,但是有一点,那是运行速度很慢,我的老年机跑了十几分钟.......