本人最近出于公司的业务需求,要构建反欺诈模型,所以测试了逻辑回归、决策树、随机森林、神经网络、SVM等一系列单一机器学习算法,以及集成学习等方法,在这里将实现方式分享给各位,毕竟业务场景不一样,结果可能完全相悖,本文的目的仅希望各位在实现各类算法时能较快速的上手。
由于数据的商业保密性,此处不贴出数据集,不喜勿喷。好了,废话不多说,开始正文。
数据初识
我整合的数据集500多个变量,包含申请信息、贷款信息、第三方数据等,目标是是否欺诈,FRAUD_FLAG就是我们的目标变量。以下是比较初步的数据整理
数据初识
- ############################# 数据读取 ###########################################
- # 读取制表分隔符文本类数据
- fraud <- read.table('F:/pxw/R-3.4.1/DATA/TB_FRAUD_MAIN_DATA_INNER_NMISS3.csv',
- header =TRUE,stringsAsFactors=FALSE, sep=",");
- summary(fraud);
- # 缺失值替换为0
- fraud[is.na(fraud)]=0
- # 选取变量
- # FRAUD_FLAG <- fraud$FRAUD_FLAG
- # fraud <- cbind(FRAUD_FLAG,fraud[,435:462])
- # 转换为数据框
- fraud <- as.data.frame(fraud)
- # 将预测目标转换为因子型
- fraud$FRAUD_FLAG <- as.factor(fraud$FRAUD_FLAG)
- # 统计字符变量占比
- # round(prop.table(table(fraud$DKFS))*100,digits = 1)
- # 按applyID排序,并去除一些没有信息的变量,如申请编号之类的
- fraud <- subset(fraud[order(fraud$applyID),],
- select = c(-applyID,-report_id,-APPLYTIME,-ZJYCJYSJ,-fkrq,-htbh))
- table(fraud$FRAUD_FLAG)
- # 20471 854
- prop.table(table(fraud$FRAUD_FLAG))
- # 0 1
- # 0.95995311 0.04004689
如上所示,2万多笔,欺诈比例约4%左右,算是比较低的了,这还是放宽欺诈定义的结果,按照严格的定义,欺诈比例只有6‰左右。
- # 各种R包的准备
- library(lattice)
- library(ggplot2)
- library(e1071)
- # roc曲线包
- library(pROC)
- # smote抽样包
- library(DMwR)
- # 机器学习模型参数自动调整包/数据集划分
- library(caret)
- # 随机森林包
- library(randomForest)
- # 混淆矩阵包
- library(gmodels)
变量选择
在建模之前我们需要进行数据质量的检验、探索分析等,由于此文的目的是各类机器学习算法的快速实现,此处略过,直接来到变量的选择。
变量选择之前,由于我们的目标变量是严重失衡数据,我们需要先利用somte方法对少类进行过采样,敲黑板了,过采样的方法非常重要,若不进行过采样,可能完全没法识别欺诈样本,当然,这跟我们的数据质量、数据广度、数据浓度是有关系的。
- # smote抽样
- # smote重采样,k表示少数类产生新样本的最近邻参数,默认是5,
- # perc.over表示对少数类样本过采样产生新样本的百分比,比如perc.over=80,在少数类样本=100的情况下,
- # 产生80个少数类样本,总计180个少数类样本
- # perc.under表示对多数类样本欠采样产生新样本的百分比,比如perc.under=300,在少数类样本=100,且perc.over=200的情况下,
- # 将从多数类样本中抽取100*200%*300%=600个多数类的样本,总计600个多数类样本
- #设置随机数种子,保证实验可重复性
- set.seed(500)
- train_smote_set1 <- SMOTE(FRAUD_FLAG ~ ., data=fraud, k=10, perc.over = 100, perc.under = 115)
- table(train_smote_set1$FRAUD_FLAG)
- prop.table(table(train_smote_set1$FRAUD_FLAG))
变量的重要性我们可以通过随机森林实现,由randomforest函数的importance来指定,随机森林变量重要性的计算方式可以通过4种方法,分类问题可以选基于OOB,计算预测误差率的MeanDecreaseAccuracy方法,和基于样本拟合模型,计算Gini系数的MeanDecreaseGini方法;回归问题可以类似的选择%IncMSE和IncNodePrity方法。此处我们选择基于信息增益原理的Gini系数。
- # 设置随机数种子,可重复
- set.seed(300)
- # formula目标变量和自变量方程
- # data是训练集
- # ntree树的数目,默认是500
- # mtry每次划分中随机选择的特征数,默认是变量数的平方根
- fraud_model1 <- randomForest(formula=FRAUD_FLAG ~ .,
- data=train_smote_set1,
- ntree=500,
- mtry=sqrt(ncol(fraud)),
- maxnotes=10,
- importance=TRUE)
- fraud_model1
- # Call:
- # randomForest(formula = FRAUD_FLAG ~ ., data = train_smote_set1, ntree = 500, mtry = sqrt(ncol(fraud)), maxnotes = 10, importance = TRUE)
- # Type of random forest: classification
- # Number of trees: 500
- # No. of variables tried at each split: 23
- #
- # OOB estimate of error rate: 21.56%
- # Confusion matrix:
- # 0 1 class.error
- # 0 672 310 0.3156823
- # 1 270 1438 0.1580796
- 我们可以看到训练模型的误差率是21.56%,算算马马虎虎的。
- # 查看变量重要性,图片无法上传
- varImpPlot(fraud_model1,main='随机森林特征选择')
- # 按GINI系数对变量重要性排序
- x <- sort(importance(fraud_model1)[,4],decreasing = T)
- # 提取GINI值大于10的列名
- y <- colnames(as.data.frame(t(subset(x,x>=10))))
- # 合并欺诈标识
- y[length(y)+1] <- "FRAUD_FLAG"
- # 提取对应的变量作为建模表
- fraud_imp_var <- subset(fraud,select = y)
这里Gini系数≥10的变量一共34个变量,接下来就是数据集的切分了。事实上我们还可以进行lasso或弹性网络或逐步回归进一步筛选变量,我最后的探索结果表明,基于这样一个数据量的数据集,不太适合采用非常高维的数据,很容易造成overfitting的现象,而在少变量的情形下(10个以内),逻辑回归更加稳定。
数据划分
- # -------------------------------- 数据划分 --------------------------------------
- # 设置随机数种子,保证实验可重复性
- set.seed(1234)
- # 调用caret包的 createDataPartition 函数划分训练和测试集,y是分层抽样变量,p为抽样比
- # 若有多个分层变量,可为每个分层变量指定抽样比,若只有一个抽样比,则每一层抽样比例相同
- spilt_index <- createDataPartition(y=c(fraud_imp_var$FRAUD_FLAG),
- p=c(0.7),list = FALSE)
- train_set <- fraud_imp_var[spilt_index,]
- test_set <- fraud_imp_var[-spilt_index,]
- # 统计占比
- prop.table(table(train_set$FRAUD_FLAG))
- # 0 1
- # 0.9596731 0.0403269
- prop.table(table(test_set$FRAUD_FLAG))
- # 0 1
- # 0.96060653 0.03939347
如上述结果,测试集和训练集的欺诈比例都在4%左右。准备工作做完了,就开始正式建模。
逻辑回归
- # -------------------------------- 逻辑回归 --------------------------------------
- set.seed(500)
- train_smote_set <- SMOTE(FRAUD_FLAG ~ ., data=train_set, k=10, perc.over = 100, perc.under = 200)
- table(train_smote_set$FRAUD_FLAG)
- prop.table(table(train_smote_set$FRAUD_FLAG))
- logit_model <- glm(FRAUD_FLAG ~.,family=binomial(link='logit'),data=train_smote_set)
- summary(logit_model)
- # Call:
- # glm(formula = FRAUD_FLAG ~ ., family = binomial(link = "logit"),
- # data = train_smote_set)
- #
- # Deviance Residuals:
- # Min 1Q Median 3Q Max
- # -2.51711 -0.95808 -0.02472 0.99347 2.32549
- #
- # Coefficients: (3 not defined because of singularities)
- # Estimate Std. Error z value Pr(>|z|)
- # (Intercept) -3.268e+00 3.815e-01 -8.566 < 2e-16 ***
- # VAR_SUM 2.429e-01 2.855e-02 8.506 < 2e-16 ***
- # SQCS 4.512e-01 3.608e-01 1.251 0.211009
- # 。。。。。
- # Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
- #
- # (Dispersion parameter for binomial family taken to be 1)
- #
- # Null deviance: 3338.2 on 2407 degrees of freedom
- # Residual deviance: 2802.4 on 2376 degrees of freedom
- # AIC: 2866.4
- # Number of Fisher Scoring iterations: 5
- # 逐步回归重选择变量,direction可以选back和forward以及both即逐步回归
- logit_model_step <- step(logit_model,direction = 'both',trace = T)
- # 将预测概率转换为0-1变量
- logit_model_pred=predict(logit_model, test_set,type='response')
- logit_model_pred1 <- round(logit_model_pred)
- # 输出混淆矩阵
- CrossTable(test_set$FRAUD_FLAG,logit_model_pred1,
- prop.chisq = F, prop.c = F,prop.r = T,
- dnn = c('ACTUAL FRAUD','PREDICTED FRAUD'))
- # 将预测目标转换为数值型,才能计算auc
- test_numeric <- as.numeric(test_set$FRAUD_FLAG)
- logit_model_pred1 <- as.numeric(logit_model_pred1)
- # 调用pROC包的roc函数
- fraud_log_auc <- roc(test_set$FRAUD_FLAG,logit_model_pred1)
- # 输出auc曲线面积
- print(fraud_log_auc)
- # 画auc图
- plot(fraud_log_auc,ylim=c(0,1),print.thres=TRUE,main=paste('AUC',round(fraud_log_auc$auc[[1]],2)))
- abline(h=1,col="blue",lwd=2)
- abline(h=0,col="red",lwd=2)
这里我们可以看到模型在测试集的auc面积,表现相当一般,可以说是比较差,才0.64。通常来说,auc面积=1,是非常完美的分类器,在0.9-1之间是很优秀的分类器,在0.8-0.9之间是一个比较良好的分类器,在0.7-0.8之间则是一般的的分类器,0.6-0.7之间则需要谨慎使用了,0.6以下就是非常坏的分类器了,基本上跟随机猜没多少区别。
随机森林
- # -------------------------------- 一般randomForest --------------------------------------
- # smote抽样
- # smote重采样,k表示少数类产生新样本的最近邻参数,默认是5,
- # perc.over表示对少数类样本过采样产生新样本的百分比,比如perc.over=80,在少数类样本=100的情况下,
- # 产生80个少数类样本,总计180个少数类样本
- # perc.under表示对多数类样本欠采样产生新样本的百分比,比如perc.under=300,在少数类样本=100,且perc.over=200的情况下,
- # 将从多数类样本中抽取100*200%*300%=600个多数类的样本,总计600个多数类样本
- set.seed(500)
- train_smote_set <- SMOTE(FRAUD_FLAG ~ ., data=train_set, k=10, perc.over = 100, perc.under = 130)
- table(train_smote_set$FRAUD_FLAG)
- prop.table(table(train_smote_set$FRAUD_FLAG))
- # 设置随机数种子,可重复
- set.seed(300)
- # formula目标变量和自变量方程
- # data是训练集
- # ntree树的数目,默认是500
- # mtry每次划分中随机选择的特征数,默认是变量数的平方根
- fraud_model <- randomForest(formula=FRAUD_FLAG ~.,
- data=train_smote_set,
- ntree=100,
- mtry=sqrt(ncol(train_smote_set)),
- maxnotes=10)
- fraud_model
- # Call:
- # randomForest(formula = FRAUD_FLAG ~ ., data = train_smote_set, ntree = 100, mtry = sqrt(ncol(train_smote_set)), maxnotes = 10)
- # Type of random forest: classification
- # Number of trees: 100
- # No. of variables tried at each split: 6
- #
- # OOB estimate of error rate: 21.3%
- # Confusion matrix:
- # 0 1 class.error
- # 0 536 246 0.314578
- # 1 177 1027 0.147010
- # fraud_model 训练模型
- # test_set测试集,必须与训练集一致
- # type可以是"response","prob","vote",分别表示输出预测向量是预测类别、预测概率或投票矩阵
- fraud_pred <- predict(fraud_model, test_set, type='response')
- # 输出混淆矩阵
- CrossTable(test_set$FRAUD_FLAG,fraud_pred,
- prop.chisq = F, prop.c = F,prop.r = T,
- dnn = c('ACTUAL FRAUD','PREDICTED FRAUD'))
- # 将预测目标转换为数值型,才能计算auc
- test_numeric <- as.numeric(test_set$FRAUD_FLAG)
- fraud_pred <- as.numeric(fraud_pred)
- # 调用pROC包的roc函数
尽管随机森林的表现依然不能让人满意,但至少比逻辑回归有所提升。
今天先写到这里,剩下的找时间再写~~~



雷达卡





京公网安备 11010802022788号







