PowerLZY's Blog

本博客主要用于记录个人学习笔记(测试阶段)

风控数据—手机App数据挖掘实践思路

引言

作为移动互联网时代的主要载体,智能手机逐渐成为人们日常生活中不可或缺的一部分,改变着人们的生活习惯。比如,可以用“饿了么”点外卖,“支付宝”可以用来种树,“抖音”可以用来上厕所......强大的App给我们的生活带来了巨大的便利。

img

正因为如此,App与用户之间存在着密不可分的联系,用户在频繁使用这些App过程中也积累了大量的个人历史数据。 这些App数据能帮助我们更好地去理解用户,推测用户的性别、职业、收入、兴趣、偏好等属性,也就是所谓的KYC(Know your customer)

在风控中,App数据也有其重要价值,常用于反欺诈、风控建模特征工程等。本文将分享App数据的一些挖掘思路,以及实践建议。

首先,让我们思考下几个问题:

  • 如何获取数据?
  • 数据长啥样?
  • 数据如何和业务相结合去理解?
  • 可以采用什么算法实现高效提取信息?
  • 如何利用这块数据服务业务?

一、 App数据长啥样?

根据资料显示,当前手机App数据主要包括:App安装包名称、App中文名、App安装列表、App安装序列。

为便于区分,常把App中文名记为app_name,App包名称(package)记为pkg_name。其中pkg_name是App的唯一ID,app_name则因为下载渠道、版本更新、数据采集等因素影响导致不唯一。例如,"企业微信"的pkg_name为“com.tencent.wework”,而app_name可能会有“企业微信”、“微信企业版”、“微信(企业版)”等多个值。

至于如何获取手机App package?可以参考这里:实现获取appPackage和appActivity的方法

img
  1. App安装集合(App List):指手机上安装的所有App的集合,一般用逗号隔开,如:“com.alibaba.android.rimet,com.tencent.mm,com.citiccard.mobilebank,com.icbc,com.hongxin1.rm”。可以认为是一个集合,因此是无序的。
  2. App安装序列(App Seq):指手机上包含安装时间的App序列。如:[" 信","1558014854044","com.tencent.mm",,"7.0.4"],分别代表App中文名、App安装时间戳、App包名称和版本号。由于可根据安装时间戳得到安装顺序,因此是有序的。

我们拿“微信”的package在腾讯应用宝中检索,那么就可以找到以下App描述数据

  1. 分类标签:标签精确表达了App的核心功能。但可能是开发者在发布App时从可选项中主观选择了一个标签,也有可能腾讯会在后期维护标签。标签不一定准确,但可作为一个重要的参考维度。
  2. 下载量:可作为判断App是否小众的一个参考维度。然而,能在应用宝上架的App一般是合规的;对于一些质量较差无法上架的app就无法获取到下载量。
  3. 应用描述:开发者对App的主要功能给出的描述性文本,可提取关键词、主题等内容。但如果只是根据关键词匹配,很容易出错。比如微信中藏有游戏中心入口,文本中出现“游戏”关键词,但这并不是一个游戏类App。

二、App如何有效分类?

特征工程-时序数据与特征工程

信贷时序数据与特征工程介绍 - 求是汪在路上的文章 - 知乎 https://zhuanlan.zhihu.com/p/397614923

引言

无论是模型还是策略,其遵循的方法论都是“发现问题->确定目标->分析原因->寻找抓手->上线应对”这样一个闭环。对于模型岗而言, 其核心KPI是提供具有良好排序能力的模型。而特征工程是提高模型区分度的最有效途径,决定了模型拟合的上限。

信贷业务流程中积累了很多时间序列数据。因此,本文将介绍如何利用时序数据来构建常见的特征。同时,为兼顾实践性,本文附上了一些特征的SQL实现逻辑,供参考。

一、信贷时序行为数据

信贷业务经营中会积累很多时间序列数据。例如,客户的APP登录行为埋点数据、借款申请行为记录数据、还款行为数据、调额记录数据等。根据是否在平台内部沉淀,这些数据又可分为内部和外部数据。

1.1 内部数据

以借款、还款行为数据为例,需要明确几个最细的时间粒度,包括:

  • 借款时点(draw_date):客户借款申请时点。
  • 应还时点(due_date):约定还款时间,固定不变。
  • 实还时点(settle_date):客户实际还款时间,若早于应还日,称提前还款;若晚于应还时点,则成为逾期(overdue);若在应还日当天还款,则为正常还款。

独立来看,每个时点的数据都具有不同的信息。对于借据而已,具有以下属性:

img

因此,从借款订单数据入手,其可反映客户的额度使用率、借款金额变化、借款时间集中度等信息。

如图2所示,当把这些时点线索交叉在一起,就会衍生出大量信息。例如:

  • 结合应还时点和实还时点,可刻画客户历史上的还款行为,包括逾期、提前还款、正常还款。——由此可构建“过去一段时间内,逾期/提前还款/正常还款次数”这类特征。
  • 结合应还时点和借款时点,如果客户在某个应还时点前多次发起借款申请,那么很可能是在“借新还旧”。——由此可构建“过去一段时间内,应还日前借款次数”这类特征。
  • 结合逾期时点和借款时点,如果客户当前处于逾期状态,则新申请借款则可直接拒绝,或者提前管制客户。
img

1.2 外部数据

客户不仅会在本平台内借款,当无法满足其借款需求时,其也会在其他平台借款,由此形成了多头借款行为数据。这是一块非常宝贵的数据。

在《求是汪在路上:多头借贷风险分析与建模》一文中,我们介绍过多头数据的相关知识。整合目前市场上数据商提供的多头数据,我们可分解为以下结构:

img

在本文中,先构造一些假数据用以后续衍生特征。可通过以下SQL语句建表,并插入数据。

二、行为特征衍生思路

如果拥有这样的数据,你会如何构建特征?通常做法是取最近一段时间窗口(比如最近12个月),在这段时间内,我们可以从以下维度来统计出可累加的指标:

  • 指标维度:金额、次数、笔数、天数、机构数、人数
  • 时间粒度:日、周、月
  • 聚合函数:计数(count),求和(sum)

为什么要是可累加的呢?这是因为我们可以再次聚合,并且后续计算比例类特征时方便。由此,可得到如图5所示的时序数据:

img

在《求是汪在路上:风控特征—时间滑窗统计特征体系》中,介绍过RFM特征体系的概念。

  • R(Recency)客户最近一次交易消费时间的间隔。R值越大,表示客户交易发生的日期越久,反之则表示客户交易发生的日期越近。
  • F(Frequency)客户在最近一段时间内交易消费的次数。F值越大,表示客户交易越频繁,反之则表示客户交易不够活跃。
  • M(Monetary)客户在最近一段时间内交易消费的金额。M值越大,表示客户价值越高,反之则表示客户价值越低。

在RFM特征体系的基础上,我们进一步扩展维度。至少能从图5中捕捉到以下信息:

1)常规统计特征

把客户行为按最小时间粒度统计完毕后,我们就会得到一个分布。接下来则是利用统计函数最大值(MAX)、最小值(MIN)、平均值(AVG)、标准差(STD)来描述以上分布特征。

但是,这些指标在统计时只能对给定的数据集合计算,没有考虑到时间序列变化信息。换言之,什么时间段在持续上升,什么时间段在持续下降,什么时间段断档形成空窗期,这些信息都被掩盖了。

2)时间距离特征

用以刻画客户最远一次、最近一次或者某个特殊事件发生的时点。例如:

  • 最近一次申请日距离观察点的日期差(天数)
  • 最近一次取到最大值、最小值的事件时点距离观察点的日期差(天数)
  • 最近一次逾期距离观察点的日期差(天数)

3)行为波动特征

用以刻画客户某段连续时间内的行为变化特征。例如:

  • 最长持续上升(或下降)的时间跨度(天数)。
  • 最长睡眠期(天数)。即,客户多久没有活跃了?
  • 连续行为窗口出现次数。

在信贷风控中,变化趋势是衡量风险非常重要的维度。我们喜欢稳定,但是风险天生具有易变性,当有变化的趋势就意味着风险,也意味着机遇。例如,当客户近期在行业内借款申请次数在上升时,我们就可以判断客户资金紧张,故而在到处借钱。那么,我们需要思考:

  1. 业务侧:客户是否因为我们的额度不够,无法满足其资金需求而跑到其他平台借款?出于余额增长的目标,我们能不能给客户提额呢?
  2. 风险侧:该客户的还款能力是否出现问题?我们给他放开额度后,我们能否承受风险?那么我们就需要给客户打一下风险分,判断客户风险。

为捕捉客户行为变化趋势,我们可以用一种简单的方法:最近N个月次数 / 最近M个月次数(N < M)。例如,多头变量中,该值越高,说明借款申请记录更为集中在近期。一般来说,近期行为对于识别短期欺诈风险更有用,远期行为对于识别长期信用风险更有用。

4)集中度特征

用以刻画客户行为的偏好程度。如果说时间维度是纵向视角,那么集中度特征则是横截面视角。通常以比例特征形式出现。例如:

  • 一天内集中于哪个时间段活动?
  • 借款机构中集中于哪些机构?
  • 借款金额倾向于什么数目(例如1K以内,1K-5K,5K以上)?
  • 购买的商品内集中于买什么品类?

