记得刚工作的时候,用的第一个模型就是逻辑回归。虽然从大二(大一暑假参加系里建模培训,感谢知识渊博的老师把我带入模型的多彩世界!)就参加了全国大学生数学建模比赛,直到研究生一直在参加数学建模,也获了大大小小一些奖。
但是这些都是纸上谈兵,只要结果好、程序高级、图文美观,就能得到评审老师的青睐。和实际模型上线,真金白银实战还是有很大的区别。
逻辑回归已经在各大银行和公司都实际运用于业务,已经有很多前辈写过逻辑回归。
本文将从我实际应用的角度阐述逻辑回归的由来,致力于让逻辑回归变得清晰、易懂。
逻辑回归又叫对数几率回归,是一种广义线性回归分析模型。虽然名字里有回归,但其实是分类模型,常用于二分类。
这篇文章是逻辑回归三部曲中的第三部,介绍sklearn库中逻辑回归参数的含义和使用方法,并给出项目实战的Python代码。
本文目录
一、在Python中如何实现逻辑回归建模
1 调用sklearn库
from sklearn.linear_model import LogisticRegression as lr
2 逻辑回归常用参数详解
逻辑回归函数中有很多参数,可以根据自己的数据进行相应调整。如果觉得纯看参数解释会有点枯燥,可以先看本文第二部分项目实战,有需要的时候再回过头来看这部分。
LogisticRegression( solver='lbfgs', penalty='l2', class_weight=None, tol=0.0001, random_state=None, C=1.0, fit_intercept=True, intercept_scaling=1, dual=False, max_iter=100, multi_class='auto', verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)
参数说明:
solver:求解逻辑回归损失函数对应参数的优化算法。str类型,有以下五种算法可以选择,默值为'lbfgs'。
注1:对于常见的多元逻辑回归(OvR)和(MvM),一般(MvM)比(OvR)分类相对准确一些,但是liblinear只支持(OvR)不支持(MvM)。这意味着我们需要相对精确的多元逻辑回归时,不能选择liblinear,从而也不可以使用优化算法只能选择liblinear的L1正则。
注2:sag每次仅仅使用了部分样本进行梯度迭代,所以当样本量少的时候最好不要选择它。而如果样本量非常大,比如大于10万,sag是第一选择。但是sag不能用于L1正则化(没有连续导数)。如果你有大量的样本,同时模型的特征非常多,想要用L1正则化让模型系数稀疏化,这时就需要进行取舍。要么通过对样本采样来降低样本量 ,要么通过特征筛选提前挑选出重要变量,要么回到L2正则化。
penalty:为了解决过拟合问题,给模型加一个惩罚项。可选{'l1','l2', 'elasticnet','none'},默认值为'l2',如果选择'none'是不加惩罚项。
如果模型的特征非常多,我们希望一些不重要的特征系数归为零,从而让模型系数稀疏化的话,可以使用L1正则化(这个可以考虑用在变量挑选,把使用L1正则化系数为0的变量剔除掉)。
penalty参数的选择会影响我们损失函数优化算法的选择。即参数solver的选择。
如果是L1正则化,只能选择'liblinear'。这是因为L1正则化的损失函数不是连续可导的,而使用{'newton-cg','lbfgs','sag'}这三种优化算法时都需要损失函数的一阶或者二阶连续导数,而'liblinear'并没有这个依赖。
如果是L2正则化,4种算法{'newton-cg','lbfgs', 'liblinear', 'sag'}都可以选择。
class_weight:由于样本不平衡,导致样本不是总体样本的无偏估计,使得模型的预测能力下降。
为解决样本不均衡问题,可通过调节样本权重,使某种类型的样本量越多则权重越低,样本量越少则权重越高。可选参数为{dict,'balanced'},默认值为None,即不调节样本权重。
当class_weight为balanced时,类权重的计算方法为:n_samples/(n_classes*np.bincount(y)),其中n_samples为样本数,n_classes为类别数,np.bincount(y)会输出每个类的样本数。
比如当样本标签为[1,1,0,0,0,0,0,0,0,0]时,n_samples为10,n_classes为2,np.bincount(y)为[2,8],类权重为10/(2*[2,8]),结果为[2.5,0.625]。即1的样本权重为2.5,0的样本权重为0.625,从而可以让标签数量少的样本占更高的权重。
当class_weight为自定义时,对于0,1二元模型,我们可以定义class_weight={0:0.2,1:0.8},这样类型0的权重为20%,类型1的权重为80%。
什么情况下要对class_weight进行调整?下面举两个例子(只是为了举例说明,不是真实统计数据):
针对以上两种情况,我们可以选择balanced,或者自己确定各个类别的权重,让类库自动提高数量少的样本的权重。
提高了某种分类的权重,相比不考虑权重,会有更多的样本分类划分到高权重的类别,从而可以解决上述问题。
注1:建模时使用balance这个参数,对于两端的数据可能会比较均匀。即不使用balance参数在高分区出现的坏样本占比可能会高于使用balance参数的模型(可能和行业数据有关)。如果想要在高分段准确率更高,要慎用balance参数。
注2:调节样本权重的方法有两种,第一种是在class_weight中使用balanced。另一种是在调用fit函数时,通过sample_weight来自己调节每个样本权重。那么样本的真正权重是:class_weight*sample_weight。
tol:迭代终止的条件。float类型,默认值为0.0001。
比如我们想要求取使得损失函数最小化的参数θ,现用梯度下降法进行求解,每迭代一次,损失函数的值都会减少一点,当迭代前后损失函数的差值小于0.0001时,迭代停止。
random_state:随机数种子。int类型,默认为None。仅在正则化优化算法为sag、liblinear时使用。
比如要随机产生一个初始值,可以设置种子的值为20,那么每次随机产生的值都是20这个种子对应的值,可以用此方法复现建模时的数据。
C:正则化系数的倒数。float类型,默认值为1.0,该值越小正则化越强。
fit_intercept:确定是否有一个常数项(截距项)应该添加到逻辑函数中的线性表达式中。bool类型,默认值为True。
intercept_scaling:仅在正则化项为‘liblinear’且fit_intercept设置为True时有用。float类型,默认值为1。
二、逻辑回归模型项目实战
项目背景:由于公司发展车贷业务,需要判断新进来的申请人有多大的概率会逾期,根据逾期的概率和资金的松紧程度决定是否放贷。
现在有一批历史上是否违约的客户样本数据(由于数据涉及安全问题,也是职业操守要求,故此数据不是原始数据,是经过处理的)。
想根据这批历史数据训练逻辑回归模型,得到模型参数,预测未来新申请的客户逾期概率。从而决定新申请人是通过、转人工核验还是拒绝。
1 导入基本库并设置文件存放路径
从Python中导入最常使用的数据处理库,并设置数据的存放的地址。
# coding: utf-8
import os #导入设置路径的库
import pandas as pd #导入数据处理的库
import numpy as np #导入数据处理的库
os.chdir('F:/微信公众号/Python/19.逻辑回归/项目实战数据') #把路径改为数据存放的路径
os.getcwd() #看下当前路径
2 导入待建模的数据
用pandas库导入待建模的csv格式数据。
data = pd.read_csv('testtdmodel.csv',sep=',',encoding='gb18030')
注:由于数据中存在中文,如果不使用encoding对编码进行申明会报如下错误:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 2: invalid start byte
把endcoding的值设置为gb18030或gbk可以解决此类问题,成功导入数据。
3 分析数据基本情况
3.1用head函数看下数据表头和前几行数据
我选择看前两行的数据,如果括号里为空默认展示前五行的数据,可以根据需要把2改为你想展示的行数。也可以用tail函数展示后几行数据。
data.head(2)
结果:
3.2用value_counts函数观测因变量y的数据分布
在信贷中,有些客户因为忘记了还款日期、或者资金在短期内存在缺口(不是恶意不还),可能会导致几天的逾期,在催收后会及时还款。
故一般不把历史逾期不超过x天(根据公司的实际业务情况和数据分析结果得出)的客户定义为坏客户(这里的坏不是坏人的意思,纯粹指逾期超过x天的客户)。
在本文把逾期超过20天的客户标签y定义为1(坏客户),没有逾期和逾期不超过20天的客户标签y定义为0(好客户)。
data.y.value_counts()
结果:
本文总计样本数量为7252,其中7155个样本是好客户,97个样本是坏客户。说明0和1的分布很不均匀,我们统计一下占比:
发现0的占比达到了98.6%,1的占比不到2%,这是典型的样本不均衡问题。
如果我们把所有的客户都预测成好客户,模型可以达到98.6%的准确率。但是这个结果是毫无意义的,因为模型没起到区分逾期和非逾期客户的作用,没有达到建模的目的。
可以用前文提到的class_weight和sample_weight对权重进行调整,让标签是1的样本占更多的权重。
实际中很多问题可能都是数据分布不均匀的,比如患病的人数和不患病的人数、欺诈商户和正常商户、逾期的客户和不逾期的客户等等。
不过这些都是正常现象,想一想如果逾期客户数超过了不逾期的人数,公司早就倒闭了好吧
。
那么建模的目的就是想把钱尽可能多地贷给能按期还钱的客户,尽可能多地拒绝可能会逾期的客户。这样公司的钱才能挣得更多的利息,产生更少的坏账,总收益才能最大化。
3.3用describe函数查看数据分布
一般建模之前要看下数据的缺失情况、检查一下数据是不是存在错误。
如果数据存在缺失值考虑删除这个样本,或用0值或中位数等统计变量去填充。如果删除存在缺失值的样本,模型用到的信息量就会减少。
如果数据存在错误,考虑从源头纠正错误数据或删除该样本。
在Python中可以用describe()函数方便地获取样本数量,变量均值,变量最小值、25%分位数、50%分位数、75%分位数、最大值等信息。
data.describe()
结果:
那根据describe得到的结果可以在建模中做什么?
对外拓展感觉有说不完的注意事项
,本文着重讲逻辑回归的实现,故后文不再在细枝末节处做过多说明。有小细节需要注意的地方,我会在后续文章中分专题详细阐述。
在变量缺失值处理好,并验证数据没有问题后。可以把数据集割分成训练集、测试集、验证集。
由于本文中的标签1只有97个,再细分可能出现1过少,导致信息过少,不满足统计特性。故不区分训练集、测试集、验证集,直接把所有数据都当成训练集。
接下来我们一起挑选重要变量,建立逻辑回归模型。
4 用IV挑选变量
挑选变量的方法多种多样,可以用IV、GBDT、随机逻辑回归、随机森林、逐步回归、cluster等等(我在建模中实际用到)。
本文采用信贷中最常用的方法IV进行阐述,其它方法会在本公众号后续挑选变量专题中进行详细阐述。
对于用IV挑选变量,在风控建模中的IV和WOE一文中有详细的阐述,感兴趣的可以仔细阅读一遍,本文只引用计算函数。
4.1 用等频分割的方法计算单个变量的IV值
等频计算IV的函数如下:
#等频切割变量函数
def bin_frequency(x,y,n=10): # x为待分箱的变量,y为target变量.n为分箱数量
total = y.count() #1 计算总样本数
bad = y.sum() #2 计算坏样本数
good = total-bad #3 计算好样本数
if x.value_counts().shape[0]==2: #4 如果该变量值是0和1则只分两组
d1 = pd.DataFrame({'x':x,'y':y,'bucket':pd.cut(x,2)})
else:
d1 = pd.DataFrame({'x':x,'y':y,'bucket':pd.qcut(x,n,duplicates='drop')}) #5 用pd.cut实现等频分箱
d2 = d1.groupby('bucket',as_index=True) #6 按照分箱结果进行分组聚合
d3 = pd.DataFrame(d2.x.min(),columns=['min_bin'])
d3['min_bin'] = d2.x.min() #7 箱体的左边界
d3['max_bin'] = d2.x.max() #8 箱体的右边界
d3['bad'] = d2.y.sum() #9 每个箱体中坏样本的数量
d3['total'] = d2.y.count() #10 每个箱体的总样本数
d3['bad_rate'] = d3['bad']/d3['total'] #11 每个箱体中坏样本所占总样本数的比例
d3['badattr'] = d3['bad']/bad #12 每个箱体中坏样本所占坏样本总数的比例
d3['goodattr'] = (d3['total'] - d3['bad'])/good #13 每个箱体中好样本所占好样本总数的比例
d3['WOEi'] = np.log(d3['badattr']/d3['goodattr']) #14 计算每个箱体的woe值
IV = ((d3['badattr']-d3['goodattr'])*d3['WOEi']).sum() #15 计算变量的iv值
d3['IVi'] = (d3['badattr']-d3['goodattr'])*d3['WOEi'] #16 计算IV
d4 = (d3.sort_values(by='min_bin')).reset_index(drop=True) #17 对箱体从大到小进行排序
cut = []
cut.append(float('-inf'))
for i in d4.min_bin:
cut.append(i)
cut.append(float('inf'))
WOEi = list(d4['WOEi'].round(3))
return IV,cut,WOEi,d4
先用如下语句整理好原始自变量和因变量:
columns_x =[
'7天内申请人在多个平台申请借款',
'1个月内申请人在多个平台申请借款',
'3个月内申请人在多个平台申请借款',
'7天内借款人手机申请借款平台数',
'1个月内借款人手机申请借款平台数',
'3个月内借款人手机申请借款平台数',
'7天内借款人身份证申请借款平台数',
'1个月内借款人身份证申请借款平台数',
'3个月内借款人身份证申请借款平台数',
'7天内关联P2P网贷平台数',
'1个月内关联P2P网贷平台数',
'3个月内关联P2P网贷平台数',
'7天内申请人关联融资租赁平台数',
'1个月内申请人关联融资租赁平台数',
'3个月内申请人关联融资租赁平台数',
'1个月内申请人关联一般消费分期平台数',
'3个月内申请人关联一般消费分期平台数',
'风险名单占比',
'一度关联节点个数',
'二度关联节点个数',
'一度风险名单个数',
'二度风险名单个数',
'一度风险名单占比',
'二度风险名单占比',
'X3个月内申请人手机号作为第二联系人手机号出现的次数',
'X3个月内申请人手机号作为前三联系人手机号出现的次数',
'是否命中法院执行模糊名单',
'是否命中法院结案模糊名单',
'是否命中手机风险关注名单',
'是否命中身份证风险关注名单',
'命中中风险关注名单笔数',
'客户异常借款笔数',
'信用异常笔数',
'执行标的',
'申请人执行标的是否超过100000',
'3个月手机号关联身份证数',
'3个月身份证关联手机号数',
'三个月银行相关平台数'
] #自变量名称
X = data[columns_x] #生成自变量数据框
Y = data['y'] #生成因变量y
再用调用等频法算IV的函数,计算单个变量的IV值,并打印结果。
IV,cut,WOEi,d4 = bin_frequency(X['1个月内申请人在多个平台申请借款'], Y)
print('IV=',IV)
d4
得到结果:
4.2 用等频分割的方法批量计算IV值
在变量one-by-one分析之前可以用等频切分的方法,快速算出所有变量的IV值,一般挑选IV值大于0.02的变量进入后续建模。
可以用如下语句批量计算变量的IV值,并打印每个变量的分箱woe情况:
ivs=[]
for i in columns_x:
print(i)
IV,cut,WOEi,d4 = bin_frequency(X[i], Y)
print('IV=', IV)
ivs.append(IV)
print(d4)
得到结果:
可以用如下语句把所有变量的IV值汇总输出到csv文件中:
IVi=pd.DataFrame({'name':columns_x, 'IV':ivs}).sort_values('IV',ascending = False)
IVi.to_csv("IV.csv")
得到结果(部分截图):
在用IV值大于0.02的规则挑选完变量后可以用相关性、vif、逐步回归等方法进一步挑选变量。
注:IV值多少可以进入模型,需要根据公司的数据情况进行调整,一般IV值低于0.02的变量几乎没有预测效果。如果很多变量的IV值都很高,阈值可以相应提高。如果变量整体的IV值都不高,根据业务逻辑也可以加一些IV值不是很高的变量测试模型效果。
挑选好变量后可以调用sklearn中的lr函数建模。
5 建立模型
5.1用挑选好的变量建立原始模型
columns_model = ['1个月内借款人身份证申请借款平台数','7天内关联P2P网贷平台数','3个月内关联P2P网贷平台数','3个月手机号关联身份证数','3个月内申请人关联融资租赁平台数','二度风险名单个数','是否命中身份证风险关注名单','原始分','一度风险名单个数']X_model = data[columns_model] #生成入模自变量y = data['y'] #生成入模因变量 from sklearn.linear_model import LogisticRegression as lr #导入逻辑回归库lr_model_1 = lr() #调用逻辑回归lr_model_1_y = lr_model_1.fit(X_model, y) #用样本数据训练逻辑回归模型y_proba_model_1 = lr_model_1_y.predict_proba(X_model) #用训练好的模型预测y_proba_model_1
注:本文为了不泄露公司的数据信息,挑选的变量已经经过数据替换,不是真实数据,只是为了数据展示。
得到结果:
可以用如下语句得到模型的系数和截距:
lr_model_1_coef = pd.DataFrame(lr_model_1_y.coef_)
lr_model_1_coef.columns = columns_model
得到结果:
把拟合好的系数和截距代入逻辑回归函数中,得到的结果就是逾期的概率值。
以上逻辑回归模型的训练直接采用默认参数,没有考虑样本不均衡的问题,可以在逻辑回归模型中引入参数class_weight,即lr(class_weight='balanced')让因变量1的权重变高,0的权重变低。
5.2把挑选好的变量转成woe再建立模型
在建完原始模型后一般要把变量转成woe后,再用逻辑回归训练一次变量,得到相应的系数。因为把变量转成woe后变量具有更好的鲁棒性,模型会更加稳定。
比如年龄这个变量,如果不分箱转成woe,在客户从25岁变到26岁时评分可能完全不一样。而转成woe后变量是一个一个的小箱子,在箱子内值的变动不会对模型分产生影响。如果25到26刚好在一个箱子里,这个人的评分不会因为年龄的改变而发生变化。
由于篇幅问题,在本文中只给转出转woe后建模的结果:
6 把模型转成评分卡的形式
把变量转成woe后,可以根据分箱情况和逻辑回归的结果,通过转换把变量变成评分卡的形式:
其实在建模过程中,特征工程、数据准备、数据预处理、变量筛选、模型建立、模型评价、模型上线都是至关重要的。所有小的模块都会在后续文章中陆续发布,欢迎持续关注。
为了更清晰地理解逻辑回归的求解和使用,建议对逻辑回归的推导过程有一个大致的了解。而且理解了逻辑回归的原理,有助于其它机器学习模型的学习。
一直对逻辑回归,这个风控建模中必不可少的一员,抱有很大的敬意。逻辑回归三部曲的文章也从2月份断断续续准备到了4月份,今天终于和大家分享第三部-逻辑回归项目实战,如有偏颇的地方,或者有不懂的地方,大家可以加我的微信‘阿黎逸阳’进行咨询。
本文纯属技术分享文档,帮助想要从事风控建模的朋友了解逻辑回归,所有文字都非商业用途,如果需要本文项目实战的数据可以到公众号中私信我,可以免费获取。
之前由于工作需要,自己看了很多资料把逻辑回归的结果转成了评分卡的形式,写完了Python的实现函数,在接下来的时间我会好好总结评分卡的内容,争取评分卡的文章也早日和大家见面。
参考文献
https://blog.csdn.net/lc574260570/article/details/82116197 https://blog.csdn.net/laobai1015/article/details/80512849https://blog.csdn.net/weixin_41712499/article/details/82526483https://blog.csdn.net/qq_27972567/article/details/81949023?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-4&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-4
-end-