前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >逻辑回归项目实战-附Python实现代码

逻辑回归项目实战-附Python实现代码

作者头像
阿黎逸阳
发布2020-09-08 16:36:14
4K0
发布2020-09-08 16:36:14
举报
文章被收录于专栏:阿黎逸阳的代码

记得刚工作的时候,用的第一个模型就是逻辑回归。虽然从大二(大一暑假参加系里建模培训,感谢知识渊博的老师把我带入模型的多彩世界!)就参加了全国大学生数学建模比赛,直到研究生一直在参加数学建模,也获了大大小小一些奖。

但是这些都是纸上谈兵,只要结果好、程序高级、图文美观,就能得到评审老师的青睐。和实际模型上线,真金白银实战还是有很大的区别。

逻辑回归已经在各大银行和公司都实际运用于业务,已经有很多前辈写过逻辑回归。

本文将从我实际应用的角度阐述逻辑回归的由来,致力于让逻辑回归变得清晰、易懂

逻辑回归又叫对数几率回归,是一种广义线性回归分析模型。虽然名字里有回归,但其实是分类模型,常用于二分类。

这篇文章是逻辑回归三部曲中的第三部,介绍sklearn库中逻辑回归参数的含义和使用方法,并给出项目实战的Python代码。

本文目录

  1. 在Python中如何实现逻辑回归建模 1.1 调用sklearn库 1.2 逻辑回归常用参数详解
  2. 逻辑回归建模项目实战 2.1 导入基本库并设置文件存放路径 2.2 导入待建模的数据 2.3 分析数据基本情况 2.4 用IV挑选变量 2.5 建立模型 2.6 把模型转成评分卡的形式

一、在Python中如何实现逻辑回归建模

1 调用sklearn库

代码语言:javascript
复制
from sklearn.linear_model import LogisticRegression as lr

2 逻辑回归常用参数详解

逻辑回归函数中有很多参数,可以根据自己的数据进行相应调整。如果觉得纯看参数解释会有点枯燥,可以先看本文第二部分项目实战,有需要的时候再回过头来看这部分。

代码语言:javascript
复制
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.liblinear:使用开源的liblinear库实现,内部使用坐标轴下降法来迭代优化损失函数,适用于小数据集。
  • 2.lbfgs:拟牛顿法的一种,利用损失函数二阶导数矩阵(海森矩阵)来迭代优化损失函数。
  • 3.newton-cg:牛顿法家族中的一种,利用损失函数二阶导数矩阵(海森矩阵)来迭代优化损失函数。
  • 4.sag:随机平均梯度下降,是梯度下降法的变种,和普通梯度下降法的区别是每次迭代仅仅用一部分的样本来计算梯度,适合于样本数据多的时候。
  • 5.saga:线性收敛的随机优化算法的的变种,适用于大数据集。

注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进行调整?下面举两个例子(只是为了举例说明,不是真实统计数据):

  • 1.样本高度失衡。比如在第三方支付公司的欺诈领域,由于欺诈商户是极少一的部分,绝大部分的商户是正常商户。在建立欺诈模型的时候,99901个商户是正常商户,99个商户是欺诈商户。即0.1%的商户是欺诈商户,99.9%的商户是正常商户。如果我们不考虑权重,把所有商户都预测成正常商户,那么模型的预测准确率为99.9%,但是这种预测结果是没有任何意义的,没有抓到任何欺诈商户。
  • 2.误分类代价很高。如果我们将欺诈商户分类为正常商户,可能会带来上万的损失。这时,在模型上我们可能愿意误判一些正常商户,让监控运营进行甄别,尽可能多地识别出欺诈商户,减少资金损失。

针对以上两种情况,我们可以选择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中导入最常使用的数据处理库,并设置数据的存放的地址。

代码语言:javascript
复制
# coding: utf-8
import os             #导入设置路径的库
import pandas as pd  #导入数据处理的库
import numpy as np   #导入数据处理的库
os.chdir('F:/微信公众号/Python/19.逻辑回归/项目实战数据')  #把路径改为数据存放的路径
os.getcwd()          #看下当前路径

2 导入待建模的数据

用pandas库导入待建模的csv格式数据。

代码语言:javascript
复制
data = pd.read_csv('testtdmodel.csv',sep=',',encoding='gb18030')