四、其他算法实现思路

前面都是从业务领域知识先入手,分析潜在有用的变量逻辑,再去实现。这是平时大部分模型师所做的事情。但目前也有一些尝试用LSTM、RNN来处理时序数据的技术方案。这是后续可尝试探索和对比的方向。

img

特征工程-风控变量加工实践经验

风控变量加工实践经验 - 求是汪在路上的文章 - 知乎 https://zhuanlan.zhihu.com/p/479784293

本文总结了风控变量开发过程中的一些实践经验。包括:

  1. 检查数据一致性,确保数据质量
  2. 设计好整体架构,提高计算效率

我们总在说,数据决定特征的上限,特征决定模型的上限。风控决策过程中依托大量变量,这些变量从开发到上线的研发流程如下:

  1. 风控模型同学先基于离线数据源使用Hive SQL挖掘一系列变量,经过建模筛选后将变量需求提给开发同学。
  2. 开发同学与模型同学一起核对数据源以及变量实现逻辑,将每个变量用Java代码实现。
  3. 在线灰度运行,与离线Hive T+1产出的变量以及模型分数做一致性比对,满足较高的一致性和稳定性后才最终发布。

一、线上线下底层数据一致

底层数据一致是最根本的要求,其内涵包括两点:

  • 线上数据落快照,确保可回溯、不穿越。
  • 线上数据经清洗到线下数据后确保一致。

1. 线上数据落快照,确保可回溯、不穿越。

数据资产非常宝贵,一些内部数据早点落快照积累,后期才能进行回溯。在这个过程中,数据穿越问题是最为常见的,其导致的结果是离线分析效果可能很棒,但真正上线后效果一塌糊涂。因此,在做特征变量时都要限制数据采集时间在回溯时点之前。

2. 线上数据经清洗到线下数据后确保一致。

线上数据一般是json这类的半结构化数据,为了离线分析时使用方便,我们通常将其解析为结构化的数据表。因此,在此转换过程中,有可能会出现字段取值错列、错误等情况,需要进行一致性检查。

二、离线变量加工注意事项

在平时业务实践中,我们习惯利用SQL来衍生特征变量,原因在于:

  • SQL可分布式计算,对于海量数据处理上效率更高。
  • SQL语法较为固定,容易阅读理解;而Python语法灵活,每个人写的代码风格差异很大,较难维护理解。
  • SQL有大量内置函数(聚合函数、窗口函数等),可省去很多自定义函数的时间。

但是,在开发过程中需要注意以下几点:

1. 检查原始字段的取值类型。对于字符型变量,直接比较时容易出错,如下所示。例如,字符串类型的1000会比800小。因此,在聚合计算风控变量"最近N个月的最大借款金额"时就会出错。

1
2
3
4
5
6
7
8
9
spark-sql> select '1000' > '800';
false
Time taken: 3.679 seconds, Fetched 1 row(s)
spark-sql> select 1000 > 800;
true
Time taken: 0.16 seconds, Fetched 1 row(s)
spark-sql> select cast('1000' as int) > cast('800' as int);
true
Time taken: 0.198 seconds, Fetched 1 row(s)

2. 检查原始字段的取值范围。避免想当然地认为,该变量什么含义就"应该"是什么取值,或者完全按照编码文档来写逻辑,一切以实际数据为准。包括但不限于:

  • 确认字段正常不应该缺失的情况,确认是否实时数据落库过程中清洗所引起的。
  • 从业务上检查字段含义对应的取值范围,确认脏数据该如何清洗。例如,对于日期类变量,其取值可能有如下格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
datetime.datetime.strptime(date, "%Y-%m-%d")
datetime.datetime.strptime(date, "%Y/%m/%d")
datetime.datetime.strptime(date, "%Y%m%d")

spark-sql> select to_date('2022.03.20','yyyy.MM.dd');
2022-03-20
Time taken: 0.18 seconds, Fetched 1 row(s)
spark-sql> select to_date('2022/03/20','yyyy/MM/dd');
2022-03-20
Time taken: 0.119 seconds, Fetched 1 row(s)
spark-sql> select to_date('20220320','yyyyMMdd');
2022-03-20
Time taken: 0.365 seconds, Fetched 1 row(s)

3. 适当定义一些中间变量。其好处在于:

  • 统一维护映射关系,便于维护修改变量口径;
  • 提高计算效率,一次计算可复用,不必多次重复运算
  • 便于代码review,结构层次上更为清晰易懂。

中间变量适合需要利用多个字段来定义的场景,或者一个字段需要进行较为复杂的计算。例如:

1
2
3
4
case when recall_date > due_date and repay_date = due_date then "已到期,正常还款"
when recall_date > due_date and repay_date < due_date then "已到期,提前还款"
when recall_date > due_date and (repay_date > due_date or repay_date is null) then "已到期,逾期还款"
else "未到期" end as repay_state -- 还款状态

对于多人协作的项目,需要大家约定好中间变量口径,避免后续整理文档时出现类似的变量多种口径,引起IT同学混淆。

4. 适当制作一些中间表

其作用在于,采取最小可用数据范围原则,减少计算成本。例如:计算借据层面的字段"最近3个月内的借款次数",只需要用到借据表的信息,并不需要还款计划表的信息。

5. 确认每张表的主键,保证唯一

SQL中常见的聚合函数包括最大值(max)、最小值(min)、平均值(avg)、计数(count)、标准差(std)、累加(sum)。如果数据记录出现重复,虽然不会影响max、min这些操作,但是会影响其他聚合计算的值。因此,在做中间表的过程中需要明确主键,确保group by分组的时候是唯一的。

6. 优化SQL代码,提高运行效率。

例如,count(distinct case when 条件 then xxx else xxx end)操作需要消耗大量资源,可以考虑将when中的条件提前到表的where中,过滤减少数据量,但要注意影响范围,避免误伤到其他变量的计算。

同时,还可以将SQL代码做成工作流(workflow),某些任务可实现并行化运行。

三、实时变量上线比对验证

实时变量计算有时效性要求,比如500毫秒内计算出数据。因此,实时变量一般将由IT同学根据文档通过Java开发,在此过程中由于以下原因可能引起变量不一致。

  • 离线变量开发口径逻辑,在文档整理过程中的表述歧义。
  • 模型同学和IT同学沟通过程中的理解差异。
  • Java代码编写过程中存在的Bug。
  • 计算引擎不同所引起的精度差异。
  • 脏数据引起的不一致。

为解决SQL离线变量到在线变量之间的翻译工作,目前有相关的实时SQL计算引擎技术,可参考下面这篇:

https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s/Rx43XfhgdwerQWLn1eI3Ww

线上线下变量确保一致是非常重要的。其能保证基于离线批量数据所建立的风控模型和策略规则,在上线后能按照预期的效果生效。如果不一致,将会导致风控系统不可控。例如,离线评估得到的模型分数阈值为低于600分则拒绝,但线上模型总是和离线有偏差,600这个阈值将拒绝更多的用户,导致通过率下降。

模型同学需要和IT同学协调来定位变量问题,整个工作流程图如下所示。可以看到这是一个闭环迭代的过程,如何加快迭代速度成为关键。

img

变量一致性比对的逻辑很直接,其依赖于测试数据来归纳排查,当两者出现不一致则必有问题。当然,变量的逻辑正确性和取值一致性是两回事,逻辑正确性需要业务知识来确认,取值一致性需要技术工具来解决

为了尽可能提高比对效率,笔者有以下几点建议:

1)准备足量测试原始数据,以及离线变量取值结果。在开发过程中准备一批(如1000条)原始数据json,这批数据必须保证和解析到数据库中的表数据是完全一致的。这样可以确保IT同学在写Java的时候,就可自己测试来消除一部分bug,减少后续工作量。

2)提前准备好相关不一致案例,预约时间集中排查。定位问题是一个非常耗时的工作,因此和IT同学约定好大片段的时间,提前准备好一些案例来沟通。案例准备过程可以将变量划分类别,聚类治之可极大提高效率。

3)整理比对所需要的代码脚本,缩短数据准备时间。比对过程是迭代中完善的,必然需要多轮数据拉取、计算、整理的工作,把这个流程打通形成工作流可加快速度。

在取数得到线上线下数据集后,可根据附录代码计算得到以下比对报告,如图2所示。我们将比对结果分为以下4种情况:

  • 有值且一致:有值并且满足精度一致要求
  • 有值不一致:有值但不满足精度一致要求
  • 缺失不一致:其中有一个为null
  • 缺失且一致:两者取值都为null

对于情况1)和情况3)则需要查相应案例来定位原因。小细节往往能发现大问题,所以不要轻视。

img

附录A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import pandas as pd
import numpy as np
from tqdm import tqdm
from multiprocessing.dummy import Pool as ThreadPool

