python风控工具_Toad | Pyhon评分卡工具轻松实现风控模型开发
前言
介绍一个超级、超级、超级强大的评分卡模型开发库 。很多从业者都知道信贷风控界有一个库叫做Scorecardpy。作者是谢士晨博士。今天为读者介绍另一个同样用于开发评分卡的库,名为toad。
⭐️toad是由厚本金融风控团队内部孵化,后开源并坚持维护的标准化评分卡库。其功能全面、性能稳健、运行速度快、问题反馈后维护迅速、深受同行喜爱。如果有些小伙伴没有一些标准化的信用评分开发工具或者企业级的定制化脚本,toad应该会极大的节省大家的时间。
本文以一个不能分享的数据为例,演示一下toad包的功能,同时为读者讲解一下评分卡的构建方法。没有真实数据又对此感兴趣的胖友,其实可以随便从网上找个二分类项目,或者使用一些风控竞赛的开源数据。
正文
⭐️首先加载本文所需的库。
import pandas as pd
from sklearn.metrics import roc_auc_score,roc_curve,auc
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV as gscv
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
import glob
import math
import xgboost as xgb
import toad
⭐️加载数据。
#加载数据path = "D:/风控模型/data/"
data_all = pd.read_csv(path+"data.txt",engine='python',index_col=False)
data_all_woe = pd.read_csv(path+"ccard_all_woe.txt",engine='python',index_col=False)
#指定不参与训练列名
ex_lis = ['uid','obs_mth','ovd_dt','samp_type','weight',
'af30_status','submit_time','bad_ind']
#参与训练列名
ft_lis = list(data_all.columns)
for i in ex_lis:
ft_lis.remove(i)
⭐️划分训练集与测试集。
#训练集与跨时间验证集合
dev = data_all[(data_all['samp_type'] == 'dev') |
(data_all['samp_type'] == 'val') |
(data_all['samp_type'] == 'off1') ]
off = data_all[data_all['samp_type'] == 'off2']
⭐️EDA,探索性数据分析 同时处理数值型和字符型。
a = toad.detector.detect(data_all)
a.head(8)
⭐️特征筛选empty:缺失率上限
iv:信息量
corr:相关系数大于阈值,则删除IV小的特征
return_drop:返回删除特征
exclude:不参与筛选的变量名
dev_slct1, drop_lst= toad.selection.select(dev,dev['bad_ind'], empty = 0.7,
iv = 0.02, corr = 0.7, return_drop=True, exclude=ex_lis)
print("keep:",dev_slct1.shape[1],
"drop empty:",len(drop_lst['empty']),
"drop iv:",len(drop_lst['iv']),
"drop corr:",len(drop_lst['corr']))
keep: 584
drop empty: 637
drop iv: 1961
drop corr: 2043
dev_slct2, drop_lst= toad.selection.select(dev_slct1,dev_slct1['bad_ind'], empty = 0.6,
iv = 0.02, corr = 0.7, return_drop=True, exclude=ex_lis)
print("keep:",dev_slct2.shape[1],
"drop empty:",len(drop_lst['empty']),
"drop iv:",len(drop_lst['iv']),
"drop corr:",len(drop_lst['corr']))
keep: 560
drop empty: 24
drop iv: 0
drop corr: 0
分箱,先找到分箱的阈值
分箱阈值的方法(method) 包括:'chi','dt','quantile','step','kmeans'
然后利用分箱阈值进行粗分箱。
#得到切分节点
combiner = toad.transform.Combiner()
combiner.fit(dev_slct2,dev_slct2['bad_ind'],method='chi',min_samples = 0.05,
exclude=ex_lis)
#导出箱的节点
bins = combiner.export()
#根据节点实施分箱
dev_slct3 = combiner.transform(dev_slct2)
off3 = combiner.transform(off[dev_slct2.columns])
#分箱后通过画图观察
from toad.plot import bin_plot,badrate_plot
bin_plot(dev_slct3,x='p_ovpromise_6mth',target='bad_ind')
bin_plot(off3,x='p_ovpromise_6mth',target='bad_ind')
⭐️后2箱不单调。
#查看单箱节点
bins['p_ovpromise_6mth']
[0.0, 24.0, 60.0, 100.0]
⭐️合并最后两箱。
adj_bin = {'p_ovpromise_6mth': [0.0, 24.0, 60.0]}
combiner.set_rules(adj_bin)
dev_slct3 = combiner.transform(dev_slct2)
off3 = combiner.transform(off[dev_slct2.columns])
bin_plot(dev_slct3,x='p_ovpromise_6mth',target='bad_ind')
bin_plot(off3,x='p_ovpromise_6mth',target='bad_ind')
⭐️对比不同数据集上特征的badrate图是否有交叉。
data = pd.concat([dev_slct3,off3],join='inner')
badrate_plot(data, x='samp_type', target='bad_ind', by='p_ovpromise_6mth')
⭐️没有交叉,因此该特征的分组不需要再进行合并。篇幅有限,不对所有特征的精细化调整做展示。接下来进行WOE映射。
t=toad.transform.WOETransformer()
dev_slct2_woe = t.fit_transform(dev_slct3,dev_slct3['bad_ind'], exclude=ex_lis)
off_woe = t.transform(off3[dev_slct3.columns])
data = pd.concat([dev_slct2_woe,off_woe])
⭐️通过稳定性筛选特征。计算训练集与跨时间验证集的PSI。删除PSI大于0.05的特征。
psi_df = toad.metrics.PSI(dev_slct2_woe, off_woe).sort_values(0)
psi_df = psi_df.reset_index()
psi_df = psi_df.rename(columns = {'index' : 'feature',0:'psi'})
psi005 = list(psi_df[psi_df.psi<0.05].feature)
for i in ex_lis:
if i in psi005:
pass
else:
psi005.append(i)
data = data[psi005]
dev_woe_psi = dev_slct2_woe[psi005]
off_woe_psi = off_woe[psi005]
print(data.shape)
(41199, 476)
⭐️由于分箱后变量之间的共线性会变强,通过相关性再次筛选特征。
dev_woe_psi2, drop_lst= toad.selection.select(dev_woe_psi,dev_woe_psi['bad_ind'], empty = 0.6,
iv = 0.02, corr = 0.5, return_drop=True, exclude=ex_lis)
print("keep:",dev_woe_psi2.shape[1],
"drop empty:",len(drop_lst['empty']),
"drop iv:",len(drop_lst['iv']),
"drop corr:",len(drop_lst['corr']))
keep: 85
drop empty: 0
drop iv: 56
drop corr: 335
⭐️接下来通过逐步回归进行最终的特征筛选。检验方法(criterion):'aic'
'bic'
⭐️检验模型(estimator):'ols': LinearRegression,
'lr': LogisticRegression,
'lasso': Lasso,
'ridge': Ridge,
dev_woe_psi_stp = toad.selection.stepwise(dev_woe_psi2,
dev_woe_psi2['bad_ind'],
exclude = ex_lis,
direction = 'both',
criterion = 'aic',
estimator = 'ols',
intercept = False)
off_woe_psi_stp = off_woe_psi[dev_woe_psi_stp.columns]
data = pd.concat([dev_woe_psi_stp,off_woe_psi_stp])
data.shape
(41199, 33)
⭐️接下来定义双向逻辑回归和检验模型XGBoost。
#定义逻辑回归
def lr_model(x,y,offx,offy,C):
model = LogisticRegression(C=C,class_weight='balanced')
model.fit(x,y)
y_pred = model.predict_proba(x)[:,1]
fpr_dev,tpr_dev,_ = roc_curve(y,y_pred)
train_ks = abs(fpr_dev - tpr_dev).max()
print('train_ks : ',train_ks)
y_pred = model.predict_proba(offx)[:,1]
fpr_off,tpr_off,_ = roc_curve(offy,y_pred)
off_ks = abs(fpr_off - tpr_off).max()
print('off_ks : ',off_ks)
from matplotlib import pyplot as plt
plt.plot(fpr_dev,tpr_dev,label = 'train')
plt.plot(fpr_off,tpr_off,label = 'off')
plt.plot([0,1],[0,1],'k--')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC Curve')
plt.legend(loc = 'best')
plt.show()
#定义xgboost辅助判断盘牙鞥特征交叉是否有必要
def xgb_model(x,y,offx,offy):
model = xgb.XGBClassifier(learning_rate=0.05,
n_estimators=400,
max_depth=3,
class_weight='balanced',
min_child_weight=1,
subsample=1,
objective="binary:logistic",
nthread=-1,
scale_pos_weight=1,
random_state=1,
n_jobs=-1,
reg_lambda=300)
model.fit(x,y)
print('>>>>>>>>>')
y_pred = model.predict_proba(x)[:,1]
fpr_dev,tpr_dev,_ = roc_curve(y,y_pred)
train_ks = abs(fpr_dev - tpr_dev).max()
print('train_ks : ',train_ks)
y_pred = model.predict_proba(offx)[:,1]
fpr_off,tpr_off,_ = roc_curve(offy,y_pred)
off_ks = abs(fpr_off - tpr_off).max()
print('off_ks : ',off_ks)
from matplotlib import pyplot as plt
plt.plot(fpr_dev,tpr_dev,label = 'train')
plt.plot(fpr_off,tpr_off,label = 'off')
plt.plot([0,1],[0,1],'k--')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC Curve')
plt.legend(loc = 'best')
plt.show()
#模型训练
def c_train(data,dep='bg_result_compensate',exclude=None):
from sklearn.preprocessing import StandardScaler
std_scaler = StandardScaler()
#变量名
lis = list(data.columns)
for i in exclude:
lis.remove(i)
data[lis] = std_scaler.fit_transform(data[lis])
devv = data[(data['samp_type']=='dev') | (data['samp_type']=='val')]
offf = data[(data['samp_type']=='off1') | (data['samp_type']=='off2') ]
x,y = devv[lis],devv[dep]
offx,offy = offf[lis],offf[dep]
#逻辑回归正向
lr_model(x,y,offx,offy,0.1)
#逻辑回归反向
lr_model(offx,offy,x,y,0.1)
#XGBoost正向
xgb_model(x,y,offx,offy)
#XGBoost反向
xgb_model(offx,offy,x,y)
⭐️在特征精细化分箱后,xgboost模型的KS明显高于LR,则特征交叉是有必要的。需要返回特征工程过程进行特征交叉衍生。两模型KS接近代表特征交叉对模型没有明显提升。反向模型KS代表模型最高可能达到的结果。如果反向训练集效果较差,说明跨时间验证集本身分布较为特殊,应当重新划分数据。
c_train(data,dep='bad_ind',exclude=ex_lis)
⭐️评分卡模型训练。
#模型训练
dep = 'bad_ind'
lis = list(data.columns)
for i in ex_lis:
lis.remove(i)
devv = data[(data['samp_type']=='dev') | (data['samp_type']=='val')]
offf = data[(data['samp_type']=='off1') | (data['samp_type']=='off2') ]
x,y = devv[lis],devv[dep]
offx,offy = offf[lis],offf[dep]
lr = LogisticRegression()
lr.fit(x,y)
⭐️分别计算:F1分数 KS值 AUC值。
from toad.metrics import KS, F1, AUC
prob_dev = lr.predict_proba(x)[:,1]
print('训练集')
print('F1:', F1(prob_dev,y))
print('KS:', KS(prob_dev,y))
print('AUC:', AUC(prob_dev,y))
prob_off = lr.predict_proba(offx)[:,1]
print('跨时间')
print('F1:', F1(prob_off,offy))
print('KS:', KS(prob_off,offy))
print('AUC:', AUC(prob_off,offy))
⭐️训练集
F1: 0.30815569972196477
KS: 0.2819389063516508
AUC: 0.6908879633467695
⭐️跨时间
F1: 0.2848354792560801
KS: 0.23181102640650808
AUC: 0.6522823050763138
⭐️计算模型PSI和变量PSI,两个角度衡量稳定性。
print('模型PSI:',toad.metrics.PSI(prob_dev,prob_off))
print('特征PSI:','\n',toad.metrics.PSI(x,offx).sort_values(0))
模型PSI: 0.022260098554531284
特征PSI:
⭐️生产模型KS报告。
off_bucket = toad.metrics.KS_bucket(prob_off,offy,bucket=10,method='quantile')
off_bucket
⭐️生产评分卡。支持传入所有的模型参数,以及Fico分数校准的基础分与pdo(point of double odds),我一直管pdo叫步长...orz。
from toad.scorecard import ScoreCard
card = ScoreCard(combiner = combiner, transer = t,class_weight = 'balanced',C=0.1,base_score = 600,base_odds = 35 ,pdo = 60,rate = 2)
card.fit(x,y)
final_card = card.export(to_frame = True)
final_card.head(8)
github主页:amphibian-dev/toadgithub.com
文档:Welcome to toad’s documentation!toad.readthedocs.io
演示:Basic Tutorial for Toadtoad.readthedocs.io
whl下载地址:Links for toadpypi.org
最后,TOAD是个好开源工具,希望能被大家看见。
感谢阅读 。
python风控工具_Toad | Pyhon评分卡工具轻松实现风控模型开发相关推荐
- DataScience:风控场景之金融评分卡模型的构建(逻辑回归)开发(转评分卡)、使用过程(线上实现)之详细攻略
DataScience:风控场景之金融评分卡模型的构建(逻辑回归)&开发(转评分卡).使用过程(线上实现)之详细攻略 目录 风控场景之金融评分卡模型的构建(逻辑回归)&开发(转评分卡) ...
- DataScience:风控场景之金融评分卡模型的简介、构建(逻辑回归)开发(转评分卡)、使用过程(线上实现)之详细攻略
DataScience:风控场景之金融评分卡模型的简介.构建(逻辑回归)&开发(转评分卡).使用过程(线上实现)之详细攻略 目录 逻辑回归之金融评分卡模型的简介.构建.开发.使用过程 1.金融 ...
- ML之FE:风控场景之金融评分卡模型之利用LoR模型权重变量系数正负符号结合p-value/P值大小实现变量筛选
ML之FE:风控场景之金融评分卡模型之利用LoR模型权重变量系数正负符号结合p-value/P值大小实现变量筛选 目录 利用LoR模型权重变量系数正负符号结合p-value/P值大小实现变量筛选
- DataScience:风控场景之金融评分卡模型的简介、构建(逻辑回归)开发(转评分卡)、使用过程(线上实现)、完整流程之详细攻略
DataScience:风控场景之金融评分卡模型的简介.构建(逻辑回归)&开发(转评分卡).使用过程(线上实现).完整流程之详细攻略 目录 逻辑回归之金融评分卡模型的简介.构建.开发.使用过程 ...
- DataScience:基于GiveMeSomeCredit数据集利用特征工程处理、逻辑回归LoR算法实现构建风控中的金融评分卡模型
DataScience:基于GiveMeSomeCredit数据集利用特征工程处理.逻辑回归LoR算法实现构建风控中的金融评分卡模型 目录 基于GiveMeSomeCredit数据集利用特征工程处理. ...
- (信贷风控九)行为评分卡模型python实现
python信用评分卡建模(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_ca ...
- (信贷风控七)申请评分卡模型Python实现(图文+代码实现)
(七)申请评分卡模型Python实现(图文+代码实现) 贷前准入环节流程图大致如下 为什么需要建立评分卡? 所有的模型一定是服务于业务的,那么业务上到底出现了什么问题,需要用到评分卡模型去解决呢?我们 ...
- (信贷风控九)行为评分卡模型python实现(详细代码+注释+讲解)
(九)行为评分卡模型python实现(详细代码+注释+讲解) 浅谈行为评分卡 我们知道行为评分卡只要用在信贷的贷中环节,贷中指的是贷款发放之后到期之前的时间段,其实行为评分卡和申请评分卡在实现上没有太 ...
- python风险评分卡系统_智能风控:Python金融风险管理与评分卡建模(梅子行毛鑫宇著)...
推荐序 前言 第1章 信用管理基础 /1 1.1 信用与管理 /2 1.2 风控术语解读 /3 1.2.1 信贷基础指标 /4 1.2.2 信贷风险指标 /5 1.3 企业信贷风控架构 /7 1.4 ...
最新文章
- Vux+Cordova打包的安卓App实现微信分享朋友和朋友圈
- mysql 从库_mysql数据库主从配置
- OpenGL 入门第一课 视窗以及三角形
- 安卓开发——基于ViewPager的图片轮播
- java 注解: Annotation
- php 未定义数组索引_如何删除PHP数组元素键值并重新排序
- 结合地理信息系统开发的项目
- 机器学习基础:模糊C均值聚类(Machine Learning Fundamentals: Fuzzy C-Means )Python实现
- python爬虫+selenium模拟点击+网页内容需要点击打开
- 配眼镜走过的那些坑。
- 写代码的时候图片显示不出来怎么办?
- 百度bae专业版svn提交问题
- vue Elementui 引入第三方icon(阿里矢量库)
- 小米机器人虚拟墙设置_大家都有就它独缺,姗姗来迟的虚拟墙方案终更新,小米扫地机器人...
- 涪陵创新计算机学校2015元旦晚会,涓涓之情助力成才丨重庆市涪陵创新计算机学校助推纳雍脱贫...
- 团队项目开发流程总结
- python解压 tar.gz文件
- 《HeadFirst设计模式》读书笔记-第9章v1-迭代器模式
- 类ExampleA继承Exception,类ExampleB继承ExampleA。 有如下代码片断:
- 一个文件的开源项目,开启你的开源之旅
热门文章
- cocos2d-x 欢乐捕鱼游戏总结
- 不可逆调速matlab,双闭环不可逆直流调速系统课程设计(matlab仿真设计).pdf
- arcgis属性表选择两个条件_arcgis中给属性表字段按条件批量赋值
- C语言数据结构-数组广义表-十字链表-实现十字链表的初始化操作-实现十字链表的删除操作
- 产品演示需要准备_技术演示:为绝对混乱做好准备
- c语言fmin最小公倍数,已知函式f(x)=2分之1cos的平方x加2分之根号3sinxcosx加1,X属于R 。求在[π/12,π/4]上的最大最小值...
- java 连接solrcloud_Java操作SolrCloud
- 全面认知 Google Earth免费版与收费版差异
- Android应用如何跳转到应用市场详情页面
- 黑鲨5Pro国际版发布时间曝光 将于6月8日发布