一. 整体项目介绍
1.风控业务和风控报表
零售金融产品相关的指标风控建模流程
2.特征工程
特征构造特征筛选
3.评分卡模型构建
逻辑回归集成学习 XGBoost LightGBM模型评估
4.样本不均衡问题/异常点检测
二. 信贷与风控介绍
信贷业务
信贷业务,就是贷款业务,是商业银行和互联网金融公司最重要的资产业务和主要赢利手段
-
通过放款收回本金和利息,扣除成本后获得利润。
-
贷款平台预测有信贷需求用户的还款情况,然后将本金借贷给还款概率大的用户
信贷的风险控制
-
信贷业务中,使用信用来预支金钱,在小额贷业务中往往没有抵押物,那么贷款方就会承担一定风险(用户不还钱)
-
风控就是对用户的信用风险进行管理与规避,对于预测信用较差的人,不向其放款,即便放款,也会是较小的贷款额度和较高的利率
-
信贷领域有两类风险
-
信用风险:借款人的的还款能力和还款意愿在贷款后出现问题的风险
-
欺诈风险:借款人压根没想还钱,以诈骗为目的
-
-
风控业务主要针对这两类风险
-
信用评分系统:针对信用风险
-
反欺诈系统:针对欺诈风险
-
-
基于机器学习的人工智能风控模型对比传统人工审批
-
人工审批:效率低,对人员业务能力要求高,不适合金融零售业务场景
-
机器学习模型:批量,迅速,准确,同时处理大量贷款请求(几万,几十万,上百万/天)
-
三. 信贷产品及风险介绍
信贷产品
信贷产品介绍
个人信贷产品 | ||
---|---|---|
大额借贷 | 房贷车贷 | |
小微企业贷 | ||
小额借贷 | 消费贷 | 蚂蚁花呗,京东白条 |
现金贷 | 蚂蚁借呗,京东金条,微粒贷,各类网贷 | |
数据服务 | 信用分服务 | 芝麻信用分,京东小白分 |
信用数据服务 | 同盾数据,百融,集奥,大峰... |
现金贷
申请借款->放款给客户->客户还款
额度 | 500~3000 |
---|---|
利率 | 24%~36% |
期限 | 714,30天 |
放款形式 | 借给现金,不限场景 |
可选功能 | 订单展期 |
现金贷产品 | 年化利率 | 现金贷产品 | 年化利率 |
---|---|---|---|
苏宁金融 | 24% | 国美易卡 | 34% |
蚂蚁借呗 | 24% | 马上消费 | 35% |
微粒贷 | 24% | 招联金融 | 36% |
有钱花 | 24% | 桔子分期 | 36% |
京东金条 | 24% | 拍拍贷 | 36% |
360借条 | 24% | 趣店 | 36% |
小米金融 | 24% | 捷信 | 36% |
美团生活费 | 24% | 宜人贷 | 44% |
分期乐 | 24% | 玖富 | 50% |
消费贷
信用卡,花呗,白条等产品,有账单日,还款日
申请消费贷 -> 额度授信->客户使用消费贷消费
额度 | 1000~10000 |
---|---|
利率 | 24% |
账期 | 30天 |
放款形式 | 指定消费场景 |
可选功能 | 最低还款,账单展期,账单分期,停息挂账,临时额度,备用金 |
产品类型
-
单期产品
-
多期产品
-
循环额度产品
还款方式
-
砍头息:短期产品, (服务费)
-
等额本金
每月还款额度依次递减,
-
等额本息
每月还款额度一样, 整个贷款利息要比等额本金多一些
信贷领域风险类别
-
信用风险:借款人的的还款能力和还款意愿在贷款后出现问题的风险
-
欺诈风险:借款人压根没想还钱,以诈骗为目的
常见风险形式
-
冒名顶替,黑产骗贷
-
多头借贷,借新还旧
客户:工行信用卡,招商信用卡... n张信用卡,网贷平台1,网贷平台2,网贷平台n
用新借来的钱还已有的负债:负债变多 -> 需新借更多 -> 设法提额 -> 信用资质不够 -> 出现流动性风险 -> 逾期
特点:第三方数据:多头申请记录
APP安装:大量借款类APP
短信:大量申请短信,提醒还款,催收短信
-
POS机套现,以少换多
购买有支付牌照机构的POS机进行套现,手续费0.6%
-
针对风控模型,制作数据
使用花呗在天猫购物,对花呗账单做分期
买入存金宝,一个礼拜后追加存金宝资金
购买***元基金
保持余额宝XXX元不动,余额宝累计收益做到 XX元
购买XXXX保险
四. 风控相关术语
术语 | 解释 |
---|---|
※DPD | Day past due 逾期天数 DPD0为到期当日,DPD1为逾期一日,DPD7为逾期一周 |
※FPD | First time past due 首次逾期天数 |
F/S/T/QPD | 首次 二次 三次 四次 逾期天数 |
※M1 | 逾期 [1, 30)天 M 是英文“Months”的首写字母 |
※M1+ | 逾期[30, inf]天 |
default | 坏账 |
delinquency | 拖欠 |
flow rate | 流动率 一般指M1向M2,M2向M3转移的比例 |
bad rate | 坏账率 当月不良资产数/总资产数 |
vintage | 账龄分析 |
五. 风控业务案例
案例背景
数据集说明
-
从开源数据改造而来,基本反映真实业务数据
-
销售,客服可以忽略
-
账单周期,放款日期
-
账单金额-实收金额 = 未收金额
-
应付日期为还款时间
-
账期分成两种:60天和90天
-
实际到账日为空白,说明没还钱
业务分析
-
每个季度账单金额和坏账率(逾期90天以上)
所有未收金额/所有账单金额
未收金额 = 账单金额-实收金额
-
每个季度60天账期的入催率,90天账单的入催率
入催率 = 入催金额/账单金额
-
历史逾期天数的回款情况(回收账单数)
历史逾期天数:历史有逾期,但是相对现在来说,钱已经还完了
当前逾期天数:现在还欠着钱,也就是说钱没还完
代码实现
导包
import pandas as pd from pyecharts.charts import * from pyecharts import options as opts import os os.chdir(r'D:\CodeProject\05JRFK_Project\my_project\day01-风控相关业务') os.getcwd()
查看数据
df1 = pd.read_excel('../data/业务数据.xls') df1.info() df1.describe() df1.head(100)
数据预处理
当前日期
准备当前日期, 已数据中的实际倒找到账日最大值为当前日期
# 准备当前日期, 已数据中的实际倒找到账日最大值为当前日期 # pd.to_datetime(df1['实际到账日']).max() # Timestamp('2019-05-17 00:00:00') # 用实际还款日的最大值表示数据统计时间 today_time = pd.to_datetime(df1['实际到账日'].fillna('0').max()) # 效果同上 today_time
缺失值
# 填充缺失值 df1['实收金额'] = df1['实收金额'].fillna(0) df1['开票金额'] = df1['开票金额'].fillna(0) df1['未收金额'] = df1['未收金额'].fillna(0) df1.info()
时间类型转换
# 时间类转换 df1['应付日期'] = pd.to_datetime(df1['应付日期']) # 还款日期 df1['账单周期'] = pd.to_datetime(df1['账单周期']) # 放款日期 # 实际到账日有缺失, 可以填充也可以过滤, 最终看需求 # 如果到账日等于当前日期, 则说明没还钱(实际到账日为实际还款日) df1['实际到账日'] = pd.to_datetime(df1['实际到账日']).fillna(today_time) df1.info()
构造中间字段
是否到期
df2 = df1.copy() # 添加是否到期列 df2['是否到期'] = df2.apply(lambda x: 0 if x['应付日期'] > today_time else 1, axis=1) # df2.['应付日期'].apply(lambda x: 0 if x > today_time else 1) # 效果同上
是否到期90天
# 添加是否到期90天字段 df2['是否到期90天'] = (today_time - df2['应付日期']).map(lambda x: 1 if x.days > 90 else 0) df2.是否到期90天
未收金额
# 构造未收金额2(原始未收金额字段有误) df2['未收金额2'] = (df2['账单金额'] - df2['实收金额'])
历史逾期和当前逾期
# 历史逾期 # 等于0时计算还款时是否逾期(小于0表示: 提前还款) # 未还金额不等于0表示到统计日期还未归还完毕(当前逾期天数) df2['历史逾期天数'] = df2.apply(lambda x:(x['实际到账日'] - x['应付日期']).days if x['未收金额2'] == 0 else (today_time - x['应付日期']).days, axis=1) # 当前逾期天数 # (负数表示统计日期还没到还款日期) df2['当前逾期天数'] = df2.apply(lambda x: x['历史逾期天数'] if x['未收金额2'] > 0 else 0, axis=1) df2.tail(100)
需求1:坏账率
每个季度账单金额和坏账率(逾期90天以上)
所有未收金额/所有账单金额
未收金额 = 账单金额-实收金额
构造季度字段
# 构建季度字段 df3['账单季度'] = df3['账单周期'].apply(lambda x: x.to_period('Q')) df3.head()
过滤季度
2019Q1和2019Q2到统计日期大概率不会有逾期90天的数据
# 过滤季度 df3['账单季度'].value_counts() df3 = df3[(df3['账单季度'] >= '2017Q3') & (df3['账单季度'] <= '2018Q4')] df3.shape
统计指标
账单金额, 到期90天金额, 当前到期90+金额
# 统计指标: 账单金额, 到期90天金额, 当前到期90+金额 # 账单金额 fn1 = df3.groupby('账单季度')[['账单金额']].sum() fn1.columns = ['账单金额'] fn1 # 到期金额(90天) df4 = df3[df3['是否到期90天'] == 1] fn2 = df4.groupby('账单季度')[['账单金额']].sum() fn2.columns = ['到期金额'] fn2 # 到期(90天+)金额 fn3 = df4.groupby('账单季度')[['未收金额2']].sum() fn3.columns = ['到期90+金额'] fn3
拼接指标计算坏账率
# 合并上述的3DF对象 final1 = pd.concat([fn1, fn2, fn3], axis=1) final1 # 计算净坏账率 final1['90+净坏账率'] = round(final1['到期90+金额'] / final1['到期金额'], 3) final1
结果可视化
# 画图 bar = ( Bar() .add_xaxis(list(final1.index.values.astype(str))) .add_yaxis( "账单金额", list(final1.账单金额), yaxis_index=0, color="#5793f3", ) .set_global_opts( title_opts=opts.TitleOpts(title="90+净坏账率"), ) .extend_axis( yaxis=opts.AxisOpts( name="90+净坏账率", type_="value", min_=0, max_=0.014, position="right", axisline_opts=opts.AxisLineOpts( linestyle_opts=opts.LineStyleOpts(color="#d14a61") ), axislabel_opts=opts.LabelOpts(formatter="{value}"), ) ) ) line = ( Line() .add_xaxis(list(final1.index.values.astype(str))) .add_yaxis( "90+净坏账率", list(final1['90+净坏账率']), yaxis_index=1, color="#675bba", label_opts=opts.LabelOpts(is_show=False), ) ) bar.overlap(line).render()
结果图
需求2: 计算60天和90天的入催率
根据账单周期计算入催金额
# 60天账期数据 df4 = df3[df3['账期'] == 60] fn1 = df4.groupby('账单季度')[['账单金额']].sum() fn1.columns = ['60天到期账单金额'] fn2 = df4.groupby('账单季度')[['未收金额2']].sum() fn2.columns = ['60天到期入催金额'] # 90天账期数据 df4 = df3[df3['账期'] == 90] fn3 = df4.groupby('账单季度')[['账单金额']].sum() fn3.columns = ['90天到期账单金额'] fn4 = df4.groupby('账单季度')[['未收金额2']].sum() fn4.columns = ['90天到期入催金额']
合并计算入催率
# 60天入催率 final2['60天账期入催率'] = round(final2['60天到期入催金额'] / final2['60天到期账单金额'], 3) # 90天入催率 final2['90天账期入催率'] = round(final2['90天到期入催金额'] / final2['90天到期账单金额'], 3) final2
结果可视化
# 可视化绘图 line = ( Line() .add_xaxis(list(final1.index.values.astype(str))) .add_yaxis( "60天账期入催率", list(final2['60天账期入催率']), yaxis_index=0, color="#675bba", label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts( title_opts=opts.TitleOpts(title="不同账期入催率"), ) .add_xaxis(list(final1.index.values.astype(str))) .add_yaxis( "90天账期入催率", list(final2['90天账期入催率']), yaxis_index=0, color="#d14a61", label_opts=opts.LabelOpts(is_show=False), ) ) line.render()
结果图
需求3: 回收帐单数(不是被催收后的回收率)
数据过滤
筛选出未收金额等于0(到统计时没有待偿还的金额)切已经到期的数据(到期就会被记录)
df5 = df3[(df3['未收金额2'] == 0) & (df3['是否到期'] == 1)] df5
cut分箱历史逾期天数
df5['历史逾期天数'] = pd.cut( df5['历史逾期天数'], bins=[-999, 0, 5, 10, 15, 20, 30, 60, 90, 999], labels=['0', '1-5', '6-10', '11-15', '16-20', '21-30', '31-60', '61-90', '91+'] ) final3 = df5.groupby('历史逾期天数')[['账期']].count() final3.columns = ['回收账单数'] final3
结果可视化
ydata = final3['回收账单数'].values.tolist() bar = ( Bar() .add_xaxis(list(final3.index.values.tolist())) .add_yaxis("收回账单数",ydata,yaxis_index=0,color="#675bba") .set_global_opts( title_opts=opts.TitleOpts(title="不同逾期天数的已收回账单数"), ) ) bar.render()
结果图
业务解读
-
从数据中看出,在2018年Q2季度之前,运营策略比较保守,坏账金额和入催率都比较低,
-
2018年Q2之后,有可能是由于运营策略调整,给更多的人放贷,但坏账率和入催率均在3%一下,在合理范围内
-
不同逾期天数收回账单的数据看,30天内能收回绝大部分账单