def compare_online_offline(data_offline,
data_online,
remove_list=['order_id'],
key_var='order_id',
spec_value=0,
filename='report.xlsx',
need_detail=1):
import time
t0 = time.time()
data_online_new = data_online.copy()
data_offline_new = data_offline.copy()
data_offline_new.columns = [i.lower() for i in data_offline_new.columns]
data_online_new.columns = [i.lower() for i in data_online_new.columns]
comp_list = list(set(data_online_new.columns) & set(data_offline_new.columns)) # 取交集
comp_list = sorted(list(set(comp_list).difference(remove_list))) # 比较列
print('比较特征数量为: ' + str(len(comp_list)))
num_varlist = list(set(data_online_new[comp_list].select_dtypes(exclude=['object','datetime']).columns))

def compare(x1, x2, vartype):
if pd.notna(x1) and pd.notna(x2):
if vartype == 'numeric':
return 1 if abs(x1 - x2) < 0.001 else 2
else:
return 1 if x1 == x2 else 2
else:
if not pd.notna(x1) and not pd.notna(x2):
return 3
else:
return np.nan

def stat(var):
df = pd.merge(data_offline_new[[key_var,var]], data_online_new[[key_var,var]], on=key_var, how='inner')
df.columns = [key_var, var+'_offline', var+'_online']
total = df.shape[0]
miss_offline = df[var+'_offline'].isnull().sum()
miss_online = df[var+'_online'].isnull().sum()
spec_offline = sum(df[var+'_offline'] == spec_value)
spec_online = sum(df[var+'_online'] == spec_value)

if var in num_varlist:
mean_offline = df[var+'_offline'].mean()
mean_online = df[var+'_online'].mean()
df[var+'_same'] = df.apply(lambda row: compare(row[var+'_offline'], row[var+'_online'], 'numeric'), axis=1)
value_same = df[df[var+'_same']==1].shape[0] # 有值并且满足精度一致要求
value_diff = df[df[var+'_same']==2].shape[0] # 有值但不满足精度一致要求
else:
mean_offline = np.nan
mean_online = np.nan
df[var+'_same'] = df.apply(lambda row: compare(row[var+'_offline'], row[var+'_online'], 'string'), axis=1)
value_same = df[df[var+'_same']==1].shape[0] # 有值并且相同
value_diff = df[df[var+'_same']==2].shape[0] # 有值但是不同

miss_same = df[df[var+'_same']==3].shape[0] # 两者取值都为null
miss_diff = df[var+'_same'].isnull().sum() # 其中有一个为null
total_same = value_same + miss_same # 两者完全相同
df[var+'_same'] = df[var+'_same'].apply(lambda x: 1 if x in [1,3] else 0)
return [var, total, miss_offline, miss_online, mean_offline, mean_online,
value_diff, value_same, miss_diff, miss_same, total_same, spec_offline, spec_online, df]

# 启动多线程
pool = ThreadPool(10)
stat_list = pool.map(stat, comp_list)
pool.close()
pool.join()

final_cols = ['var','total','miss_offline','miss_online','mean_offline','mean_online',
'value_diff','value_same','miss_diff','miss_same','total_same','spec_offline','spec_online']
stat_arr = [x[:-1] for x in stat_list]
df_stat = pd.DataFrame(stat_arr, columns=final_cols)
df_stat['miss_del'] = df_stat['miss_offline'] - df_stat['miss_online']
df_stat['mean_del'] = df_stat['mean_offline'] - df_stat['mean_online']
df_stat['same_rate'] = df_stat['total_same'] / df_stat['total']
df_report = df_stat[['var','total','miss_offline','miss_online','miss_del','mean_offline','mean_online','mean_del',
'value_diff','value_same','miss_diff','miss_same','total_same','same_rate','spec_offline','spec_online']]
df_report.columns = ['变量名','比较数','离线缺失','在线缺失','离线-在线缺失','离线均值','在线均值','离线-在线均值',
'有值不一致','有值一致量','缺失不一致','缺失一致量','一致量','一致率','离线特殊值数','在线特殊值数']
df_report = df_report.sort_values(by=['一致率'], ascending=1)
df_report.to_excel(filename, index=0)
print('对比报告保存在%s' % filename)
t1 = time.time()
print('耗时%s分钟' % int((t1-t0)/60))

print('拼接明细...')
df_detail = pd.DataFrame(data_offline_new[key_var], columns=[key_var])
if need_detail:
df_list = [x[-1] for x in stat_list]
for i in tqdm(range(len(df_list))):
df_detail = pd.merge(df_detail, df_list[i], on=key_var)

return df_report, df_detail

风控模型

求是汪在路上:多头借贷风险分析与建模

求是汪在路上:抽样对Lift和KS指标的影响

求是汪在路上:客户级通用信用评分模型架构设计

求是汪在路上:大数据信贷风控模型架构

求是汪在路上: 风控模型开发流程标准化

求是汪在路上:风控模型上线部署流程

求是汪在路上:如何量化样本偏差对信贷风控模型的影响

求是汪在路上: 样本权重对逻辑回归评分卡的影响探讨

求是汪在路上:利用样本分群提升风控模型性能

一、风控模型开发流程标准化

风控业务背景

在大型风控建模项目中,模型开发阶段实际上只占其中很小的一部分,我们常把大量精力投入在数据准备、特征工程、模型设计等阶段。同时,开发阶段所用方法重复性相对较高。如果将其模块化封装,可大大提高建模效率。Don‘t repeat yourself!

img

本文主要内容在于:

  1. 理论角度,系统阐述了风控模型开发流程,以及相应的理论依据。
  2. 实践角度,整理了目前一些开源工具包,可供大家自行选择使用。

1.1 风控模型开发流程标准化的意义

在《大数据信贷风控模型架构》一文中,我们系统探讨了大数据风控模型架构的优缺点。从该架构出发,基于历次大型模型开发项目实践,我们往往会发现存在以下问题:

  1. 各建模同学的工程能力存在差异,无法保证所有人都有代码优化及debug的能力。
  2. 整个流程不规范统一,某些同学不清楚基础指标函数的底层逻辑,出现问题难以定位。
  3. 浪费大量时间用于协调沟通,尤其是过程文档记录参差不齐,无法复现。
  4. 建模脚本可能存在多个版本,模型版本管理困难

风控模型开发流程标准化的意义在于:

  1. 提高建模效率:可批量快速生产模型,保证项目按期完成,同时让建模同学更有精力关注模型优化分析等复杂工作。
  2. 理解计算逻辑:帮助团队更为深刻理解各类评估指标背后的业务含义,便于调试优化。
  3. 统一建模流程:约定命名方式。保证在分工协作的情况下,模型开发文档更容易整合汇总,便于Review。
  4. 流程规范约束:减少建模同学(尤其是新手)出错的概率,降低返工可能,并记录必要的中间过程,便于问题回溯。

由于经常需要跨时间窗来分析变量,我们约定以下名词:

  • 样本集:训练集(In the Sample,INS)、验证集(Out of Sample,OOS)、测试集(Out of Time,OOT)
  • 自然月:由于风控领域中样本相对较少,一般按月粒度来观察。对于某段时间内的样本,我们也称为cohort或vintage。通俗而言,就是一个批次(batch)。

1.2 功能模块 - 探索性数据分析

探索性数据分析(Exploratory Data Analysis,EDA)用于初步检验数据质量,因此需要计算各类数据特征指标。

  • 探索数据分布(Exploratory Data Distribution,EDD)
  • 缺失率(Missing Rate)
  • 重复值(Duplicate Value)
  • 单一值(Unique Value)
  • 其他数据质量检查(Quality Check)

1.2.1 探索数据分布(Exploratory Data Distribution,EDD)

功能:按自然月/样本集维度,统计变量的数据分布。

指标

  • 连续型变量,包括:数量(count)、均值(mean)、标准差(std)、最小值(min)、分位数P25、P50、P75、最大值(max)。其中,最大值和最小值可用来观察异常值(outlier)。
  • 离散型变量,包括:取值及出现次数(cnt)、占比(ratio)。

示例:连续变量数据分布(月维度)、离散变量数据分布

img
img
  • 业务含义:基于“历史与未来样本分布相同”的建模假设,我们才能基于历史数据拟合X和Y之间的关系来预测未来。因此,在变量分布上,首先需要保证这一点。

从图1中,我们可以观察分位数的变化差异。例如,变量 [公式] 在2018年1月时最大值异常,其余月份均正常。此时,我们就需要检查:数据源是否存在问题?变量回溯过程是否出错?

1.2.2 缺失率(Missing Rate)

功能:按自然月/样本集维度,统计变量的缺失率。

指标:缺失率 = 未覆盖样本数 / 总样本数 × 100%

img

业务:

  • 用于分析数据源的缺失率,以及未来的采集率趋势。如果缺失率持续升高,我们就认为这块数据不可用。
  • 造成缺失的原因多种多样,可分为随机缺失和非随机缺失。例如,如果是用户自填信息,用户主观不愿意填写而导致数据缺失,属于非随机缺失。

1.2.3 重复值(Duplicate Value)

功能:检验建模样本中是否有重复数据。

指标:按样本ID分组后,统计行数

img

业务:观察相同订单的特征变量取值是否一致。取值相同,只需简单去重;否则,说明生成逻辑有误,需检查SQL逻辑。

1.2.4 单一值(Unique Value)

功能:统计变量中某一固定值的占比。