:由于数据中存在中文,如果不使用encoding对编码进行申明会报如下错误:

代码语言:javascript
复制
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 2: invalid start byte

把endcoding的值设置为gb18030或gbk可以解决此类问题,成功导入数据。

3 分析数据基本情况

3.1用head函数看下数据表头和前几行数据

我选择看前两行的数据,如果括号里为空默认展示前五行的数据,可以根据需要把2改为你想展示的行数。也可以用tail函数展示后几行数据。

代码语言:javascript
复制
data.head(2)

结果:

3.2用value_counts函数观测因变量y的数据分布

在信贷中,有些客户因为忘记了还款日期、或者资金在短期内存在缺口(不是恶意不还),可能会导致几天的逾期,在催收后会及时还款。

故一般不把历史逾期不超过x天(根据公司的实际业务情况和数据分析结果得出)的客户定义为坏客户(这里的坏不是坏人的意思,纯粹指逾期超过x天的客户)。

在本文把逾期超过20天的客户标签y定义为1(坏客户),没有逾期和逾期不超过20天的客户标签y定义为0(好客户)。

代码语言:javascript
复制
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%分位数、最大值等信息。

代码语言:javascript
复制
data.describe()

结果:

那根据describe得到的结果可以在建模中做什么?

  • 1.甄别变量是否存在缺失值。count的结果是该变量中非空值的个数,如果某个变量count值和样本个数不等,说明该变量存在缺失值,样本个数和count值的差值即为该变量缺失值个数。
  • 2.判断某个变量是否存在数据错误。由于describe中有变量最小值和最大值的信息,可以分析该值是否符合常识来判断变量是否正确。比如人的年龄不可能是个负数,如果年龄中存在负数说明该变量数据出现错误。
  • 3.分析样本在某个变量上是不是有集中性。比如上图中历史最高逾期天数这个变量,从describe结果可发现50%分位数的值为0,75%分位数的值为1,最大值为44。说明50%以上的客户从未逾期,75%的客户从未逾期或逾期不超过1天,说明在该变量上,样本集中在没有逾期这一档。
  • 4.分析好坏客户的样本分布是否差别很大。可以把数据集分成标签为1和0的子集,分别看子集中describe的结果。

对外拓展感觉有说不完的注意事项

,本文着重讲逻辑回归的实现,故后文不再在细枝末节处做过多说明。有小细节需要注意的地方,我会在后续文章中分专题详细阐述。

在变量缺失值处理好,并验证数据没有问题后。可以把数据集割分成训练集、测试集、验证集。

由于本文中的标签1只有97个,再细分可能出现1过少,导致信息过少,不满足统计特性。故不区分训练集、测试集、验证集,直接把所有数据都当成训练集。

接下来我们一起挑选重要变量,建立逻辑回归模型。

4 用IV挑选变量

挑选变量的方法多种多样,可以用IV、GBDT、随机逻辑回归、随机森林、逐步回归、cluster等等(我在建模中实际用到)。

本文采用信贷中最常用的方法IV进行阐述,其它方法会在本公众号后续挑选变量专题中进行详细阐述。

对于用IV挑选变量,在风控建模中的IV和WOE一文中有详细的阐述,感兴趣的可以仔细阅读一遍,本文只引用计算函数。

4.1 用等频分割的方法计算单个变量的IV值

等频计算IV的函数如下:

代码语言:javascript
复制
#等频切割变量函数
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

先用如下语句整理好原始自变量和因变量:

代码语言:javascript
复制
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值,并打印结果。

代码语言:javascript
复制
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情况:

代码语言:javascript
复制
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文件中:

代码语言:javascript
复制
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用挑选好的变量建立原始模型

代码语言:javascript
复制
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

注:本文为了不泄露公司的数据信息,挑选的变量已经经过数据替换,不是真实数据,只是为了数据展示。

得到结果:

可以用如下语句得到模型的系数和截距:

代码语言:javascript
复制
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-

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿黎逸阳的代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
批量计算
批量计算(BatchCompute,Batch)是为有大数据计算业务的企业、科研单位等提供高性价比且易用的计算服务。批量计算 Batch 可以根据用户提供的批处理规模,智能地管理作业和调动其所需的最佳资源。有了 Batch 的帮助,您可以将精力集中在如何分析和处理数据结果上。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档