数据分析项目分析之风控建模!!
风控建模概述
学习目标
知道信贷审批业务的基本流程
知道ABC评分卡是什么,有什么区别
知道风控建模的流程
掌握评分卡模型正负样本定义方法
知道如何构建特征,如何评估特征
1 互联网金融风控体系介绍
信贷审批业务基本流程
四要素认证:银行卡持有人的姓名、身份证号、银行卡号、手机号
互联网金融风控体系主要由三大部分组成:
用户数据:用户基本信息、用户行为信息、用户授权信息、外部接入信息。
数据采集会涉及到埋点和爬虫技术,基本上业内的数据都大同小异。
免费的运营商数据
安卓可爬的手机内部信息(app名称,手机设备信息,部分app内容信息)
收费的征信数据、各种信息校验、外部黑名单之类的
特定场景的现金贷和消费金融会有自有的数据可供使用
比如阿里京东自己的电商数据
滴滴的司机数据、顺丰中通的快递数据
用户基本信息(联系人,通讯录,学历...)
用户行为信息(操作APP时的行为,注册,点击位置...)
用户授权信息(运营商,学信网,设备IMEI....)
外部接入信息(P2P信贷,其它金融机构如芝麻信用分...)
策略体系:反欺诈规则、准入规则、运营商规则、风险名单、网贷规则
收集来用户的信息之后,把用户信息输入到策略引擎
欺诈规则
准入规则(年龄,地域,通讯录,行为规则)
运营商规则(通话规则)
风险名单(黑名单,失信名单,法院名单)
网贷(多头,白户...)
机器学习模型:欺诈检测模型、准入模型、授信模型、风险定价、额度管理、流失预警、失联修复。
贷前准入 贷中管理 贷后催收 信用 申请评分卡 行为评分卡 催收评分卡 反欺诈 申请反欺诈 交易反欺诈 运营 用户响应模型 用户流失模型、用户分群、用户画像 失联修复 其他 套现识别、洗钱识别
2 风控建模流程
2.1 评分卡简介
风控模型其中包含了A/B/C卡。模型可以采用相同算法,一般以逾期天数来区分正负样本,也就是目标值Y的取值(0或1)
贷前 申请评分卡 Application score card
贷中 行为评分卡 Behavior score card
贷后 催收评分卡 Collection score card
C卡因为用途不同Y的取值可能有区别
公司有内催,有外催。外催回款率低,单价贵
可以根据是否被内催催回来定义C卡的Y。
2.2 机器学习模型的完整工程流程
准备
明确需求
模型设计
业务抽象成分类/回归问题
定义标签(目标值)
样本设计
特征工程
数据处理,选取合适的样本,并匹配出全部的信息作为基础特征
特征构建
特征评估
模型
模型训练
模型评价
模型调优
上线运营
模型交付
模型部署
模型监控
2.3 项目准备期
项目准备期 → 特征工程 → 模型构建 → 上线运营
明确需求
目标人群:新客,优质老客,逾期老客
给与产品:额度,利率
市场策略:冷启动,开拓市场,改善营收
使用时限:紧急使用,长期部署
举例
业务需要针对全新客户开放一个小额现金贷产品,抢占新市场
针对高风险薄数据新客的申请评分卡
模型设计
业务抽象成分类/回归问题
风控场景下问题通常都可以转化为二分类问题:
信用评分模型期望用于预测一个用户是否会逾期,逾期用户1
营销模型期望用于预测一个用户被营销后是否会来贷款,没贷用户1
失联模型期望用于预测一个用户是否会失联,失联用户1
风控业务中,只有欺诈检测不是二分类问题。因为样本数量不足,可能是一个无监督学习模型
模型算法
规则模型
逻辑回归
集成学习
融合模型
模型输入:
数据源
时间跨度
Y标签定义
在构建信贷评分模型时,原始数据中只有每个人的当前逾期情况,没有负样本,负样本需要人为构建
通常选一个截断点(阈值),当逾期超过某个阈值时,就认定该样本是一个负样本,未来不会还钱
比如逾期15天为正负样本的标记阈值,Y = 1的客户均是逾期超过15天的客户
逾期>15天时 Y = 1,那么Y=0如何定义
只会将按时还款和逾期较少的那一部分人标记为0。如:将逾期<5天和没有逾期的人作为正样本
逾期5~15天的数据(灰样本)会从样本中去掉,去掉“灰样本”,会使样本分布更趋于二项分布,对模型学习更加有利。
“灰样本”通常放入测试集中,用于确保模型在训练结束后,对该部分样本也有区分能力。
样本选取
代表性:样本必须能够充分代表总体。如消费贷客群数据不能直接用到小额现金贷场景
充分性:样本集的数量必须满足一定要求。评分卡建模通常要求正负样本的数量都不少于1500个。随着样本量的增加,模型的效果会显著提升
时效性:在满足样本量充足的情况下,通常要求样本的观测期与实际应用时间节点越接近越好。如银行等客群稳定的场景,观察期可长达一年半至两年。
排除性(Exclusion):虽然建模样本需要具有代表整体的能力,但某些法律规定不满足特定场景贷款需求的用户不应作为样本,如对行为评分卡用户、无还款表现或欺诈用户均不应放入当前样本集。
评分卡建模通常要求正负样本的数量>=1500,但当总样本量超过50000个时,许多模型的效果不再随着样本量的增加而有显著提升,而且数据处理与模型训练过程通常较为耗时。
如果样本量过大,会为训练过程增加不必要的负担,需要对样本做欠采样(Subsampling)处理。由于负样本通常较少,因此通常只针对正样本进行欠采样。常见的欠采样方法分为:
随机欠采样:直接将正样本欠采样至预期比例。
分层抽样:保证抽样后,开发样本、验证样本与时间外样本中的正负样本比例相同。
等比例抽样:将正样本欠采样至正负样本比例相等,即正样本量与负样本量之比为1:1。 需要注意的是,采样后需要为正样本添加权重。如正样本采样为原来的1/4,则为采样后的正样本增加权重为4,负样本权重保持为1。因为在后续计算模型检验指标及预期坏账时,需要将权重带入计算逻辑,才可以还原真实情况下的指标估计值,否则预期结果与实际部署后的结果会有明显偏差。
而当负样本较少的时候,需要进行代价敏感加权或过采样(Oversampling)处理
观察期和表现期
观察期是指用户申请信贷产品前的时间段
表现期是定义好坏标签的时间窗口,如果在该时间窗口内触发坏定义就是坏样本,反之就是好样本。
举例: 要建立A卡模型, 观察期12个月,表现期3个月
用户贷款前12个月的历史行为表现作为变量,用于后续建模
如设定用户在到期3个月内未还款,即认为用户为负样本,则称表现期为3个月
训练数据测试数据划分
数据集在建模前需要划分为3个子集:
开发样本(Develop):开发样本与验证样本使用分层抽样划分,保证两个数据集中负样本占比相同
验证样本(Valuation): 开发样本与验证样本的比例为6:4
时间外样本(Out of Time,OOT): 通常使用整个建模样本中时间最近的数据, 用来验证模型对未来样本的预测能力,以及模型的跨时间稳定性。
举例:
申请评分卡 | 行为评分卡 | 催收评分卡 | |
---|---|---|---|
客群 | 新客 | 未逾期老客 | 逾期老客 |
观察期 | 申请时点前一年 | 当期某一日前一年 | 当期还款日前一年 |
表现期 | FPD30 | DPD60 | DPD1->DPD30 |
样本设计
选取客群:新客,未逾期老客,逾期老客
训练集 测试集 1月 2月 3月 4月 5月 6月 7月 8月 总# 100 200 300 400 500 600 700 800 坏# 3 6 6 8 15 12 14 24 坏% 3% 3% 2% 2% 3% 2% 2% 3% 客群描述:首单用户、内部数据丰富、剔除高危职业、收入范围在XXXX
客群标签:好: FPD<=5 坏: FPD>15, (5,15)灰样本,不参与训练,参与测试评估
2.4 特征工程
数据调研
明确对目标人群有哪些可用数据, 明确数据获取逻辑
明确数据的质量,覆盖度,稳定性
特征构建
误区:拿到数据之后,立即做特征
构建特征之前需要明确:
数据源对应的具体数据表,画出ER图
评估特征的样本集
B卡样本集不能包含逾期数据
C卡样本集不能包含按时还款的数据
特征框架,确保对数据使用维度进行了全面思考
确定思维框架, 与组内其它人讨论
明确数据源对应的具体数据表
明确数据是从哪里来的: (DE Data Engineer 数仓工程师)
数据分析师拿到的数据可能是:
数仓原始表
数仓重构表
数仓原始表和数仓重构表可能数据量有差异,因为更新时间不同!
尽量使用数仓工程师加工好的重构表,确保逻辑统一
实时预测要确保生产数据库和数据仓库数据一致 (很难)
画出类ER图 数据关系 一对一,一对多,多对多
-
写SQL查询时要从 用户列表出发, Join其它表
不能出现SELECT DISTINCT user_id FROM order_table
明确评估特征的样本集
新申请客户没有内部信贷数据
未逾期老客户当期没有逾期信息
逾期老客户和未逾期老客的还款数据一定差别很大
如何从原始数据中构建特征:指定特征框架,确保对数据使用维度进行了全面思考
每个属性都可以从R(Recency) F(Frequency) M(Monetary)三个维度思考,来构建特征
GPS 经纬度 R 最近GPS所在省市区, 申请时间GPS所在省市区 F GPS出现过的省市区,出现最多的省市区 M GPS出现最多的省市区的GDP,人口,坏账率, 该地区的其他统计信息 时间 R 最近一天/周/月的GPS数 F 过去N天/周/月的GPS的平均数, 早上/中午/晚上/上班日/周末GPS数 M None 地址 R 最近GPS距离家庭/工作地址距离 F 出现最多GPS距离家庭/工作地址距离 M GPS序列围绕家庭/工作地址的信息熵 补充 是否授权GPS,最近连续无GPS的天数 特征构建方法
用户静态信息特征:用户的姓名,性别,年龄
用户时间截面特征:
截面时点电商购物GMV
截面时点银行存款额
截面时点逾期最大天数
用户时间序列特征 用户过去一个月的GPS数据 用户过去六个月的银行流水 用户过去一年的逾期记录
特征评估
什么是好的特征
好的特征需要满足的条件: 评估指标 覆盖度高,很多用户都能使用 稳定,在后续较长时间可以持续使用 PSI (Population Stability Index) 区分度好,好坏用户的特征值差别大 IV (Information Value)
也可以用模型的评估指标来评估特征:单特征AUC, 单特征KS
可以拿效果最好的单特征的AUC,KS来估计模型的效果
特征评估报表
全量样本 带标签样本 特征名称 覆盖度 缺失率 零值率 AUC KS IV 全量样本—覆盖度:全量样本上,有多少用户有这个特征
全量样本:包含不带标签的样本
缺失率:带标签样本缺失率,与全量样本覆盖度作对比,看差距是不是很大,选择差距不大的特征
零值率:好多特征是计数特征,比如电商消费单数,通信录记录数,GPS数据,如零值太多,特征不好
剔除风险趋势不合逻辑的特征,用常识和业务逻辑去评估
2.5 模型构建
设计实验→模型训练→模型评估
设计实验
训练模型时有很多可能的因素会影响模型效果
我们需要通过设计实验去验证哪些因素是会提升模型效果的
模型评估
好的模型需要满足的条件: 稳定,在后续较长时间可以持续使用 PSI (Population Stability Index) 区分度好,好坏用户的信用分差别大 AUC, KS, GINI
报表一:区分度,抓坏人能力在不同分段的表现
分数段 总人数 坏人数 坏人率 KS [300, 550] [500, 600] [600, 700] [700, 750] [750, 800] [800, 850] [850, 950] 报表二:跨时间稳定性
分数段 测试集1 测试集2 线上1期 线上2期 ... [300, 550] 10% 10% [500, 600] 20% 20% [600, 700] 20% 20% [700, 750] 25% 25% [750, 800] 20% 20% [800, 850] 4% 4% [850, 950] 1% 1% 决策点上比例 50% 50% 总用户数 3000 2000 平均分 730 725 PSI - 0.01
2.6 上线运营
模型交付→模型部署→模型监控
模型交付
交付流程:
1 提交特征和模型报表 2 离线结果质量复核 (无缺失,无重复,存储位置正确,文件名规范) 3 保存模型文件,确定版本号,提交时间 4 老大审批,通知业务方 5 线上部署,案例调研, 持续监控
特征报告
1 特征项目需求 2 特征项目任务列表 3 特征项目时间表 4 类ER图 5 样本设计 6 特征框架 7 每周开发进度和结果 8 每周讨论反馈和改进意见笔记 9 特征项目交付说明 10 特征项目总结
模型报告
1 模型项目需求
2 模型项目任务列表
3 模型项目时间表
4 模型设计
5 样本设计
6 模型训练流程和实验设计
7 每周开发进度和结果
8 每周讨论反馈和改进意见笔记
9 模型项目交付说明
10 模型项目总结
模型部署
确保开发环境和生产环境一致性
使用PMML文件或Flask API进行部署
一定要做:对一批客户进行离线打分和线上打分,确保离线结果和线上结果一致
模型监控
特征监控:特征稳定性
模型监控:模型稳定性
3 业务规则挖掘
3.1 规则挖掘简介
两种常见的风险规避手段:
AI模型
规则
如何使用规则进行风控
使用一系列判断逻辑对客户群体进行区分,不同群体逾期风险有显著差别
举例:多头借贷数量是否超过一定数量
如果一条规则将用户划分到高风险组,则直接拒绝,如果划分到低风险组则进入到下一规则
规则 和 AI模型的优缺点
规则,可以快速使用,便于业务人员理解,但判断相对简单粗暴一些,单一维度不满足条件直接拒绝
AI模型,开发周期长,对比使用规则更复杂,但更灵活,用于对风控精度要求高的场景
可以通过AI模型辅助建立规则引擎,决策树很适合规则挖掘的场景
3.2 规则挖掘案例
案例背景
某互联网公司拥有多个业务板块,每个板块下都有专门的贷款产品
外卖平台业务的骑手可以向平台申请“骑手贷”
电商平台业务的商户可以申请“网商贷”
网约车业务的司机可以向平台申请“司机贷”
公司有多个类似的场景,共用相同的规则引擎及申请评分卡,贷款人都是该公司的兼职人员
近期发现,“司机贷”的逾期率较高
整个金融板块30天逾期率为1.5%
“司机贷”产品的30天逾期达到了5%
期望解决方案
现有的风控架构趋于稳定
希望快速开发快速上线,解决问题
尽量不使用复杂的方法
考虑使用现有数据挖掘出合适的业务规则
数据字典
变量类型 基础变量名 释义 数值型 oil_amount 加油升数 discount_amount 折扣金额 sale_amount 促销金额 amount 总金额 pay_amount 实际支付金额 coupon_amount 优惠券金额 payment_coupon_amount 支付优惠券金额 分类型 channel_code 渠道 oil_code 油品品类(规格) scene 场景 source_app 来源端口(1货车帮、2微信) call_source 订单来源(1:中化扫描枪 2:pos 3:找油网 4:油掌柜5:司机自助加油 6 油站线) class_new 用户评级 日期时间 create_dt 账户创建时间 oil_actv_dt 放款时间 标签 bad_ind 正负样本标记 加载数据
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">import</span> <span style="color:#000000">pandas</span> <span style="color:#770088">as</span> <span style="color:#000000">pd</span>
<span style="color:#770088">import</span> <span style="color:#000000">numpy</span> <span style="color:#770088">as</span> <span style="color:#000000">np</span>
<span style="color:#000000">data</span> = <span style="color:#000000">pd</span>.<span style="color:#000000">read_excel</span>(<span style="color:#aa1111">'data/rule_data.xlsx'</span>)
<span style="color:#000000">data</span>.<span style="color:#000000">head</span>()</span></span>
显示结果:
uid oil_actv_dt create_dt total_oil_cnt pay_amount_total class_new bad_ind oil_amount discount_amount sale_amount amount pay_amount coupon_amount payment_coupon_amount channel_code oil_code scene source_app call_source 0 A8217710 2018-08-19 2018-08-17 275.0 48295495.4 B 0 3308.56 1760081.0 1796001.0 1731081.0 8655401.0 1.0 1.0 1 3 2 0 3 1 A8217710 2018-08-19 2018-08-16 275.0 48295495.4 B 0 4674.68 2487045.0 2537801.0 2437845.0 12189221.0 1.0 1.0 1 3 2 0 3 2 A8217710 2018-08-19 2018-08-15 275.0 48295495.4 B 0 1873.06 977845.0 997801.0 961845.0 4809221.0 1.0 1.0 1 2 2 0 3 3 A8217710 2018-08-19 2018-08-14 275.0 48295495.4 B 0 4837.78 2526441.0 2578001.0 2484441.0 12422201.0 1.0 1.0 1 2 2 0 3 4 A8217710 2018-08-19 2018-08-13 275.0 48295495.4 B 0 2586.38 1350441.0 1378001.0 1328441.0 6642201.0 1.0 1.0 1 2 2 0 3
查看class_new
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#000000">data</span>.<span style="color:#000000">class_new</span>.<span style="color:#000000">unique</span>()</span></span>
显示结果:
<span style="background-color:#f8f8f8"><span style="color:#000000">array</span>([<span style="color:#aa1111">'B'</span>, <span style="color:#aa1111">'E'</span>, <span style="color:#aa1111">'C'</span>, <span style="color:#aa1111">'A'</span>, <span style="color:#aa1111">'D'</span>, <span style="color:#aa1111">'F'</span>], <span style="color:#000000">dtype</span>=<span style="color:#3300aa">object</span>)</span>
原始数据的特征太少,考虑在原始特征基础上衍生出一些新的特征来,将特征分成三类分别处理
数值类型变量:按照id分组后,采用多种方式聚合,衍生新特征
分类类型变量,按照id分组后,聚合查询条目数量,衍生新特征
其它:日期时间类型,是否违约(标签),用户评级等不做特征衍生处理
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#000000">org_list</span> = [<span style="color:#aa1111">'uid'</span>,<span style="color:#aa1111">'create_dt'</span>,<span style="color:#aa1111">'oil_actv_dt'</span>,<span style="color:#aa1111">'class_new'</span>,<span style="color:#aa1111">'bad_ind'</span>]
<span style="color:#000000">agg_list</span> = [<span style="color:#aa1111">'oil_amount'</span>,<span style="color:#aa1111">'discount_amount'</span>,<span style="color:#aa1111">'sale_amount'</span>,<span style="color:#aa1111">'amount'</span>,<span style="color:#aa1111">'pay_amount'</span>,<span style="color:#aa1111">'coupon_amount'</span>,<span style="color:#aa1111">'payment_coupon_amount'</span>]
<span style="color:#000000">count_list</span> = [<span style="color:#aa1111">'channel_code'</span>,<span style="color:#aa1111">'oil_code'</span>,<span style="color:#aa1111">'scene'</span>,<span style="color:#aa1111">'source_app'</span>,<span style="color:#aa1111">'call_source'</span>]</span></span>
创建数据副本,保留底表,并查看缺失情况
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#000000">df</span> = <span style="color:#000000">data</span>[<span style="color:#000000">org_list</span>].<span style="color:#000000">copy</span>()
<span style="color:#000000">df</span>[<span style="color:#000000">agg_list</span>] = <span style="color:#000000">data</span>[<span style="color:#000000">agg_list</span>].<span style="color:#000000">copy</span>()
<span style="color:#000000">df</span>[<span style="color:#000000">count_list</span>] = <span style="color:#000000">data</span>[<span style="color:#000000">count_list</span>].<span style="color:#000000">copy</span>()
<span style="color:#000000">df</span>.<span style="color:#000000">isna</span>().<span style="color:#000000">sum</span>()</span></span>
显示结果:
<span style="background-color:#f8f8f8">uid <span style="color:#116644">0</span> create_dt <span style="color:#116644">4944</span> oil_actv_dt <span style="color:#116644">0</span> class_new <span style="color:#116644">0</span> bad_ind <span style="color:#116644">0</span> oil_amount <span style="color:#116644">4944</span> discount_amount <span style="color:#116644">4944</span> sale_amount <span style="color:#116644">4944</span> amount <span style="color:#116644">4944</span> pay_amount <span style="color:#116644">4944</span> coupon_amount <span style="color:#116644">4944</span> payment_coupon_amount <span style="color:#116644">4946</span> channel_code <span style="color:#116644">0</span> oil_code <span style="color:#116644">0</span> scene <span style="color:#116644">0</span> source_app <span style="color:#116644">0</span> call_source <span style="color:#116644">0</span> dtype: int64</span>
查看数值型变量的分布情况
<span style="background-color:#f8f8f8"><span style="color:#333333">df.describe()</span></span>
显示结果:
bad_ind oil_amount discount_amount sale_amount amount pay_amount coupon_amount payment_coupon_amount channel_code oil_code scene source_app call_source count 50609.000000 45665.000000 4.566500e+04 4.566500e+04 4.566500e+04 4.566500e+04 45665.000000 45663.000000 50609.000000 50609.000000 50609.000000 50609.000000 50609.000000 mean 0.017764 425.376107 1.832017e+05 1.881283e+05 1.808673e+05 9.043344e+05 0.576853 149.395397 1.476378 1.617894 1.906519 0.306072 2.900729 std 0.132093 400.596244 2.007574e+05 2.048742e+05 1.977035e+05 9.885168e+05 0.494064 605.138823 1.511470 3.074166 0.367280 0.893682 0.726231 min 0.000000 1.000000 0.000000e+00 0.000000e+00 1.000000e+00 5.000000e+00 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 25% 0.000000 175.440000 6.039100e+04 6.200100e+04 5.976100e+04 2.988010e+05 0.000000 1.000000 1.000000 0.000000 2.000000 0.000000 3.000000 50% 0.000000 336.160000 1.229310e+05 1.279240e+05 1.209610e+05 6.048010e+05 1.000000 1.000000 1.000000 0.000000 2.000000 0.000000 3.000000 75% 0.000000 557.600000 2.399050e+05 2.454010e+05 2.360790e+05 1.180391e+06 1.000000 100.000000 1.000000 0.000000 2.000000 0.000000 3.000000 max 1.000000 7952.820000 3.916081e+06 3.996001e+06 3.851081e+06 1.925540e+07 1.000000 50000.000000 6.000000 9.000000 2.000000 3.000000 4.000000
缺失值填充
对creat_dt做补全,用oil_actv_dt来填补
截取申请时间和放款时间不超过6个月的数据(考虑数据时效性)
<span style="background-color:#f8f8f8"><span style="color:#333333"><span style="color:#770088">def</span> <span style="color:#0000ff">time_isna</span>(<span style="color:#000000">x</span>,<span style="color:#000000">y</span>):<span style="color:#770088">if</span> <span style="color:#3300aa">str</span>(<span style="color:#000000">x</span>) == <span style="color:#aa1111">'NaT'</span>:<span style="color:#000000">x</span> = <span style="color:#000000">y</span><span style="color:#770088">return</span> <span style="color:#000000">x</span>
<span style="color:#000000">df2</span> = <span style="color:#000000">df</span>.<span style="color:#000000">sort_values</span>([<span style="color:#aa1111">'uid'</span>,<span style="color:#aa1111">'create_dt'</span>],<span style="color:#000000">ascending</span> = <span style="color:#770088">False</span>)
<span style="color:#000000">df2</span>[<span style="color:#aa1111">'create_dt'</span>] = <span style="color:#000000">df2</span>.<span style="color:#000000">apply</span>(<span style="color:#770088">lambda</span> <span style="color:#000000">x</span>: <span style="color:#000000">time_isna</span>(<span style="color:#000000">x</span>.<span style="color:#000000">create_dt</span>,<span style="color:#000000">x</span>.<span style="color:#000000">oil_actv_dt</span>),<span style="color:#000000">axis</span> = <span style="color:#116644">1</span>)
<span style="color:#000000">df2</span>[<span style="color:#aa1111">'dtn'</span>] = (<span style="color:#000000">df2</span>.<span style="color:#000000">oil_actv_dt</span> <span style="color:#981a1a">-</span> <span style="color:#000000">df2</span>.<span style="color:#000000">create_dt</span>).<span style="color:#000000">apply</span>(<span style="color:#770088">lambda</span> <span style="color:#000000">x</span> :<span style="color:#000000">x</span>.<span style="color:#000000">days</span>)
<span style="color:#000000">df</span> = <span style="color:#000000">df2</span>[<span style="color:#000000">df2</span>[<span style="color:#aa1111">'dtn'</span>]<span style="color:#981a1a"><</span><span style="color:#116644">180</span>]
<span style="color:#000000">df</span>.<span style="color:#000000">head</span>()</span></span>
显示结果:
uid create_dt oil_actv_dt class_new bad_ind oil_amount discount_amount sale_amount amount pay_amount coupon_amount payment_coupon_amount channel_code oil_code scene source_app call_source dtn 50608 B96436391985035703 2018-10-08 2018-10-08 B 0 NaN NaN NaN NaN NaN NaN NaN 6 9 2 3 4 0 50607 B96436391984693397 2018-10-11 2018-10-11 E 0 NaN NaN NaN NaN NaN NaN NaN 6 9 2 3 4 0 50606 B96436391977217468 2018-10-17 2018-10-17 B 0 NaN NaN NaN NaN NaN NaN NaN 6 9 2 3 4 0 50605 B96436391976480892 2018-09-28 2018-09-28 B 0 NaN NaN NaN NaN NaN NaN NaN 6 9 2 3 4 0 50604 B96436391972106043 2018-10-19 2018-10-19 A 0 NaN NaN NaN NaN NaN NaN NaN 6 9 2 3 4 0
将用户按照id编号排序,并保留最近一次申请时间,确保每个用户有一条记录
<span style="background-color:#f8f8f8"><span style="color:#333333">base = df[org_list]
base['dtn'] = df['dtn']
base = base.sort_values(['uid','create_dt'],ascending = False)
base = base.drop_duplicates(['uid'],keep = 'first')
base.shape
</span></span>
显示结果:
(11099, 6)
特征衍生
对连续统计型变量进行函数聚合
方法包括对历史特征值计数、求历史特征值大于0的个数、求和、求均值、求最大/小值、求最小值、求方差、求极差等
<span style="background-color:#f8f8f8"><span style="color:#333333">gn = pd.DataFrame()
for i in agg_list:tp = df.groupby('uid').apply(lambda df:len(df[i])).reset_index()tp.columns = ['uid',i + '_cnt']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求历史特征值大于0的个数tp = df.groupby('uid').apply(lambda df:np.where(df[i]>0,1,0).sum()).reset_index()tp.columns = ['uid',i + '_num']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求和tp = df.groupby('uid').apply(lambda df:np.nansum(df[i])).reset_index()tp.columns = ['uid',i + '_tot']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求平均值tp = df.groupby('uid').apply(lambda df:np.nanmean(df[i])).reset_index()tp.columns = ['uid',i + '_avg']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求最大值tp = df.groupby('uid').apply(lambda df:np.nanmax(df[i])).reset_index()tp.columns = ['uid',i + '_max']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求最小值tp = df.groupby('uid').apply(lambda df:np.nanmin(df[i])).reset_index()tp.columns = ['uid',i + '_min']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求方差tp = df.groupby('uid').apply(lambda df:np.nanvar(df[i])).reset_index()tp.columns = ['uid',i + '_var']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')#求极差tp = df.groupby('uid').apply(lambda df:np.nanmax(df[i]) -np.nanmin(df[i]) ).reset_index()tp.columns = ['uid',i + '_ran']if gn.empty:gn = tpelse:gn = pd.merge(gn,tp,on = 'uid',how = 'left')
</span></span>
查看衍生结果
<span style="background-color:#f8f8f8"><span style="color:#333333">gn.columns
</span></span>
显示结果:
<span style="background-color:#f8f8f8">Index(['uid', 'oil_amount_cnt', 'oil_amount_num', 'oil_amount_tot','oil_amount_avg', 'oil_amount_max', 'oil_amount_min','oil_amount_var_x', 'oil_amount_var_y', 'discount_amount_cnt','discount_amount_num', 'discount_amount_tot', 'discount_amount_avg','discount_amount_max', 'discount_amount_min', 'discount_amount_var_x','discount_amount_var_y', 'sale_amount_cnt', 'sale_amount_num','sale_amount_tot', 'sale_amount_avg', 'sale_amount_max','sale_amount_min', 'sale_amount_var_x', 'sale_amount_var_y','amount_cnt', 'amount_num', 'amount_tot', 'amount_avg', 'amount_max','amount_min', 'amount_var_x', 'amount_var_y', 'pay_amount_cnt','pay_amount_num', 'pay_amount_tot', 'pay_amount_avg', 'pay_amount_max','pay_amount_min', 'pay_amount_var_x', 'pay_amount_var_y','coupon_amount_cnt', 'coupon_amount_num', 'coupon_amount_tot','coupon_amount_avg', 'coupon_amount_max', 'coupon_amount_min','coupon_amount_var_x', 'coupon_amount_var_y','payment_coupon_amount_cnt', 'payment_coupon_amount_num','payment_coupon_amount_tot', 'payment_coupon_amount_avg','payment_coupon_amount_max', 'payment_coupon_amount_min','payment_coupon_amount_var_x', 'payment_coupon_amount_var_y'],dtype='object') </span>
对dstc_lst变量求distinct个数
<span style="background-color:#f8f8f8"><span style="color:#333333">gc = pd.DataFrame()
for i in count_list:tp = df.groupby('uid').apply(lambda df: len(set(df[i]))).reset_index()tp.columns = ['uid',i + '_dstc']if gc.empty:gc = tpelse:gc = pd.merge(gc,tp,on = 'uid',how = 'left')
</span></span>
将变量组合在一起
<span style="background-color:#f8f8f8"><span style="color:#333333">fn = pd.merge(base,gn,on= 'uid')
fn = pd.merge(fn,gc,on= 'uid')
fn.shape
</span></span>
显示结果:
(11099, 67)
merge过程中可能会出现缺失情况,填充缺失值
<span style="background-color:#f8f8f8"><span style="color:#333333">fn = fn.fillna(0)
fn.head(100)
</span></span>
显示结果:
uid create_dt oil_actv_dt class_new bad_ind dtn oil_amount_cnt oil_amount_num oil_amount_tot oil_amount_avg ... payment_coupon_amount_max payment_coupon_amount_min payment_coupon_amount_var_x payment_coupon_amount_var_y payment_coupon_amount_var channel_code_dstc oil_code_dstc scene_dstc source_app_dstc call_source_dstc 0 B96436391985035703 2018-10-08 2018-10-08 B 0 0 1 0 0.00 0.00 ... 0.0 0.0 0.0 0.0 0.0 1 1 1 1 1 1 B96436391984693397 2018-10-11 2018-10-11 E 0 0 1 0 0.00 0.00 ... 0.0 0.0 0.0 0.0 0.0 1 1 1 1 1 2 B96436391977217468 2018-10-17 2018-10-17 B 0 0 1 0 0.00 0.00 ... 0.0 0.0 0.0 0.0 0.0 1 1 1 1 1 3 B96436391976480892 2018-09-28 2018-09-28 B 0 0 1 0 0.00 0.00 ... 0.0 0.0 0.0 0.0 0.0 1 1 1 1 1 100 rows × 74 columns
训练决策树模型
<span style="background-color:#f8f8f8"><span style="color:#333333">x = fn.drop(['uid','oil_actv_dt','create_dt','bad_ind','class_new'],axis = 1)
y = fn.bad_ind.copy()
from sklearn import tree
dtree = tree.DecisionTreeRegressor(max_depth = 2,min_samples_leaf = 500,min_samples_split = 5000)
dtree = dtree.fit(x,y)
</span></span>
输出决策树图像,并作出决策
需要安装 Graphviz 软件,下载地址Download | Graphviz
下载之后需要配置环境变量,把安装目录如‘C:/Program Files (x86)/Graphviz2.38/bin/’
需要安装两个python库
pip install graphviz
pip install pydotplus
<span style="background-color:#f8f8f8"><span style="color:#333333">import pydotplus
from IPython.display import Image
from six import StringIO
import os
os.environ["PATH"] += os.pathsep + 'C:/Program Files (x86)/Graphviz2.38/bin/'
#with open("dt.dot", "w") as f:
# tree.export_graphviz(dtree, out_file=f)
dot_data = StringIO()
tree.export_graphviz(dtree, out_file=dot_data,feature_names=x.columns,class_names=['bad_ind'],filled=True, rounded=True,special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
Image(graph.create_png())
</span></span>
显示结果:
利用结果划分用户
<span style="background-color:#f8f8f8"><span style="color:#333333">group_1 = fn.loc[(fn.amount_tot>48077.5)&(fn.amount_cnt>3.5)].copy()
group_1['level'] = 'past_A'
group_2 = fn.loc[(fn.amount_tot>48077.5)&(fn.amount_cnt<=3.5)].copy()
group_2['level'] = 'past_B'
group_3 = fn.loc[fn.amount_tot<=48077.5].copy()
group_3['level'] = 'past_C'
</span></span>
如果拒绝past_C类客户,则可以使整体负样本占比下降至0.021
如果将past_B也拒绝掉,则可以使整体负样本占比下降至0.012
至于实际对past_A、past_B、past_C采取何种策略,要根据利率来做线性规划,从而实现风险定价
小结
信贷审批业务的基本流程
申请→ 审批 → 放款→ 还款 → 再次申请 → 复贷审批
↙↘ ↘ ↙↘
规则 模型 逾期→催收 规则 模型
ABC评分卡
A申请,B行为,C催收
针对客群不同,可用数据不同,Y定义不同
风控建模的流程
项目准备 → 特征工程 → 建模 → 上线运营
明确需求 数据处理 模型训练 模型交付
模型设计 特征构建 模型评价 模型部署
样本设计 特征评估 模型调优 模型监控
评分卡模型正负样本定义方法
一般习惯Y = 1 为坏用户(违约)
Y=1 选取:用DPD30,DPD15.... (根据具体业务情况)做截断,删除灰色部分用户
未逾期,或逾期5天以内作为好用户
如何构建特征,如何评估特征
特征构建
画出E-R图,知道数据在哪些表中保存,表与表之间关系
知道那些数据可以用
单个特征从三个维度RFM考虑生成新特征
用户时间截面特征
用户时间序列特征
特征评估
覆盖度
稳定性 PSI
区分度 IV 单特征AUC/KS
规则引擎如何工作
使用一系列判断逻辑对客户群体进行区分,不同群体逾期风险有显著差别
可以通过机器学习模型,辅助规则挖掘
数据分析项目分析之风控建模!!相关推荐
- tvpvar模型的建模步骤_风控建模六部曲
这期开始咱们聊聊风控建模的事情.在借贷这个场景下,建立风控模型的目的大都是为了预测某个客户未来逾期的概率.主要的逻辑就是根据过去预测未来,逾期客户的行为是相似的.先收集历史上已经逾期到一定阶段的客户, ...
- 风控建模 python 知乎_风控建模基本要求及面试问题小结
(本文是写给风控入门同学的建议!) 一 准备工作 根据核心职业CD法则,找工作前,你得先清楚自己有什么,自己要什么:面试单位有什么,面试单位要什么.就笔者近一年的学习及工作经验总结而言: 科班同学,最 ...
- python风控建模实战(分类器模型+回归模型)
在全球数字经济时代,有一种金融优势,那就是基于消费者大数据的纯信用! 我们不妨称之为数据信用,它是一种面向未来的财产权,它是数字货币背后核心的抵押资产,它决定了数字货币时代信用创造的方向.速度和规模. ...
- 风控建模(七):催收评分卡的流程—上(所有评分模型与机器学习模型同样适用)
关注公众号" 番茄风控大数据",获取更多数据分析与风控大数据的实用干货. 说了那么久的建模,今天应该跟大家分享一下实际建模的流程步骤了. 整套建模代码我分别用sas跟pyt ...
- 风控建模中的样本偏差与拒绝推断
风控业务背景 幸存者偏差(Survivorship Bias)是一个广泛存在的逻辑谬误.我们在进行统计的时候,可能会忽略样本的随机性和全面性,用局部样本代替了总体样本,对总体的描述出现偏差,从而得出错 ...
- 手把手系列|风控建模中共线性的影响和处理(上)
序言: 所谓的多重共线性是指一些自变量之间存在较强的线性关系.这种情况在实际应用中非常普遍,如研究高血压与年龄.吸烟年限.饮白酒年限等因素的关系,这些自变量通常是相关的. 如果当我们发现有多个变量存在 ...
- 风控建模十二:数据淘金——如何从APP数据中挖掘出有效变量
风控建模十二:数据淘金--如何从APP数据中挖掘出有效变量 1.常识知识 2.个例分析 3.分布排查 智能手机的诞生改变了人类的生活方式,智能手机所承载的功能日臻完善.强大,人们在衣.食.住.行.工作 ...
- 《Python金融大数据风控建模实战》 第6章 变量分箱方法
<Python金融大数据风控建模实战> 第6章 变量分箱方法 本章引言 Python代码实现及注释 本章引言 变量分箱是一种特征工程方法,意在增强变量的可解释性与预测能力.变量分箱方法主要 ...
- 风控建模中的IV和WOE
在风控建模中IV(信息价值)和WOE(证据权重)分别是变量筛选和变量转换中不可缺少的部分. 很多文章已经讨论过这两个变量,本文在吸收前人优秀成果的基础上,希望用通俗易懂的语言让大家快速理解这两 ...
- UC伯克利马毅老师新书:高维数据分析的低维建模
马毅老师为计算机视觉领域国际知名学者,现任加州大学伯克利分校电子工程与计算机科学系教授.2014年至2017年间,他曾任上海科技大学信息科学与技术学院常务院长兼教授. 马老师的研究方向主要为计算机视觉 ...
最新文章
- python读取文件特定内容_python读取指定内存的内容
- IOS和Andriod手机浏览器内核
- java安全点_关于OopMap、SafePoint(安全点)以及安全区域
- birt脚本for循环_Shell脚本编程2 for循环/while循环
- 【最爽的日期工具包LocalDate·超爽,超实用】(Java8版本)
- 拦截器 过滤器 监听器 的区别
- 怎么用计算机搜索文件,Win7系统如何使用内置搜索功能筛选文件名与内容
- Kali Linux 秘籍 翻译完成!
- hotmail发送邮件html,HTML电子邮件中的链接在Hotmail中不起作用
- 实现UniqueAttribute唯一性约束-优化版
- 西门子1200走总线FB284控制3台V90伺服和相机调整角 度,DP通讯控制FANUC机器人
- 【乌拉喵.教程】TestBench仿真给输出脚赋值引起的问题
- 无为而无不为——论老子哲学的深度悖论(转载)
- 计算机硕士研究生毕设选题方向推荐 - 题目推荐
- 纯CSS实现3D旋转相册特效
- Vue组织架构图组件
- cdr怎么把矩形去掉一个边_如何把一个矩形的四个角变形?
- Log4j2高危漏洞CNVD-2021-95914分析复现修复
- 知云文献翻译打不开_一款好用的文献英中翻译软件
- Blender_7_倒角
热门文章
- jquery html包含自身,jquery 获取 outerHtml 包含当前节点本身的代码
- 饥荒怎么把离线服务器改成在线,饥荒联机版专服简易启动教程
- html生成jsessionid,jsessionid是什么
- android信鸽推送demo_android studio 集成腾讯信鸽推送
- Arcis矢量化边界问题处理
- Mac磁盘如何分区?教你Mac系统磁盘自由分区教程!
- MATLAB - 傅里叶分析及 FFT 频谱分析
- oracle的dbv命令,Oracle的DBV命令行工具用法详解
- 微信公众号php支付设置回调,php微信公众号支付讲解(JSAPI)
- 硬盘的老化测试软件,硬盘检测工具使用方法