指标:变量每个取值的出现次数。

示例:变量中0的取值占到70%.

业务:如果变量取值中某一固定值占比很高,那么该变量区别度往往很低。通常,单一值比例超过90%以上,建议删除该变量。

1.2.5 其他数据质量检查(QC)

变量取值本身具有某些业务含义,我们需要结合业务来检验,并记录归档。例如:

  • 特殊值归档说明:例如-9999999是代表缺失,还是其他含义,需给出描述说明。
  • 0的业务逻辑确认:真实值为0?数据缺失?默认填充值?

1.3 功能模块 - 特征选择

根据RFM特征体系,我们可以构造成千上万个特征变量(参考《风控特征—时间滑窗统计特征体系》)。但是,这些变量并不都满足我们的要求, 我们需要剔除不符合要求的变量,从输入上保证风控系统的鲁棒性。

变量筛选(selection)是一个比较复杂的精细活,需要考虑很多因素,比如:预测能力、相关性、稳定性、合规性、业务可解释性等等。考虑不同维度,我们会依据一系列指标进行变量筛选。

从广义上,可分为业务指标和技术指标两大类。

1.3.1 业务指标包括:

  1. 合规性(compliant):用以加工变量的数据源是否符合国家法律法规?是否涉及用户隐私数据?例如,如果某块爬虫数据被监管,那么相关变量的区分度再好,我们也只能弃用。而在国外,种族、性别、宗教等变量被禁止用于信贷风控中,这会存在歧视性。
  2. 可得性(available):数据未来是否能继续采集?这就涉及产品流程设计、用户授权协议、合规需求、模型应用环节等诸多方面。例如,如果产品业务流程改动而导致某个埋点下线,那么相关埋点行为变量只能弃用。又比如,如果需要做额度授信模型,那么只能利用在额度阶段能采集到的实时数据,这就需要提前确认数据采集逻辑。
  3. 稳定性(stable):一方面,数据源采集稳定是变量稳定性的基本前提。例如,外部数据常会因为政策性、技术性等原因导致接入不稳定,这就需要做好数据缓存,或者模型降级机制。另一方面,变量取值分布变化是导致不稳定的直接原因。我们将会采取一些技术指标展开分析,下文将会介绍。
  4. 可解释性(interpretable):需要符合业务可解释性。如果变量的业务逻辑不清晰,那么我们宁可弃之。同时,这也是保证模型可解释性(参数 + 变量)的前提。
  5. 逻辑性(logical):也就是因果逻辑,特征变量是因,风控决策是果。如果某个变量是风控系统决策给出的,那么我们就不能入模。例如,用户历史申贷订单的利率是基于上一次风控系统决策的结果,如果将“用户历史申贷订单的利率”作为变量,那么在实际使用时就会有问题。
  6. 可实时上线:模型最终目的是为了上线使用。如果实时变量不支持加工,那么对应的离线变量就只能弃之。例如,某个离线变量在统计时限定观察期为180天,但线上只支持观察期为90天,那么就不可用。对于不熟悉线上变量加工逻辑的新手,往往容易踩坑而导致返工。

1.3.2 技术指标包括:

基于缺失率(Missing Rate) 基于变异系数(Coefficient of Variation,CV) 基于稳定性(Population Stability Index,PSI) 基于信息量(Information Value,IV) 基于RF/XGBoost特征重要性(Feature Importance) 变量聚类(Variable Cluster,VarClus) 基于线性相关性(Linear Correlation) 基于多重共线性(Multicollinearity) 基于逐步回归(stepwise) 基于P-Vaule显著性检验

  • 基于缺失率(Missing Rate)

业务:变量缺失率越高,可利用价值越低。缺失率变化不稳定的变量,尤其是缺失率趋势在升高,代表未来数据源采集率下降,不建议采用。数据源是特征变量的基础,数据源不稳定,直接导致模型稳定性变差。

功能:

step 1. 按自然月/样本集维度,统计变量缺失率,并计算缺失率的均值、标准差。 step 2. 根据实际场景,设置阈值(如50%、70%、90%)进行筛选。

img
  • 基于变异系数(Coefficient of Variation,CV)

业务:变异系数越小,代表波动越小,稳定性越好。缺点在于CV没有统一的经验标准。

功能

step 1. 基于数据分布EDD,选择某个指标(如均值mean)计算变异系数CV,用来衡量变量分布的稳定性。 step 2. 设置阈值进行筛选。

指标:变异系数 C·V =( 标准偏差 SD / 平均值Mean )× 100%

  • 基于稳定性(Population Stability Index,PSI)

业务:需分申请层、放款层,分别评估变量稳定性。通常会选择0.1作为阈值,只要任意一个不满足稳定性要求就弃用。PSI无法反映很多细节原因,比如分布是右偏还是左偏。此时需要从EDD上进行分析。

img

功能

step 1. 以训练集(INS)分布为期望分布,计算变量的群体稳定性指标PSI。 step 2. 根据PSI的经验阈值进行筛选。

指标[公式]

上式含义为:PSI = SUM( (实际占比 - 预期占比)* ln(实际占比 / 预期占比) );预期占比是指训练集上每个分箱里的样本占比,实际占比是待比较样本集的每个分箱里的样本占比。可参考《群体稳定性指标(PSI)深入理解应用》。

img
  • ==基于信息量(Information Value,IV)==

业务用以评估变量的预测能力。通常情况下,IV越高,预测能力越强。但IV过高时,我们就要怀疑是否发生信息泄漏(leakage)问题,也就是在自变量X中引入了Y的信息。

img

功能

step 1. 按自然月/样本集维度,统计变量的IV。 step 2. 根据IV的经验阈值来筛选。

指标:IV。可参考WOE与IV指标的深入理解应用

[公式]
img
  • ==基于RF/XGBoost特征重要性==

业务:在特征变量特别多的时候,可用于快速筛选特征。从机器学习可解释性角度而言,特征重要性只具有全局可解释性,无法对单个case给出解释。

功能

step 1. 根据树模型训练后给出的特征重要性,一般选择累积重要性Top 95%的变量。 step 2. 为降低一次训练所导致的随机性影响,可综合多次结果来筛选。

指标:特征重要性。XGBoost实现中Booster类get_score方法输出特征重要性,其中 importance_type参数支持三种特征重要性的计算方法:

  1. weight(默认值):使用特征在所有树中作为划分属性的次数。
  2. gain:使用特征在作为划分属性时,OBj的平均降低量。
  3. cover:使用特征在作为划分属性时对样本的覆盖度。

RF中计算特征重要度的主要思想:影响力分析。如果某个输入变量产生扰动,导致模型系统输出性能(利用袋外数据OOB的准确率来评估)产生明显影响,那么说明输入变量对系统具有较大的影响力,特征重要度比较高。

执行步骤为:

step 1. 基准性能:对每一颗决策树,选择相应的袋外数据计算误差,记为 [公式] ; step 2. 输入扰动:随机对袋外数据所有样本的特征加入噪声干扰(如白噪声),再次计算袋外数据误差,记为 [公式] ; step 3. 重要度计算: [公式] ,N为随机森林中决策树的棵数。

img
  • 变量聚类(Variable Cluster,VarClus)

业务:在SAS中对应Proc VarClus方法。

功能

step 1. 列聚类,将所有变量进行层次聚类。 step 2. 根据聚类结果,剔除IV相对较低的变量。

指标

1
2
3
4
5
6
7
from sklearn.cluster import FeatureAgglomeration
from sklearn import preprocessing

X_Scale = preprocessing.StandardScaler().fit_transform(input_df[var_list])
ward = FeatureAgglomeration(n_clusters=n_clusters, linkage='ward')
ward.fit(X_Scale)
clusters = list(ward.labels_)
img
  • 基于线性相关性(Linear Correlation)

业务:逻辑回归作为一种线性模型,其基础假设是:自变量 [公式] 之间应相互独立。当两变量间的相关系数大于阈值时(一般阈值设为0.6),剔除IV值较低的变量。

功能

step 1. 计算变量的线性相关性。 step 2. 相关性较高的多个变量里,保留IV较高的变量。

指标皮尔逊相关系数(Pearson Correlation Coefficient),系数的取值为[公式],两变量相关系数越接近0,说明线性相关性越弱;越接近1或-1,说明线性相关性越强。

[公式]
img
  • 基于多重共线性(Multicollinearity)

对于自变量[公式],如果存在不全为0的系数[公式],使得以下线性等式近似成立:

[公式]

换言之,对于 [公式] 个变量,其中 [公式] 个可以由剩下的 [公式] 个变量线性表示。此时,我们就称自变量[公式]具有较强的多重共线性。

当出现多重共线性时,变量之间的联动关系会导致估计标准差偏大,置信区间变宽。这就会产生一个常见的现象,LR中变量系数出现正负号不一致。

业务:VIF取值的业务含义为:

若VIF < 3,说明基本不存在多重共线性问题 若VIF > 10,说明问题比较严重。

功能:

step 1. 计算变量的方差膨胀因子(Variance Inflation Factor,VIF),适用于线性模型。 step 2. 根据阈值剔除VIF较高的变量。

指标:通常用VIF衡量一个变量和其他变量的多重共线性。

