Python数据科学:全栈技术详解1-个人贷款违约预测模型

浏览: 4546

Ben,多本数据科学畅销书作家,先后在亚信、德勤、百度等企业从事电信、金融行业数据挖掘工作。

配套学习教程:数据科学实战:Python篇 https://edu.hellobi.com/course/270


教科书中一般提供了建模使用的宽表,我们学习的是建立一个逻辑回归模型作预测。但是当我们面临许多张原始客户或帐户数据表时,很可能手足无措。建模的人都知道构建建模宽表(属于特征工程最重要的部分,但是和机器学习中常提到的变量扩增、变量压缩算法是两码事)是商业数据分析最难、最耗时、最考验数据科学家功底的环节。本案例使用一套真实的数据集为大家演示贷款违约预测模型开发的全流程。这个流程就是本书第1章介绍的数据挖掘项目的分析思路的直接体现,如图4-98所示。


4-98

1.1数据介绍

本数据为一家银行的个人金融业务数据集,可以作为银行场景下进行个人客户业务分析和数据挖掘的示例。这份数据中涉及到5300个银行客户的100万笔的交易,而且涉及700份贷款信息与近900张信用卡的数据。通过分析这份数据可以获取与银行服务相关的业务知识。例如,提供增值服务的银行客户经理,希望明确哪些客户有更多的业务需求,而风险管理的业务人员可以及早发现贷款的潜在损失。

账户表(Accounts):每条记录描述了一个账户(account_id)的静态信息,共4500条记录;

image.png 

