在Github 上面找到了一个信用卡诈骗数据集:
https://github.com/georgymh/ml-fraud-detection
数据源于 kaggle 的信用卡诈骗数据。
其中给出了三种大方法,分别是神经网络,kmeans,以及逻辑回归。
但是今天我想要用XGBoost 试一试。
import sklearn as sk
import xgboost as xgb
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split # 功能:数据集按比例切分为训练集和测试集
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.preprocessing import scale # 数据集标准化
import matplotlib.pyplot as plt
from pandas_ml import ConfusionMatrix # 引入混淆矩阵
# https://github.com/pandas-ml/pandas-ml/
df = pd.read_csv('creditcard.csv', low_memory=False)
X = df.iloc[:,:-1] # 基于位置的纯整数索引
y = df['Class'] # 类别
X_scaled = scale(X) # 标准化
train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.35 ,random_state = 12)
dtrain = xgb.DMatrix(train_X, label = train_y)
dtest = xgb.DMatrix(test_X, label = test_y)
param = {
# --------------- Tree Booster 参数 ---------------
'eta':0.3, # 每个循环的学习步长,就是sklearn的learning_rate
'booster':'gbtree', # gbtree使用基于树的模型进行提升计算,gblinear使用线性模型进行提升计算
'max_depth':5, # 每棵树的最大深度,默认6
'silent':1, # 默认0,而1表示静默模式开启,不会输出任何信息
# ---------------------------------------------------------------------
'num_class':2, # 类别个数,记得改!
# ---------------------------------------------------------------------
'min_child_weight':1.0, # 决定最小叶子节点样本权重和
# 'max_leaf_nodes': # 树上最大的节点或叶子的数量。 可以替代max_depth的作用。
'scale_pos_weight':1.0, # 调节正负样本不均衡问题
'gamma':0.1, # 这个参数的值越大,算法越保守,默认0
'max_delta_step':0, # 这参数限制每棵树权重改变的最大步长。0意味着没有约束。
'subsample':1.0, # 控制对于每棵树,随机采样的比例。
# 减小这个参数的值,算法会更加保守,避免过拟合。
# 如果这个值设置得过小,它可能会导致欠拟合。一般0.5-1
'colsample_bytree':1.0, # 用来控制每棵随机采样的列数的占比(每一列是一个特征)。一般0.5-1
'colsample_bylevel':1.0, # 控制树的每一级的每一次分裂,对列数的采样的占比。
# 'scale_pos_weight':1, # 在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛
# --------------- Linear Booster 参数 ---------------
# 'lambda_bias':0, # 在偏置上的L2正则
# 'lambda':1, # 权重的L2正则化项。默认1
# 'alpha':1, # 权重的L1正则化项。默认1
# --------------- Task 参数 ---------------
'objective':'multi:softprob', # 定义学习任务及相应的学习目标
#'objective':'reg:linear', # 线性回归
#'objective':'reg:logistic', # 逻辑回归
#'objective':'binary:logistic', # 二分类的逻辑回归问题,输出为概率
#'objective':'binary:logitraw', # 二分类的逻辑回归问题,输出结果为 wTx,wTx指机器学习线性模型f(x)=wTx+b
#'objective':'count:poisson' # 计数问题的poisson回归,输出结果为poisson分布
#'objective':'multi:softmax' # 让XGBoost采用softmax目标函数处理多分类问题,同时需要设置参数num_class
#'objective':'multi:softprob' # 和softmax一样,但是输出的是ndata * nclass的向量,
# 可以将该向量reshape成ndata行nclass列的矩阵。
# 每行数据表示样本所属于每个类别的概率。
'base_score':0.5, # 所有实例的初始预测评分, global bias
# 'eval_metric'
'seed':0 # 默认为0
}
num_round = 20 # 循环次数
bst = xgb.train(param, dtrain, num_round) # 参数 + 训练集 + 循环次数
preds = bst.predict(dtest) # 对测试集作出预测
print("训练完成")
best_preds = np.asarray([np.argmax(line) for line in preds]) # np.argmax(line) 返回沿着line轴的最大值的索引
# np.asarray 将列表转换为数组
# 精确度(Precision):
# P = TP/(TP+FP) ; 反映了被分类器判定的正例中真正的正例样本的比重
print("精确度(Precision):", precision_score(test_y, best_preds, average='macro')) # 97.32%
print("召回率(Recall):", recall_score(test_y, best_preds, average='macro')) # 90.34%
predicted_y = np.array(best_preds)
right_y = np.array(test_y)
# 混淆矩阵的每一列代表了预测类别,
# 每一列的总数表示预测为该类别的数据的数目;
# 每一行代表了数据的真实归属类别,
# 每一行的数据总数表示该类别的数据实例的数目。
confusion_matrix = ConfusionMatrix(right_y, predicted_y)
# print("Confusion matrix:\n%s" % confusion_matrix)
# confusion_matrix.plot(normalized=True)
# plt.show()
confusion_matrix.print_stats()
'''
population: 99683
P: 176
N: 99507
PositiveTest: 150
NegativeTest: 99533
TP: 142
TN: 99499
FP: 8
FN: 34
TPR: 0.806818181818
TNR: 0.999919603646
PPV: 0.946666666667
NPV: 0.99965840475
FPR: 8.03963540253e-05
FDR: 0.0533333333333
FNR: 0.193181818182
ACC: 0.999578664366
F1_score: 0.871165644172
MCC: 0.87374835762
informedness: 0.806737785464
markedness: 0.946325071417
prevalence: 0.00176559694231
LRP: 10035.5071023
LRN: 0.193197350544
DOR: 51944.3308824
FOR: 0.000341595249817
'''
进一步的问题在于,如何使用类似 SKlearn 中的 balance 参数,让少数的诈骗1和大多数的非诈骗0均衡一下,让1的学习权重更重。
这样的后果就是 False Negative 值降低,但是False Positive 值急剧增高。关键看采用哪种策略吧。
目前的研究在于这个
'scale_pos_weight':1.0, # 调节正负样本不均衡问题
这个参数是 XGBoost 的 SKlearn API中的,如果直接使用XGBoost好像就没有这个参数?当然也可能是我不明白。反正目前的参数是不错的。
相对的,在 Github 中的程序有一个线性回归的例子就带有这个 balance 参数,效果很奇妙...