1
from statsmodels.stats.outliers_influence import variance_inflation_factor

示例:

img
  • 基于逐步回归(stepwise)

    • 前向选择(forward selection):逐步加入变量,计算指标进行评估,若不合格则不加入,直到评估完所有变量。

    • 后向选择(backward selection):初始时加入所有变量,根据指标逐渐剔除不合格的变量。

    • 逐步选择(stepwise):将向前选择和向后选择的结合,逐步放入最优的变量、移除最差的变量。

  • 基于P-Vaule显著性检验

业务:根据逻辑回归参数估计表,剔除P值大于0.05的变量。

功能:

step 1. 用于检验自变量X与因变量Y之间的相关性。 step 2. 剔除P值大于0.05的变量。

指标:P-Vaule。在变量相关分析中,对相关系数进行假设检验时:

原假设H0:自变量与因变量之间线性无关。 备择假设H1:自变量与因变量之间线性相关。

或许不太恰当,但通常当P值大于0.05时,接受原假设,否则拒绝原假设。因此,我们需要剔除P值大于0.05的变量。

img

1.4 功能模块 - 模型训练

该模块主要包括变量变换(如分箱)样本准备(包括样本赋权、拒绝推断等)模型参数估计模型分数校准模型文件保存等功能。

1.4.1 WOE转换(Weight of Evidence)

业务:训练集上的WOE曲线需满足单调性,并且经过跨时间窗验证WOE变换逻辑同样满足单调性。如果不满足,那就需要再次调整。

功能:在评分卡模型中用于变量WOE变换,支持等频、等距、自定义间距等分箱操作。

原理:可参考WOE与IV指标的深入理解应用

示例:

img

1.4.2 样本权重(Sample Weight)

功能:给建模样本赋予权重列,以逼近总体样本。

原理:建模本质在于从历史样本中学习未来样本的数理统计规律。

不同的样本权重主要有几个目的:

  • 为了让模型更稳健,一般都是拿近期样本refit模型,但上线后没几个月很快就衰减了,说明训练样本有偏程度比较高,不够代表总体。
  • 早期的历史样本都是拿钱换来的,如何把这部分样本利用起来,不浪费。

==业务角度(主观性强,操作性高):==

  • 按时间因素,近期样本提高权重,较远样本降低权重
  • 按贷款类型,不同额度、利率、期限的样本赋予不同权重
  • 按样本分群,不同群体赋予不同权重

技术角度(操作性复杂,可解释性弱):

  • 借鉴Adaboost思想,对误判样本提高权重
  • 过采样、欠采样、SMOTE等

拒绝演绎(Reject Inference)

功能:样本偏差将会导致模型估计过于乐观。通过推断拒绝样本的贷后表现,加入建模样本,可降低放贷建模样本与申请作用样本之间的偏差。

原理:参考《风控建模中的样本偏差与拒绝推断

参数估计(Parameter Estimation)

功能:支持机器学习模型(如随机森林、GBDT、XGBoost、LightGBM等)调参、评分卡模型逐步回归(stepwise)。

原理:LR采用最大似然估计进行参数估计。而机器学习模型的超参数较多,通常需要借助网格搜索(grid search)、贝叶斯调参等技术,降低对人工经验的依赖。

分数校准(Calibration)

功能:其一,一致性校准,将模型预测概率校准到真实概率。其二,尺度变换,将风险概率转换为平时所见的整数分数。

原理:参考《信用评分卡模型分数校准

模型保存(Save Model)

功能:将模型(参数+结构)保存为pkl和pmml文件,用以最终上线部署。注意需要给不同版本的模型赋予易于辨识、含义清晰的命名。一般建议至少包含以下内容:

命名 = 生成日期+责任人+业务线+应用环节+版本号 例如:20190101_小王_有钱花_额度授信_V1.pmml

原理:参考《风控模型上线部署流程

1.5 功能模块 - 模型评估

在实际业务实践中,我们对风控模型的衡量维度主要包括以下几个方面:

  • 稳定性(Stability)
  • 区分度(Discrimination)
  • 排序性(Ranking)
  • 拟合度(Goodness of Fit)

1.5.1 稳定性(Stability)

业务:在风控中,稳定性压倒一切。PSI取值所对应的业务含义如图8所示。我们需在申请层和放贷层上评估稳定性。原因在于:

  • 虽然模型是基于放贷订单训练,但最终应用在申请层。
  • 申请层和放贷层客群存在差异。

当PSI曲线的趋势一直在上升时,我们就要分析原因,消除不稳定因素。排查方向可参考《特征稳定性指标(CSI)深入理解应用》中的Part 4。

指标:PSI。可参考《群体稳定性指标(PSI)深入理解应用

示例:

img模型分数PSI计算结果表(申请+放贷)

img

1.5.2 区分度(Discrimination)

业务:KS指标是风控建模同学最为追求的指标之一。通常情况下,KS越大,模型性能越好。但更需要分析KS不佳的原因,可结合ROC曲线进行辅助分析。

指标:Gini、AUC、KS。可参考《区分度评估指标(KS)深入理解应用img

img

1.5.3 排序性(Ranking)

业务:大部分模型在最终应用时只是利用到排序性,其主要是为了观察曲线的斜率。如图21所示的模型,排序性非常好,前5个分箱就能抓住大约77%的坏客户。

指标:按自然月/样本集维度,统计包含bad_rate、lift、odds等指标的Gain Table。

lift = 召回样本的bad rate / 全部样本的bad rate odds = bads / good

示例:模型分数Gain-Table

img

1.5.4 拟合度(Goodness of Fit)

业务:我们以训练集为基准,将模型分数划分成10个(或20个)分箱后,查看每个分箱里的reject rate等指标是否一致。理论上,如果按每个Vintage绘制多条曲线,这些曲线会几乎重合,也就是围绕在训练集附近波动。此时,我们就可以相信模型在每一档的坏账估计比较准确。

指标:按自然月/样本集,评估每个分箱里lift、reject rate、bad rate的点估计

示例:

img

1.6 开源的风控建模工具包简介

前文主要介绍一些理论,接下来介绍目前一些开源的风控建模工具包。

feature-selector scorecardpy toad

1.6.1 feature-selector

  • https://github.com/WillKoehrsen/feature-selector

主要功能:包括缺失率、单一值、相关性、特征重要性等。

  1. Missing Values
  2. Single Unique Values
  3. Collinear Features
  4. Zero Importance Features
  5. Low Importance Features

1.6.2 scorecardpy

  • https://github.com/shichenxie/scorecardpy

主要功能:包括数据集划分、变量筛选、WOE分箱变换、评分卡尺度变换、模型性能评估。

  1. data partition (split_df)
  2. variable selection (iv, var_filter)
  3. weight of evidence (woe) binning (woebin, woebin_plot, woebin_adj, woebin_ply)
  4. scorecard scaling (scorecard, scorecard_ply)
  5. performance evaluation (perf_eva, perf_psi)

1.6.3 toad

  • https://toad.readthedocs.io/en/stable/tutorial_chinese.html

主要功能:包括数据分析处理、特征筛选、模型筛选、模型评估、评分卡尺度变换等。

  1. data handling
  2. feature selection and WOE binning
  3. model selection
  4. results evaluation and model validation
  5. scorecard transformation.

风控模型

多头借贷风险分析与建模

抽样对Lift和KS指标的影响

客户级通用信用评分模型架构设计

大数据信贷风控模型架构

风控模型开发流程标准化

风控模型上线部署流程

如何量化样本偏差对信贷风控模型的影响

样本权重对逻辑回归评分卡的影响探讨

利用样本分群提升风控模型性能

抽样对Lift和KS指标的影响

风控业务背景

在测试外部三方数据时,数据服务商只允许我们抛出有限一定量样本(例如5w、10w、20w)来匹配三方数据。这就要求我们对样本抽样。在对外部数据建模测算后,我们如何将结果还原到原始样本上评估呢?针对这种常见场景,本文将探索抽样对lift指标和KS指标的影响。

一、样本抽样方法

在《外部数据风控建模评估分析》中,我们介绍过外部数据的一些基本知识,其测算流程大致如图1所示。

img

在测算外部数据前,第一步非常重要,样本是我们一切分析的载体。由于三方数据服务商一般只能提供有限的测试量(比如10w),那么我们只能从业务样本中进行抽样。那么可以采取哪些抽样方法呢?一般有以下几种:

1.1 简单随机抽样,保持内部真实好坏比例。

  • 好处:建模时可以直接评估模型效果,而不需要考虑==bad rate==失真。
  • 坏处:好坏样本严重不均衡,坏样本总是极少量的,我们可能需要逐月评估单变量和模型KS,以此观察区分度的稳定性。此时,坏样本较少,导致结果失去统计意义,波动较大
img

1.2 好坏分层抽样,好样本N:1抽样,坏样本1:1抽样。

比如,假设内部bad rate是5%,也就是好坏比例约为20:1,那么从20个好样本中随机抽1个,坏样本则全部保留。由此,我们在评估真实bad rate捕捉率时,也可以按此权重进行还原。即,1个好样本代表20个原始好样本。

img

1.3 按资质对客群分层,再分层随机抽样。