顾客信息表(Clients:每条记录描述了一个客户(client_id)的特征信息,共5369条记录;

 image.png

权限分配表(Disp):每条记录描述了顾客(client_id)和账户(account_id)之间的关系,以及客户操作账户的权限,共5369条记录;

image.png 

支付订单表(Orders:每条记录代表描述了一个支付命令,共6471条记录;

 image.png

交易表(Trans):每条记录代表每个账户(account_id)上的一条交易,共1056320条记录;

 image.png 

贷款表(Loans):每条记录代表某个账户(account_id)的上的一条贷款信息,共682条记录;

 

image.png

 

信用卡(Cards):每条记录描述了一个顾客号的信用卡信息,共892条记录;

 

image.png

 

人口地区统计表(District):每条记录描述了一个地区的人口统计学信息,共77条记录。

image.png

 

实际业务中的一个人可以拥有多个账户号(account_id),一个账户号(account_id)可以对应有多个顾客(client_id),即多个顾客共享一个账户号(account_id),但是每个帐户号(account_id)的所有者(即最高权限者)只能是一人。账户号(account_id)与客户号(client_id)的对应关系,在表Disposition”中进行列示;表“Loan”为银行提供给账户号(account_id)的服务;表Credit card”为银行提供给的顾客(client_id)的服务,每个客户可以申请一张信用卡;贷款为基于账户的服务,一个账户(account_id)在一个时点最多只能有一笔贷款。

关系实体图(E-R图)可以直观的描述表间关系,如图4-99所示。图中将每张表的主键与外键通过实线相连接,可以明确指导我们如何将表进行横向连接。比如要知道贷款客户的性别,就需要使用贷款表(Loan)中的account_id先与权限分配表(Disposition)中的account_id连接,然后再拿client_id和客户表(Client)中的client_id连接。


4-99

1.2业务分析

在贷款审批方面,如果可以通过构建量化模型对客户的信用等级进行一定的区分。在信贷资金管理方面,得知了每个账户的违约概率后,可以预估一下未来的坏账比例,及时做好资金安排。也可以对违约概可能性较高的客户进行更加频繁的“关怀”,及时发现问题,以避免损失。

在这个量化模型中,被解释变量为二分类变量,因此需要构建一个排序类分类模型。而排序类分类模型中最长使用的算法是逻辑回归。

1.3数据理解

建模分析中,获取预测变量是最为艰难的。需要根据建模的主题进行变量的提取。其中第一步称为维度分析,即采集的数据涉及哪些大的方面。如下图所示:


有商业智能(BI)经验的人初次接触维度分析这个概念,会联想起联机分析处理(OLAP),这两个概念有相通之处。后者是为业务分析服务的,因此,将信息归结到公司各级部门这个粒度。比如某支行、分行或总行的某业务数量。数据挖掘的维度分析会更为广泛,是指将数据按照研究对象进行信息提取的工作。比如,如果需要研究的是个人客户的消费行为,则需要将数据以客户为粒度进行归结。这样问题就来了,银行保存的客户数据非常多,如何去整理这些数据以获取合适的信息,而且单独从内部获取数据是否可以满足分析的需求?为了回答这些问题,需要对研究的问题进行深入研究,以明确研究需要的客户信息。不同的分析主题需要的客户信息是不同的,以下提供了一个信息获取的框架,在实际操作中可以按照以下4个方面进行考虑。

1、属性表征信息:在分析个人客户时,也被称为人口统计信息。主要涉及最基本的性别、出生日期的信息。这类指标对客户的行为预测并不具有因果关系,只是根据历史数据统计而得到的规律。比如,随着客户年龄的提高,会对房贷、消费贷款、教育储蓄、个人理财等产品依次产生需求,但是年龄并不是对产品需求的根本原因,其实婚龄才是其原因。只不过婚龄和年龄在同时期人群中是高度相关罢了。同理,性别和某种业务表现的高相关性,很多也来自于外部世界对性别类型的一种行为期望。这类数据对于银行、汽车4S店这类需要客户临柜填写表格的公司而言是可以获取“真实”信息的,而对于电商而言,是难以获取“真实”信息的。但是电商的分析人员也不必气馁,其实“真实”这个概念是有很多内涵的,根据电商数据虽然不能知道客户人口学上的“真实”年龄,但是根据其消费行为完全可以刻画出其消费心理上的“真实”年龄,而后者在预测客户需求和行为方面更有效。

2、行为信息:行为是内部需求在外部特定环境下的一种表现。首先,行为是内部需求的结果。比如,活期存款的客户将手头的钱存起来,应付不时之需的需求。其次,这些行为是在特定环境下表现出来的,在活期理财产品推出之前,活期存款是唯一的选择。对于银行而言,行为数据仅限于业务数据,而电信公司可以获取的行为数据更加广泛,不仅可以获取通话行为、上网行为等业务信息,还可以获取周末出行、业余生活等个人行为信息。获取的客户行为信息越多,对客户的了解越深入。在这方面,各类企业都具有很大的深挖潜力。由于行为数据均为详细记录,数量庞大,而建模数据是一个样本只能有一条记录,因此需要对行为数据依照RFM方法进行行为信息的提取,比如过去一年的帐户余额就是按照”M”计算得到的,这类变量会称为一级衍生变量。这还不够,比如要看帐户余额是否有增长趋势,就要计算过去一年每月的平均帐户余额,然后计算前后两月平均帐户余额增长率的均值,这个变量就被称为二级衍生变量。行为信息的提取可以按照RFM方法做到三级甚至四级衍生变量。

3、状态信息:指客户的社会经济状态和社会网络关系。社会学认为,人之所以为特定的人,就在于其被固化在特定的关系之中,这被称为嵌入理论。了解客户的社会关系,就了解了外界对该客户的期望,进而推断出其需求。通过深入的分析,甚至可以推断出客户未来的需求,达到比客户更了解自己的状态。在这方面,有些企业走在了前面。比如,电信企业通过通话和短信行为确定客户的交友圈,通过信号地理信息定位客户的工作、生活和休闲区域,来推测其工作类型、所处阶层和社交网络类型。有些企业刚刚起步,只是通过客户住址大致确定一下客户居住小区的档次,以确定其社会经济地位。这类信息是值得每个以客户为中心的企业花时间和精力去深挖的。

4、利益信息:如果可以知道客户的内在需求,这当然是最理想的,而这类数据获取方式是很匮乏的。传统方式只能通过市场调研、客户呼入或客户投诉得到相关数据。现在利用客服、微信公众号、微博、论坛等留言信息,可以便捷的获取客户评价信息。


4-100

以上的构建变量的准则是放之四海而皆准的,而具体到违约预测的这个主题种,还需要更有针对性的分析,以往的研究认为影响违约的主要因素有还款能力不足和还款意愿不足两个方面。还款意愿不足有可能是欲望大于能力、生活状态不稳定。以上是概念分析,之后就需要量化,比如使用“资产余额的变异系数”作为生活状态不稳定的代理指标。

在建模过程中,有预测价值的变量基本都是衍生变量,比如:

一级衍生,比如最近一年每月的资产余额均值来自于交易数据中每月的帐户余额;

二级衍生,比如年度资产余额的波动率来自于每月的资产余额均值;

三级衍生,比如资产余额的变异系数来自资产余额的波动率除以资产余额均值。

大数据元年之前,在数据科学中有一个不成文的规定:如果一个模型中二级以上的衍生变量不达到80%以上的比例,是不好意思提交成果的。老牌数据分析强公司(比如美国的第一资本)基本上是以每人每月约20-50个衍生变量的速度积累十年才拥有所谓的万级别的衍生变量。而2016年兴起的所谓智能自学习建模,宣称使用原始指标放入模型建模。笔者认为是伪数据科学家大量涌入到这个行业而兴起的一股逆流,就像房地产火热时期许多投机者涌入造成了建筑质量的大幅度下滑。这个现象必将会回归到以分析作为根底的本源,而非炫耀技术。

 

1.4数据整理

以上介绍了维度分析要注意的主要方面,下面将根据维度分析的框架创建建模用变量。不过我们不要期望可以创建起全部四个维度的变量,一般前三个维度足矣。首先生成被解释变量。在贷款(Loans)表中还款状态(status)变量记录了客户的贷款偿还情况,其中A代表合同终止且正常还款,B代表合同终止但是未还款,C代表合同未结束且正常还款,D代表合同未结束但是已经拖欠贷款了。我们以此构造一个客户行为信用评级模型,以预测其他客户贷款违约的概率。

1、数据提取中的取数窗口

我们分析的变量按照时间变化情况可以分为动态变量和静态变量,属性变量(比如性别、是否90后)一般是静态变量,行为、状态和利益变量均属于动态变量。动态变量还分为时点变量和区间变量,状态变量(比如当前帐户余额、是否破产)和利益变量(对某产品的诉求)均属于时点变量。行为变量(存款频次、平均帐户余额的增长率)为区间变量。在建造模型过程中,需要按照下图所示的取数窗口提取变量。其中有两个重要的时间窗口:观察窗口,预测窗口。观察窗口是观测和收集供分析的自变量的时间段。预测窗口是观测因变量变化的时间段,如果在这个时间段中出现显性状态(比如出现贷款拖欠)则设置为”1”,如果始终没有出现,则设置为”0”

 image.png

取数窗口期的长短和模型易用性是一对矛盾: 窗口期越短,缺失值越少、可分析的样本就越多、越便于使用。但是区间变量单个变量的观测期越短,数据越不稳定,这样难以获得稳健的参数。但是取数窗口期越长,新的客户就因为变量缺失而无法纳入到研究样本。因此取数窗口的长短是需要根据建模面临的任务灵活调整。本案例中的观测窗口定为一年。

同样的,预测窗口可长可短,取决于构建什么样的模型,以及目标变量是什么。通常,越长的预测窗口样本量越少。然而,预测窗口过短则有些客户的最终结果还没有表现出来,即在这种情况下,模型的所预测的结果显现之时,现实中的商业还未有足够时间做出反应。本书并非标准的信用评级的教科书,因此没有严格按照信用评级模型的取数窗口进行设置,需要深入学习的读者请参考《信用风险评分卡研究:基于SAS的开发与实施》。

2、利用pandas导入可用于建模的样本数据,利用Loans表生成被解释变量。如图4-1014-402所示。

 

image.png

image.png

4-101

以下为创建被解释变量。

image.png 

4-102

3、表征信息:将所有维度的信息归结到贷款表(LOANS)上,每个贷款帐户只有一条记录。寻找有预测能力的指标。首先是客户表征信息如性别,年龄。客户的人口信息保存在客户信息表(ClIENTS)表中,但是该表是以客户为主键的,需要和权限分配表(DISP)相连接才可以获得账号级别的信息,连接条件如图4-103和图104所示。

 

image.png

4-103

image.png

4-104

4、状态信息:提取借款人居住地情况,如居住地失业率等变量。与district表进行连接。

 

image.png

4-104

5、行为信息:根据客户的帐户变动的行为信息,考察借款人还款能力,如账户平均余额、余额的标准差、变异系数、平均入账和平均支出的比例、贷存比等。

首先将贷款表和交易表按照account_id内连接。

 image.png

然后将来自贷款表和交易表中的两个字符串类型的日期变量转换为日期,为窗口取数作准备。

 image.png

帐户余额和交易额度这两个为字符变量,有千分位符,需要进行数据清洗,并转化为数值类型。

image.png 

以下这个语句实现了窗口取数,只保留了贷款日期前365天至贷款前1天内的交易数据。

image.png 

以下语句计算了每个贷款帐户贷款前一年的平均帐户余额(代表财富水平)、帐户余额的标准差(代表财富稳定情况)和变异系数(代表财富稳定情况的另一个指标)。

image.png 

以下语句计算平均入账和平均支出的比例。首先按照上一步时间窗口取数得到的数据集,按照每个帐户的“借-贷”类型分别汇总交易金额。

 image.png

上一步汇总后的数据,每个帐号会有两条记录,需要将其进行拆分列操作,将每个帐户两条观测转换为每个帐户一条观测。以下语句中使用pd.pivot_table函数进行堆叠列。

image.png 

以下语句将分别计算的平均帐户余额、帐户余额的标准差、变异系数、平均入账和平均支出的比例等变量与之前的data3数据合并。

image.png 

最后计算贷存比、贷收比。

 image.png

 

1.5建立分析模型

这部分是从信息中获取知识的过程。数据挖掘方法分为分类和描述两大类,其中预测账户的违约情况属于分类模型。使用逻辑回归对刚才创建的数据建立模型,建模语句如下。

1)提取状态为C的用于预测。其它样本随机抽样,建立训练集与测试集。

 image.png

2)使用向前逐步法进行逻辑回归建模

 image.png