具体操作方法为:

  1. 以某段时间窗的样本训练一个排序模型,例如风险模型(PD),或过退模型(AR)。
  2. 对这个时间窗外待抽样的样本进行排序,将人群分为若干个分层。
  3. 分层抽样,每组中相同比例。

这样就可以用来建立适应不同客群的信用分。

img

这三种方案里,我们最常采用方案二。因此,本文以该方案为例进行探讨。

二、Lift和KS指标介绍

Lift, 名称为提升度, 其含义为:经过某种排序后圈出来的坏人浓度, 相对于随机抽样的坏人浓度的提升, 即: \[ L i f t=\frac{cover-bad-rate}{total-bad-rate} \] 举例说明, 如图5所示, 现在有 50 个人, 其中 5 个坏人, 那么整体坏人占比为 \(10 \%\) 。我们将其随机分为 5 组, 每组 10 人,则理想情况下,每组中有 1 个坏人。

现在我们拥有一个排序模型, 对这 50 个人排序后, 我们笑选出分数最低的 10 个人, 也就是第一组, 此时能抓住 2 个 坏人, 坏人浓度为 \(20 \%\) 。那么 lift \(=20 \% / 10 \%=2\)

img

在反欺诈场景中,我们常会用到Lift这个指标来判断规则的有效性。在测试外部数据时,我们会发现有些变量的区分度虽然很低,或者查得率很低,但是其查得人群的坏人捕捉率却很高。这对我们如何进行规则发现也是一个启发。

KS指标的含义详见:《区分度评估指标(KS)深入理解应用

三、抽样对Lift指标的影响

现在我们有这样一个场景,在抽样样本上我们制定一个阈值cutoff,高于这个阈值则通过(PS),低于这个阈值则拒绝(RJ)。在抽样样本上,我们测算拒绝部分的坏人lift,那么在原始样本上,相应的lift是多少呢?

img

我们按照lift的定义来推导所带来的影响。 \[ \begin{aligned} & L i f t=\frac{B a d_{r j} / R J}{\left(B a d_{r j}+B a d_{p s}\right) /(R J+P S)} \\ & =\frac{B a d_{r j}}{R J} \times \frac{R J+P S}{B a d_{r j}+B a d_{p s}} \\ & =\frac{B a d_{r j}}{\operatorname{Bad}_{r j}+\operatorname{Bad}_{p s}} \times \frac{R J+P S}{R J} \\ & =\frac{B a d_{r j}}{B a d_{r j}+B a d_{p s}} \times\left(1+\frac{P S}{R J}\right) \\ & \end{aligned} \] 其中: - \(B a d_{r j}\) : 拒绝部分的真实坏样本数 - \(\operatorname{Good}_{r j}\) : 拒绝部分的真实好样本数 - \(B_{p s}\) :通过部分的真实坏样本数 - \(\operatorname{Good}_{p s}\) : 通过部分的真实好样本数

由于全部保留 \(B a d\) 样本, 同时采用相同的cutoff, 那么 \(B a d_{r j}\)\(B a d_{p s}\) 在抽样前后保持不变。同时, 不可能有 完美的模型, 因此无论是 \(R J\) 还是 \(P S\) 中都有好坏样本,即: \[ \begin{aligned} & R J=B_{a d_{r j}}+\operatorname{Good}_{r j} \\ & P S=\operatorname{Bad}_{p s}+\operatorname{Good}_{p s} \end{aligned} \] 由于简单随机抽样, 假设抽样比例为 rate,那么: \[ \text { Good }_{\text {sample }}=\text { Good }_{\text {origin }} \times \text { rate } \] 同时,在相同的cutoff下,也就是相同的标准下,抽样和原始样本中的好样本比例应该相同,即: \[ \begin{aligned} & \text { Good }_{r j \_} \_ \text {sample }=\text { Good }_{r j \_} \_ \text {origin } \times \text { rate } \\ & \text { Good }_{p s \_} \text {sample }=\text { Good }_{p s \_} \_ \text {origin } \times \text { rate } \\ & \end{aligned}在抽样样本上: \] 在抽样样本上: \[ \begin{aligned} & L i f t_{s a m p l e}=\frac{B a d_{r j}}{B a d_{r j}+B a d_{p s}} \times\left(1+\frac{P S_s}{R J_s}\right) \\ & =\frac{B a d_{r j}}{B a d_{r j}+B a d_{p s}} \times\left(1+\frac{B a d_{p s}+\operatorname{Good}_{p s}}{B a d_{r j}+\operatorname{Good}_{r j}}\right) \end{aligned} \] 在原始样本上: \[ \begin{aligned} & =\frac{B a d_{r j}}{\operatorname{Bad}_{r j}+\text { Bad }_{p s}} \times\left(1+\frac{\text { Bad }_{p s}+\text { Good }_{p s} / \text { rate }}{\operatorname{Bad}_{r j}+\text { Good }_{r j} / \text { rate }}\right) \\ & =\frac{B a d_{r j}}{\operatorname{Bad}_{r j}+\operatorname{Bad}_{p s}} \times\left(1+\frac{\text { rate } \times \operatorname{Bad}_{p s}+\operatorname{Good}_{p s}}{\text { rate } \times \operatorname{Bad}_{r j}+\operatorname{Good}_{r j}}\right) \\ & \end{aligned} \] 对比公式(6)和 (7),我们发现,原始样本上的lift完全可以通过抽样分布上的值进行计算。我们再以实验佐证理论,发现几乎是一样的。造成些许差异的原因可能是样本量。

四、抽样对KS指标的影响

在《区分度评估指标(KS)深入理解应用》中,我们认识了KS的计算原理。那么对于随机抽样而言,好人分布和坏人分布是相互独立的,那么在计算累积好样本分布和累积坏样本分布时,理论上KS不应该发生变化。

数据胜于雄辩。首先对好样本5:1抽样,坏样本则保持1:1不变,由此组成新样本集。

img

接下来,我们对比抽样前后的KS差异,如图8所示。结果并无明显区别。

img

因此,结论为:分层抽样不会改变KS,抽样样本与原始样本上计算的KS相同。在实践中,我们可能因为坏样本过少等原因会影响一些差异,但整体无明显差异。

五、总结

在匹配外部数据后,我们在抽样样本上会进行一些测算,同时希望能还原在真实原始样本上的结果。针对一些模型指标(Lift、KS)的在抽样前后变化情况,结论为:

  • lift指标数值将会发生变化,但可以换算得到。
  • ks指标不会发生变化,但需要保证样本量足够。

Python - dict和set

前言:我们经常会听见很多的概念,哈希值,哈希表,可哈希对象,不可哈希对象,散列表,字典,映射,等等,那么这么多的概念后面到底又有什么区别和联系,它们的本质又是怎么样的,本此系列文章将针对这些概念进行说明,鉴于篇幅较多,本次系列文章将分为两篇来说明,此为第一篇,介绍数据结构哈希表与哈希值。

一、可哈希对象与不可哈希对象

1.1 哈希表(散列表)——hash table

哈希表,又称之为散列表,是一种空间换时间的存储结构,是在算法中提升效率的一种比较常用的方式,但是所需空间太大也会让人头疼,所以通常需要在二者之间权衡。我们会在之后的具体算法章节中得到更多的领悟。

哈希表(散列表),是能够通过给定的关键字的值直接访问到具体对应的值的一个数据结构。也就是说,把关键字映射到一个表中的位置来直接访问记录,以加快访问速度。

  • 关键字,也称之为,或者是Key,这就是我们哈希函数计算的依据
  • 记录,也称之为,,也称之为哈希值,或者是Value,也就是我们通过key然后经过哈希函数运算之后得到的结果,存储这个记录,即存放哈希函数得到的结果的数组称之为 哈希表 或者是 散列表

1.2 哈希表的问题——碰撞Clollision

其中有个特殊情况,就是通过不同的 Key,可能访问到同一个地址,这种现象叫作碰撞(Collision)。

如果“键-值对”在加入哈希表的时候产生了冲突,就必须找另外一个地方来存放它,冲突太多会降低数据插入和搜索的效率,因此希望能找到一个不容易产生冲突的函数,即构造一个地址分布比较均匀的哈希函数。

一个好的散列表设计,除了需要选择一个性能较好的哈希函数,否则冲突是无法避免的,所以通常还需要有一个好的冲突处理方式,目前,这个哈希函数比较常用的实现方法比较多,通常需要考虑几个因素:关键字的长度、哈希表的大小、关键字的分布情况、记录的查找频率,等等。常见的一些哈希函数有:

  • 直接寻址法
  • 数字分析法
  • 平方取中法
  • 取随机数法
  • 除留取余法

当出现冲突的时候,处理冲突的一些方法有:

  • 开放地址法(也叫开放寻址法)
  • 再哈希法
  • 链地址法
  • 建立一个公共溢出区

1.3 什么是可哈希(hashable)?

不可变的数据结构(数字类型(int,float,bool)、字符串str、元组tuple、自定义类的对象)。

不可哈希的数据类型,即可变的数据结构 (字典dict,列表list,集合set)

An object is hashable if it has a hash value which never changes during its lifetime (it needs a hash() method), and can be compared to other objects (it needs an eq()or cmp() method). Hashable objects which compare equal must have the same hash value.

如果一个对象是可哈希的,那么在它的生存期内必须不可变(而且该对象需要一个哈希函数),而且可以和其他对象比较(需要比较方法).比较值相同的对象一定有相同的哈希值,即一个对象必须要包含有以下几个魔术方法:

  • __eq__():用于比较两个对象是否相等
  • __ cmp__() : 用于比较两个对象的大小关系,它与__eq__只要有一个就可以了
  • __hash__():实际上就是哈希函数(散列函数),返回经过运算得到的哈希值

二、dict

CPython 的字典实现为可调整大小的哈希表。与 B-树相比,这在大多数情况下为查找(目前最常见的操作)提供了更好的性能,并且实现更简单。

PU Learning在风控中的应用(理论篇)

一、风控业务背景

在实际分类场景中,我们经常会遇到类似这样的问题:只有已标记的正样本,以及未标记的样本。比如金融风控场景,只有部分用户被标记为欺诈用户,剩下的大量用户未被标记。虽然这其中大多数信用良好,但仍有少量可能为欺诈用户。

为了方便操作,我们可以将未标记的样本都作为负样本进行训练,但存在几个缺陷:

  1. 正负样本极度不平衡,负样本数量远远超过正样本,效果很差。
  2. 某些关键样本会干扰分类器的最优分隔面的选择,尤其是SVM。

如何辨别未标记样本中的正负样本,提升模型准确度,就成为一个值得思考的问题。PU Learning就是解决这种场景的一种学习方法。

本文尝试回答以下几个问题:

  • 我们如何将从PU数据中学习的问题形式化?
  • 通常对PU数据进行哪些假设以便于学习算法的设计?
  • 我们可以从PU数据估计类先验吗?为什么这很有用?
  • 我们如何从PU数据中学习模型?
  • 我们如何在PU环境中评估模型?
  • PU数据何时以及为什么出现在实际应用中?
  • PU学习与机器学习的其他领域有什么关系?

数学符号含义速查\(x\) :某样例的特征向量, \(x \in \boldsymbol{x}\)

\(y\) : 某样例的标签变量, \(y \in \boldsymbol{y}=\{0,1\}\)

\((\boldsymbol{x}, \boldsymbol{y})\) : 某样本二元组

\(s\) : 某样例是否被标注的指示变量, \(s \in s=\{0,1\}\)

\(\alpha\) :先验类别(Class Prior), \(\alpha=\operatorname{Pr}(y=1)\) ,指正类别先验比例

\(c\) : 标签频率(Label Frequency), \(c=\operatorname{Pr}(s=1 \mid y=1)\), 指L集合占总体P集的比例 \(e\) : 倾向评分函数 (Propensity Score) , \(e(x)=\operatorname{Pr}(s=1 \mid y=1, x)\)

\(f(x)\) : 总体样本的概率密度分布函数, \(\quad f(x)=\operatorname{Pr}(x \mid y \in\{0,1\})\)

\(f_{+}(x)\) : 正样本的概率密度分布函数, \(\quad f_{+}(x)=\operatorname{Pr}(x \mid y=1)\)

\(f_{-}(x)\) : 负样本的概率密度分布函数, \(\quad f_{-}(x)=\operatorname{Pr}(x \mid y=0)\)

\(f_l(x)\) : 有标签 (labeled) 样本的概率密度分布函数, \(\quad f_l(x)=\operatorname{Pr}(x \mid s=1) \subset f_{+}(x)\)

\(f_u(x)\) : 无标签 (unlabeled) 样本的概率密度分布函数, \(\quad f_u(x)=\operatorname{Pr}(x \mid s=0)\)

\(\hat{\theta}\) : 对参数 \(\theta\) 的估计值

\(P:\) 正样本集, Positive, 已标注的正样本

\(N\) : 负样本集, Negative, 实际末知

\(L\) : 有标签样本集, Labeled, 只有正样本

\(U\) : 无标签样本集, Unlabeled, 包括末知的正负样本

\(P U:\) 正样本和无标签样本组成的集合

\(R N\) : 可靠的负样本

评分卡基础—逻辑回归算法理解

风控业务背景

逻辑回归(Logistic Regression,LR)是建立信贷金融评分卡的重要模型,其具有形式简单、易于解释、鲁棒性强等优点。然而,很多建模同学并不是很清楚其原理。本文尝试对逻辑回归基础加以分析理解。

目录 Part 1. 从线性回归到逻辑回归 Part 2. 为什么采用sigmoid函数 Part 3. 利用极大似然估计法估计参数 Part 4. 最优化问题求解之梯度下降法 Part 5. 正则项的作用和种类 Part 6. 总结 致谢 版权声明 参考资料

一、从线性回归到逻辑回归

线性模型是指对各种属性进行线性加权组合的函数:

[公式]

这一过程将信息进行整合;不同的权重(weight)反映了自变量对因变量不同的贡献程度 。线性回归(Liner Regression)具有广泛应用,例如:预测房价、天气等等。

但在实际应用中,很多人会忽略线性回归的几大假设:

  • 零均值假设:随机误差项均值为0。
  • 同方差假设:随机误差项方差相同。若满足这一特性,称模型具有同方差性
  • 残差 [公式] 服从正态分布 [公式]
  • 无自相关假设:若不满足这一特性,称模型具有自相关性(Autocorrelation)。
  • ...

显然,线性回归的输出结果 [公式] 。那如果要做分类呢?我们就考虑将线性回归的输出与分类任务的真实标签 [公式]联系起来,即再找一个映射函数。

我们采用一个 [公式] 函数(也叫对数几率):

[公式]

其函数图像如图2所示,直观感受其优美的姿态,对称、平滑,且输出 [公式] .

img

我们尝试把 [公式] 函数模块拼接到线性回归的输出后面,如图3所示。

img

把图3用公式表达,也就是在 [公式] 函数内嵌套一个线性回归:

[公式]

我们再将其变换得到逻辑回归的另一种常见形式:

[公式]

为什么要这样做呢?这是因为右边就是线性回归,而左边则引入了 [公式] (几率) 的概念,即事件发生概率相对于不发生概率的比值。

显然可以得到正负样例的概率表达式:

[公式]

二、为什么采用sigmoid函数

根本原因:

函数优点:

至此,你可能会有疑问:为什么这里就直接选择了[公式] 函数?如果只是为了将输出结果从 [公式] 映射到 [公式] ,完全可以选择其他函数,比如单位阶跃函数:

[公式]

若预测值 [公式] 则判为正例, [公式] 则判为负例, [公式] 则可任意判别。你可能会说,这个阶跃函数不可微,也无法像[公式] 函数那样输出概率。这就冒出两个问题:

  1. 为什么这个映射函数一定要求可微?
  2. 为什么 [公式] 函数输出值可以代表概率?

首先,我们先分析 [公式] 函数的基本性质:

  1. 定义域:[公式]
  2. 值域:[公式]
  3. 函数在定义域内为连续和光滑函数
  4. 处处可导,导数为 [公式] ,以下是推导过程:
[公式]

可以看到,[公式] 函数确实具有很多优点,但这仍不是我们选择它的根本原因。这是因为,我们仍可以找到一些与之类似性质的函数。

根本原因:

由于逻辑回归本质上属于线性模型,我们尝试从广义线性模型(Generalized Linear Model,GLM)角度入手解释。前文提到,线性回归存在诸多假设,实际应用中往往无法满足。这就会有以下问题:

  • [公式] 的取值范围 [公式] 与某些场景矛盾。例如,要求 [公式] 。假设一个线性回归模型预测当温度下降10摄氏度,沙滩上的游客将减少1000人。那么,如果当前20摄氏度时,沙滩上只有50人,按此模型预测,当温度为10摄氏度时,沙滩上便有-950人。这显然不符合常理,因为人数不能为负数。
  • 残差 [公式] 服从正态分布 [公式] ,且要求方差 [公式] 是常数。但有时,均值 [公式] 越大,我们越预测不准确(方差 [公式] 越大)。

为了解决这些局限性,后人发展了GLM,用以提高线性模型的普适性。

GLM允许因变量 \(y\) 的分布并不一定要服从正态分布,而可以服从其它分布。

广义线性模型GLM由三要素组成, 即:

  • 概率分布 (Probability distribution) : 指因变量 \(y\) 的分布假设, 来自指数分布族。

  • 线性预测 (Linear predictor) : 自变量的线性组合, 即 \(\eta=\boldsymbol{X} \boldsymbol{\beta}\)

  • 链接函数 (Link function) : 通过均值 \(\mu\) 来链接前两者, 即 \(E(Y)=\mu=g^{-1}(\eta)\) image-20220708133222814

    首先分析概率分布。对于只有单个参数 \(\theta\) 的指数分布族的通用形式为: \[ f(x \mid \theta)=h(x) \exp (\eta(\theta) \cdot T(x)-A(\theta)) \] 其中, \(h(x)\)\(T(x)\) 只是关于自变量 \(x\) 的函数; \(\eta(\theta)\)\(A(\theta)\) 只是关于末知参数 \(\theta\) 的函数。 不同的线性模型具有不同的分布假设。比如:

  • 线性回归假设 \(y\) 的残差 \(\varepsilon\) 服从正态分布 \(N\left(\mu, \sigma^{2}\right)\)

  • 逻辑回归假设 \(y\) 服从伯努利分布 (Bernoulli)