通过以上语句得到结果。以下列出了逻辑回归的模型参数表。其中申请贷款前一年的贷存比(r_lb)、存款余额的标准差(stdev_balance)、贷款期限(duration)与违约正相关。存款余额的均值(avg_balance)、贷款者当地1000人中有多少企业家(A14)与违约负相关。以上这些回归系数的正负号均符合我们的预期,而且均显著。

 image.png

3)模型效果评估

以下使用测试数据进行模型效果评估。此处调用了sklearn的评估模块绘制ROC曲线。

 image.png

可以看到模型的ROC曲线非常接近左上角,其曲线下面积(AUC)为0.9435,这说明模型的排序能力很强。

 image.png

 

1.6模型运用

在这个案例中,贷款状态为”C”的帐户是尚没有出现违约的合同未到期客户。这些贷款客户中有些人的违约可能性较高,需要业务人员重点关注。一旦发现问题时,可以及时处理,挽回损失。可以通过以下语句得到每笔贷款的违约概率

 image.png

这里需要强调的是,此处的概率仅是代表违约可能性的相对值,并不代表其真实违约概率。比如预测“概率”为0.77的违约可能性高于0.46,这已经足够了,因为业务人员知道哪些客户为重点关注的即可。

 image.png

1.7流程回顾

本例中,我们遵照数据挖掘项目通用的流程:CRISP-DM进行建模。最后回顾一下本案例的业务整理课表构建的流程。