接下来,我们尝试:

  1. 将逻辑回归因变量 \(y\) 变换到式 [公式] 的形式,确定以上几个函数,验证其属于指数分布族。
  2. 求解出逻辑回归对应的链接函数。注意,此时我们还没有认可sigmoid函数。⚠️

由于逻辑回归假设 \(y\) 服从伯努利分布(Bernoulli),即:

[公式]

对比式 [公式] 指数函数族的通用形式,我们发现:

  • [公式]
  • [公式]

这说明伯努利分布也是指数分布族(exponential family)的成员。按GLM的第二要素定义:

[公式]

我们再计算 [公式] 的反函数,就得到了[公式] 函数:

[公式]

按类似方法,我们可以推导出各分布函数及其链接函数,如图5所示。

img

从广义线性模型角度,我们确实推导出 [公式]函数与逻辑回归之间密不可分的联系。但是,sigmoid函数输出值为什么可以代表概率?

上文提到,逻辑回归中因变量 \(y\) 服从伯努利分布,而伯努利分布的参数 [公式] 的含义就是样例属于 [公式] 的概率。

三、利用极大似然估计法估计参数

在模型参数估计问题上,两大主流学派持有不同观点:

  • 频率主义学派(Frequentist): 认为参数虽然未知,但却是客观存在的固定值。因此,可通过优化似然函数等准则估计参数值。
  • 贝叶斯学派(Bayesian): 认为参数是未观察到的随机变量,其本身也可有分布。因此,可假定参数服从一个先验分布,再基于观察到的数据来计算参数的后验分布。

极大似然估计法(Maximum Likelihood Estimation,MLE)属于频率主义学派方法,其蕴含的朴素思想在于:

我们已经确定了一个模型种类 [公式] ,但还不清楚其真实参数 [公式] 。既然目前观察样本已经出现,那么就由果溯因,估计出一组参数 [公式] ,使得出现目前结果的可能性最大(优化目标),如图6所示。

由于一组样本中的所有样例是一个整体,因此我们将各样例的概率相乘(排列组合中的乘法原理)来得到我们的目标函数。

img

我们把第 [公式] 个样例的类别属于 [公式] 的概率记为: [公式] .

现在,我们有观测样本 [公式] ,那么似然函数为:

[公式]

其中,样例 [公式] 具有标签 [公式] 。右边为什么要写成这种形式呢?主要原因在于这是伯努利分布的常见形式。按正负样例分析,可以帮助你理解这个形式:

  • [公式] 时,[公式]
  • [公式] 时,[公式]

为便于求解,将连乘 [公式] 转为 [公式] ,我们对等式 [公式] 两边同取对数 [公式] ,写成对数似然函数:

[公式]

我们的优化目标是:

[公式]

认真考虑后,我们发现并没有其他约束项。(事实上,这里将蕴含正则项的思想)

四、最优化问题求解之梯度下降法

回到式 [公式] 这个问题中:

[公式]

我们不断重复这一过程:达到某个点 [公式] 后,继续计算下一个点 [公式]

[公式]

那么,这个迭代过程何时才能停止呢?一般满足以下任意条件即可:

  • 达到迭代次数上限: [公式]
  • 学习曲线变化很小:[公式] 小于阈值。

业务风控常用算法归纳

风控碎碎念:https://www.zhihu.com/column/c_1483570023214510080

求是汪在路上:https://www.zhihu.com/column/c_1156982447794233344

前言

本篇文章在这里主要是做一个综述性的文章简单介绍一下,做一个归纳性质的文章:风控最核心的算法,按我的理解可以分解为以下几类

  • 有监督的去评价用户各个场景的风险层级:欺诈类风险,信用类风险等;给用户一个评分,作为用户后续策略的基础;【LR、XGB、LGB】
  • 无监督的去挖掘用户的社区关系:社区发现类算法,最主要的适用于数据标签的扩散,通过用户的相似性或者用户的关联性,达到增加覆盖的目的
  • 发现数据中的异常值:往往用于日常的风险感知和监控之中,可以有最简单的箱线图,也可以有单分类的孤立森林;
  • 行为序列的发现:最简单的可以是频繁项集,复杂的可以用各类用于序列挖掘的深度学习模型,可以作为模型的一个特征,也可以作为风险行为的发现;
  • 内容风控中:往往从正则和规则开始,有复杂的深度学习模型来cover,这一块了解较少;

一、风控算法场景

1.1 用于用户风险评级的算法

这里不考虑冷启动阶段所涉及的一些专家规则和头脑风暴,一般评级往往存在于项目运行了一段时间,有了比较可靠的数据积累之后,这时候往往需要我们更精确的根据场景进行用户的划分,最为人熟知的便是信贷中常用的信用分的那一套东西,这一块的知识,我常常是看这个博主 求是汪在路上;这一块的算法或者说这一类的算法,这位大佬讲的已经十分清楚了,有兴趣的可以翻出来看看;

这一类的算法,主要是以有监督为准,难点主要集中在,正负样本不平衡,正负样本的选择,样本的观察期,表现期,观察点等的选择。一般涉及到的算法,主要以lr,xgb为主,那么在实际使用中,xgb和lightGBM因为其效果出色,特征工程的东西往往比较简单,往往在使用中用的更多一些。

这一类场景,往往目标相对明确,用途相对明确,做起来,工作量主要集中在特征工程上,一般来说特征工程做好了,这一类模型都可以做出还可以的效果;

1.2 用于社区挖掘的算法

社区挖掘,其实和聚类有点点像,也有点像推荐中的基于用户相似的推荐,本质就是将有关系,有联系的各类用户放到一起,用以达到用户分群的目的,这一块写的比较好的文章,我推荐:

  1. 怎么去找边与边之间的关联:

2、 社区挖掘的算法有哪些:

基本社区发现就是在于如何发现用户之间,边与边之间的关系,可以是直接关系可以是相似关系,然后将边连成图,进行图挖掘,一般来说一个庞大的图大多数会先选择做连通图,再到连通图下做二次挖掘,用以增速提效。前期的数据探索,我比较推荐gephi软件,如果你习惯用python,networkx也是很好的选择,你只需要定义好点和边,便可以大致看出数据的分布和图的密度,选择合适的算法进行愉快的聚类咯。

其实这一块,我觉得和推荐场景有一点点像,推荐是猜你喜欢,风控是猜你会干坏事,只是标签不一样,要做的事情是一样的,所有用于推荐相关的算法,也可以类比的用于风控,往往会有出其不意的效果

1.3 异常值相关的发现算法

异常值算法,其实和不平衡数据集(pu-learning)的解决方法类似,因为对于风控来讲,风控本身针对的用户在平台或者在用户群中,占比就非常非常低,部分场景可能连千分之一,万分之一都不到,对于大数据集来讲,本身就算是一种异常值了,所以往往异常值发现的算法,在实际使用当中,往往会有意想不到的好效果;

说到异常值发现,日常的监控可能是一个方面,比如同比环比,比例异常,用户群占比异常等;除此之外还有一些,xgbod,iforest的半监督或者单分类的模型可以用于此类场景,这类场景,推荐可以参考:

xiaobo Xie:异常检测用于不平衡数据的二分类

我个人觉得这是一类对于风控来讲非常好的思考方式,就是当我们很难刻画黑用户的特征的时候,我们能尽可能好的描述好白用户的特征,我们一样可以解决问题,这一类问题的模型解决也往往是朝着这个思路来做的;

1.4 行为序列相关的算法

行为序列作为风控行为挖掘的重要组成部分,在部分场景往往有着意想不到的作用,举个例子,电商场景,正常人从搜索到下单,往往有着复杂的序列,至少大部分用户会有复杂的序列,而黑灰产往往会点击某些固定的链接直接进入商详,或者搜索固定的词进入商品,下单购买一气呵成,而如果一个非营销类商品大多数聚集着这样的序列,可能往往就存在一些问题,如果这个问题深入探讨下去,便是行为序列的挖掘;

从简入深,可以有频繁项集(前后无关联,只要频繁反复出现某类行为即为有问题),往深了做,可以有rnn及其改进的lstm相关的算法,进行进一步的建模,可以更加准确。在业务前期,我们可以先用频繁项集提取一些有价值的规则,在满足前期需求之后,可以进一步使用模型对当前场景进行优化,已达到提升召回和准确的作用。

同样我也在这里推荐一篇知乎上的文章:

没什么大不了:浅谈行为序列建模

1.5 内容风控相关算法

这一部分是我的一个知识盲区,我自己大约只能做到正则,基于同义词的正则相关的策略,其余更深入的算法探索,我没有做过,也没有相关经验,也就不班门弄斧了,毕业以来做深度学习的东西比较少,理论大约停留在表面,不太深入,也没做过什么实践,就不赘述了;网上也没找到什么比较深入探讨的资料,如果有小伙伴推荐的话,我也希望可以评论或者私信告知,不甚荣幸。