1.业务分析:需要构建一个分类模型预测每个客户的违约概率,其实是对客户的信用进行一个排序。分类模型有很多种,其中逻辑回归是最常用到的。

2. 数据解读:从业务需求出发,了解、熟悉现有的数据结构、数据质量等信息。主要寻找对客户违约成本、还款意愿、还款能力(资产规模和稳定性)有代表意义的变量

3. 数据准备:结合数据的内在价值与业务分析,提取各类有价值的信息,构建被解释变量和解释变量。

4. 模型构建与评价:按照SEMMA标准算法,即数据抽样、变量分布探索、修改变量、构建逻辑回归、评价模型的优劣。

5.模型监控:当模型上线后,对模型的表现进行长期监控,主要检验模型预测准确性与数据的稳定性。

由于本章的重点在于介绍建模之前的宽表构建的部分,关于模型构建的细致化会在其他章节详细讲解,本章节此部分只提供一个粗略的流程。

其实在实际的工作中,上面提供的流程1-3的部分并不一定一次性做好,很多时候这部分是需要多次的反复验证、反复解读的。因为我们往往需要多次的分析审核,才能较好的理解拿到的数据、并且能够识别出数据中的异常或错误的内容,而此部分若纳入了错误的数据则会导致后面的如建模等工作完全没有意义。

 

 

推荐 3
本文由 Python爱好者社区 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

3 个评论

1.5 2)使用向前逐步法进行逻辑回归建模中的forward_select函数是怎么来的?
def forward_select(data, response):
import statsmodels.api as sm
import statsmodels.formula.api as smf
remaining = set(data.columns)
remaining.remove(response) # 移除 response 名
selected = []
current_score, best_new_score = float('inf'), float('inf') # 初始化分值都为无究大
while remaining:
aic_with_candidates=[] # aic分值存储的列表容器,越小越好
for candidate in remaining: # 对于保留的每个候选变量
formula = "{} ~ {}".format(
response,' + '.join(selected + [candidate]))
aic = smf.glm(
formula=formula, data=data,
family=sm.families.Binomial(sm.families.links.logit)).fit().aic
aic_with_candidates.append((aic, candidate)) # 将aci分值及对应的特征以元组的方式放置到aci列表框中
aic_with_candidates.sort(reverse=True)
best_new_score, best_candidate=aic_with_candidates.pop()# 移除最后一项,分值与特征分别赋给对应的变量
if current_score > best_new_score:
remaining.remove(best_candidate)
selected.append(best_candidate)
current_score = best_new_score
print ('aic is {},continuing!'.format(current_score))
else:
print ('forward selection over!')
break

formula = "{} ~ {} ".format(response,' + '.join(selected))
print('final formula is {}'.format(formula))
model = smf.glm(
formula=formula, data=data,
family=sm.families.Binomial(sm.families.links.logit)
).fit()
return(model)
向您学习!

要回复文章请先登录注册