分类问题经常用逻辑回归,模型简单,容易训练,可解释性强。

逻辑回归既可以看作是分类算法,也可以看作是回归算法。通常作为分类算法用,只能解决二分类问题

 

LightGBM 概念:

LigthGBM是boosting集合模型中的新进成员,由微软提供,它和XGBoost一样是对GBDT的高效实现,原理上它和GBDT及XGBoost类似,都采用损失函数的负梯度作为当前决策树的残差近似值,去拟合新的决策树。

 

LightGBM相比XGBOOST在原理和性能上的差异?

1.速度和内存上的优化:

        xgboost用的是预排序(pre-sorted)的方法, 空间消耗大

  • 这样的算法需要保存数据的特征值,还保存了特征排序的结果(例如排序后的索引,为了后续快速的计算分割点),这里需要消耗训练数据两倍的内存。
  • 其次,时间上也有较大的开销,在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。

LightGBM用的是直方图(Histogram)的决策树算法,直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。在遍历数据的时候,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。

 

2.准确率上的优化:

  • xgboost 通过level(depth)-wise策略生长树, Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。
  • LightGBM通过leaf-wise(best-first)策略来生长树, Leaf-wise则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。

 

3.对类别型特征的处理

  • xgboost不支持直接导入类别型变量,需要预先对类别型变量作亚编码等处理。如果类别型特征较多,会导致哑变量处理后衍生后的特征过多,学习树会生长的非常不平衡,并且需要非常深的深度才能来达到较好的准确率。
  • LightGBM可以支持直接导入类别型变量(导入前需要将字符型转为整数型,并且需要声明类别型特征的字段名),它没有对类别型特征进行独热编码,因此速度比独热编码快得多。LightGBM使用了一个特殊的算法来确定属性特征的分割值。基本思想是对类别按照与目标标签的相关性进行重排序,具体一点是对于保存了类别特征的直方图根据其累计值(sum_gradient/sum_hessian)重排序,在排序好的直方图上选取最佳切分位置。

 

 逻辑回归构建评分卡

import pandas as pd
from sklearn.metrics import roc_auc_score,roc_curve,auc
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.linear_model import LogisticRegression
import numpy as np
import random
import math #import math :  导入这个math模块,就可以用模块里面的一些做数学运算的函数了。

此表是B卡的用户,属于放款中

data=pd.read_csv("bcard.txt")
data

 

obs_mth

bad_ind

uid

td_score

jxl_score

mj_score

rh_score

zzc_score

zcx_score

person_info

finance_info

credit_info

act_info

0

2018-10-31

0.0

A10000005

0.675349

0.144072

0.186899

0.483640

0.928328

0.369644

-0.322581

0.023810

0.00

0.217949

1

2018-07-31

0.0

A1000002

0.825269

0.398688

0.139396

0.843725

0.605194

0.406122

-0.128677

0.023810

0.00

0.423077

2

2018-09-30

0.0

A1000011

0.315406

0.629745

0.535854

0.197392

0.614416

0.320731

0.062660

0.023810

0.10

0.448718

3

2018-07-31

0.0

A10000481

0.002386

0.609360

0.366081

0.342243

0.870006

0.288692

0.078853

0.071429

0.05

0.179487

4

2018-07-31

0.0

A1000069

0.406310

0.405352

0.783015

0.563953

0.715454

0.512554

-0.261014

0.023810

0.00

0.423077

...

...

...

...

...

...

...

...

...

...

...

...

...

...

95801

2018-11-30

0.0

Ab99_96436391998107976

0.890233

0.442687

0.802687

0.776982

0.638971

0.605522

0.078853

0.142857

0.25

0.076923

95802

2018-11-30

0.0

Ab99_96436391998176292

0.161840

0.495766

0.085750

0.536738

0.596144

0.132972

0.078853

0.023810

0.00

0.076923

95803

2018-11-30

0.0

Ab99_96436391998322771

0.746522

0.732739

0.025475

0.831805

0.642904

0.029297

0.078853

0.023810

0.00

0.076923

95804

2018-11-30

0.0

Ab99_96436391998973383

0.176846

0.749610

0.933879

0.506921

0.867099

0.751643

0.078853

0.023810

0.02

0.076923

95805

2018-11-30

0.0

Ab99_96436392001380983

0.417920

0.650343

0.985863

0.374100

0.330634

0.596833

0.078853

0.071429

0.62

0.076923

95806 rows × 13 columns

person_info  用户相关的信息   身份特质
finance_info:财务相关的信息   履约能力
credit_info:  信用相关的信息    信用历史
act_info;      行为信息               行为偏好

score 结尾的是,其他信用机构的评分

身份特质: 稳定性,所在公司,职业类型,消费稳定度,近一年内使用手机号码数,手机号码稳定天数,地址稳定天数

履约能力:是否有车,是否有房,近一个月流动资产日均值,近三个月流动资产日均值,近六个月流动资产日均值,近一年流动资产日均值,近一个月理财产品总收益,近三个月理财产品总收益,近六个月理财产品总收益,近一年理财产品总收益,历史理财产品总收益,近一个月支付总金额,近三个月支付总金额,近六个月支付总金额,近一个月消费总金额,近三个月消费总金额,近六个月消费总金额,

信用历史:近一个月主动查询金融机构次数,近三个月主动查询金融机构数,近六个月主动查询金融机构数,近一个月信贷类还款总金额,近三个月信贷类还款总金额,近六个月信贷类还款总金额,近一年信贷类还款总金额,近一年信贷类还款月份数,近一年M1状态,近一年M3状态,近一年M6状态,近两年M1状态,近两年M3状态,近两年M6状态,近五年M1状态,近五年M3状态,近五年M6状态

人脉关系:近1年人脉圈稳定度,社交影响力指数,信用环境指数

行为偏好:消费区域个数,近一年支付活跃场景数,近一年母婴消费总金额,近一年母婴消费总笔数,近一年游戏消费总金额,近一年游戏消费总笔数,近三个月家居建材消费总金额,进三个月家具建材消费总笔数,近一年汽车消费总金额,近一年汽车消费总笔数,近一年航旅度假消费总金额,近一年航旅度假消费总笔数

data.info() #没有缺失值
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 95806 entries, 0 to 95805
Data columns (total 13 columns):
 #   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   obs_mth       95806 non-null  object 
 1   bad_ind       95806 non-null  float64
 2   uid           95806 non-null  object 
 3   td_score      95806 non-null  float64
 4   jxl_score     95806 non-null  float64
 5   mj_score      95806 non-null  float64
 6   rh_score      95806 non-null  float64
 7   zzc_score     95806 non-null  float64
 8   zcx_score     95806 non-null  float64
 9   person_info   95806 non-null  float64
 10  finance_info  95806 non-null  float64
 11  credit_info   95806 non-null  float64
 12  act_info      95806 non-null  float64
dtypes: float64(11), object(2)
memory usage: 9.5+ MB
'''
data.describe() #基本分布

 

bad_ind

td_score

jxl_score

mj_score

rh_score

zzc_score

zcx_score

person_info

finance_info

credit_info

act_info

count

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

95806.000000

mean

0.018767

0.499739

0.499338

0.501640

0.498407

0.500627

0.499672

-0.078229

0.036763

0.063626

0.236197

std

0.135702

0.288349

0.288850

0.288679

0.287797

0.289067

0.289137

0.156859

0.039687

0.143098

0.157132

min

0.000000

0.000005

0.000013

0.000007

0.000005

0.000012

0.000010

-0.322581

0.023810

0.000000

0.076923

25%

0.000000

0.250104

0.249045

0.250517

0.250115

0.249501

0.248318

-0.261014

0.023810

0.000000

0.076923

50%

0.000000

0.500719

0.499795

0.503048

0.497466

0.501688

0.499130

-0.053718

0.023810

0.000000

0.205128

75%

0.000000

0.747984

0.748646

0.752032

0.747188

0.750986

0.750683

0.078853

0.023810

0.060000

0.346154

max

1.000000

0.999999

0.999985

0.999993

0.999986

0.999998

0.999987

0.078853

1.023810

1.000000

1.089744

#看一下月份分布,我们用最后一个月做为跨时间验证集合
data.obs_mth.unique()  #数据经过脱敏,把一个月份的放到一天中
#array(['2018-10-31', '2018-07-31', '2018-09-30', '2018-06-30', '2018-11-30'],dtype=object)
#划分训练集和跨时间验证集
train = data[data.obs_mth != '2018-11-30'].reset_index().copy() #train.count() 查看总条数79831 #划分训练集
val = data[data.obs_mth == '2018-11-30'].reset_index().copy()  #val。count() 15975  #跨时间验证集
#把最后一个月留出来,看模型是否稳定,迁移能力强不强,不能是在线下特别好,上线就垮掉
#.copy() 函数返回一个字典的浅复制。浅复制是指当对象的字段值被复制时,字段引用的对象不会被复制
train

index

obs_mth

bad_ind

uid

td_score

jxl_score

mj_score

rh_score

zzc_score

zcx_score

person_info

finance_info

credit_info

act_info

0

0

2018-10-31

0.0

A10000005

0.675349

0.144072

0.186899

0.483640

0.928328

0.369644

-0.322581

0.023810

0.00

0.217949

1

1

2018-07-31

0.0

A1000002

0.825269

0.398688

0.139396

0.843725

0.605194

0.406122

-0.128677

0.023810

0.00

0.423077

2

2

2018-09-30

0.0

A1000011

0.315406

0.629745

0.535854

0.197392

0.614416

0.320731

0.062660

0.023810

0.10

0.448718

3

3

2018-07-31

0.0

A10000481

0.002386

0.609360

0.366081

0.342243

0.870006

0.288692

0.078853

0.071429

0.05

0.179487

4

4

2018-07-31

0.0

A1000069

0.406310

0.405352

0.783015

0.563953

0.715454

0.512554

-0.261014

0.023810

0.00

0.423077

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

79826

79826

2018-09-30

0.0

Ab99_96436392005147255

0.905578

0.927706

0.994447

0.315842

0.959443

0.042640

0.078853

0.071429

0.13

0.076923

79827

79827

2018-10-31

0.0

Ab99_96436392005205003

0.639265

0.219267

0.845014

0.751332

0.275557

0.902642

0.078853

0.023810

0.00

0.076923

79828

79828

2018-10-31

0.0

Ab99_96436392005412387

0.355061

0.837747

0.931882

0.442463

0.579277

0.740754

0.078853

0.023810

0.02

0.076923

79829

79829

2018-10-31

0.0

Ab99_96436392006241624

0.797237

0.501238

0.490850

0.592068

0.479618

0.859870

0.078853

0.023810

0.00

0.076923

79830

79830

2018-10-31

0.0

Ab99_96436392007495598

0.990920

0.833572

0.993425

0.732783

0.925975

0.395655

0.078853

0.023810

0.00

0.076923

79831 rows × 14 columns

#这是我们全部的变量,info结尾的是自己做的无监督系统输出的个人表现,score结尾的是收费的外部征信数据
feature_lst = ['person_info','finance_info','credit_info','act_info','td_score','jxl_score','mj_score','rh_score']
feature_lst
'''
['person_info',
 'finance_info',
 'credit_info',
 'act_info',
 'td_score',
 'jxl_score',
 'mj_score',
 'rh_score']

'''
#测试集
x = train[feature_lst]
y = train['bad_ind']

#验证集
val_x =  val[feature_lst]
val_y = val['bad_ind']

#创建逻辑回归模型,训练评分卡模型
lr_model = LogisticRegression(C=0.1) #c=0.1 学习率
lr_model.fit(x,y)
'''

LogisticRegression(C=0.1)
'''
data.groupby(['bad_ind']).count()

 

obs_mth

uid

td_score

jxl_score

mj_score

rh_score

zzc_score

zcx_score

person_info

finance_info

credit_info

act_info

bad_ind

 

 

 

 

 

 

 

 

 

 

 

 

0.0

94008

94008

94008

94008

94008

94008

94008

94008

94008

94008

94008

94008

1.0

1798

1798

1798

1798

1798

1798

1798

1798

1798

1798

1798

1798

# 利用模型预测结果  predict_proba 得到是预测的概率值 [第一个是是0的概率,第二个是是1的概率]
lr_model.predict_proba(x)
lr_model.predict_proba(x)
'''
array([[0.9926959 , 0.0073041 ],
       [0.99102314, 0.00897686],
       [0.98376226, 0.01623774],
       ...,
       [0.9757364 , 0.0242636 ],
       [0.97646662, 0.02353338],
       [0.97714918, 0.02285082]])
'''
#训练集的表现
y_pred = lr_model.predict_proba(x)[:,1] # 取出是坏人的概率
#评估模型利用 KS值 还要绘制ROC曲线
fpr_lr_train,tpr_lr_train,_ = roc_curve(y,y_pred)
#计算训练集的ks值
train_ks = abs(fpr_lr_train-tpr_lr_train).max() #abs(): 绝对值
train_ks
#0.4151676259891534
#验证集的表现

y_pred = lr_model.predict_proba(val_x)[:,1] # 取出是坏人的概率
#评估模型利用 KS值 还要绘制ROC曲线
fpr_lr,tpr_lr,_ = roc_curve(val_y,y_pred) #验证集跟预测集比
#计算训练集的ks值
val__ks = abs(fpr_lr-tpr_lr).max() #abs(): 绝对值
val__ks
#0.3856283523530577
0.4151676259891534-0.3856283523530577
#0.02953927363609571
# 在验证集的表现上 比训练集差7.1。 如果是差百分之十以上,是不可以接受,必须在百分之十以内才可以接着用
0.02953927363609571/0.4151676259891534
#0.07115023375369699
#图形展示
import matplotlib.pyplot as plt
plt.plot(fpr_lr_train,tpr_lr_train,label = 'train LR')
plt.plot(fpr_lr,tpr_lr,label='evl LR')
plt.plot([0,1],[0,1],'k--')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC Curve')
plt.legend(loc = 'best') 
#分别将字符串1、字符串2、字符串3……标注到图中,每个字符串对应的图标为画图时的图标。
#loc = 'best'是选择最佳得位置放置图标
plt.show()

#图形显示,黄的在下面,黄色的抖动更厉害。理想的效果是比较平滑的线,

逻辑回归项目实战信用卡欺诈检测ppt 逻辑回归评分卡_算法

 

模型评价

  • KS 0.6 最好是0~1 0.2可以用 0.8左右
  • ROC曲线上的点 找到 Y-X 最大的
  • AUC CTR预估(推荐,计算广告,搜索)
  • auc>0.6 就是可以用的 0.8左右
  • AUC 最小值 0.5  

KS值的计算

通过roc 计算出fpr,tpr

ks = abs(fpr- tpr).max()

模型评价的时候,训练集和验证集 表现(ks值)相差不要超过10%

 

使用第一种方式,方差膨胀系数作为特征选择 

#再做特征筛选  计算VIF值 方差膨胀系数
from statsmodels.stats.outliers_influence import variance_inflation_factor
X = np.array(x)
for i in range(X.shape[1]):
    print(variance_inflation_factor(X,i))
'''
1.302139754557776
1.9579535743187257
1.2899442089163675
2.9681708673324065
3.287109972276014
3.286493284008914
3.317508798033784
3.2910065791107597

'''

 

使用第二种方式lightgbm,作为特征筛选 

lightgbm的原理是把数据丢到模型中,最主要的是feature_importance是当前模型的特征重要程度

# lightgbm 进行特征筛选
import lightgbm as lgb
from sklearn.model_selection import train_test_split
train_x,test_x,train_y,test_y = train_test_split(x,y,random_state=0,test_size=0.2)
def  lgb_test(train_x,train_y,test_x,test_y):
    clf =lgb.LGBMClassifier(boosting_type = 'gbdt',
#LGBMClassifier分类器  
#gbdt全称梯度下降树,GBDT GBDT(Gradient Boosting Decision Tree)(梯度提升决策树)
                           objective = 'binary',#binary:logistic: 二分类逻辑回归,输出为概率
                           metric = 'auc', #auc:曲线下的面积; etric评价函数
                           learning_rate = 0.1, #学习率,控制每次迭代更新权重时的步长,默认0.3,这里设置得步长是0.1
                           n_estimators = 24, #生成最大树的数目,也是最大的迭代次数。
                           max_depth = 5, #树的最大深度
                           num_leaves = 20,# 一棵树上的叶子数
                           max_bin = 45,   #分段数量 bin的最大数 决定 特征的最大组数
                           min_data_in_leaf = 6, #min_data_in_leaf:一个叶子上数据的最小数量. 可以用来处理过拟合
                           bagging_fraction = 0.6, #不进行重采样的情况下随机选择部分数据
                           bagging_freq = 0, #bagging的次数。0表示禁用bagging,非零值表示执行k次bagging
                           feature_fraction = 0.8,  # 建树的特征选择比例 #通过设置feature_fraction使用特征子采样 
                           )
# 一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。默认是100。
#降低学习率,增加迭代次数.学习率通常和迭代次数配合使用

#max_bin:建议使用较小的 max_bin (e.g. 63) 来获得更快的速度. 为直方图算法中特征值离散化的分段数量
#使用较大的直方图数目 max_bin,这样会牺牲训练速度

#min_data_in_leaf 是一个很重要的参数, 也叫min_child_samples,
#它的值取决于训练数据的样本个树和num_leaves. 将其设置的较大可以避免生成一个过深的树, 但有可能导致欠拟合。

    # fit传入数据训练模型   eval_metric =‘auc’ 利用AUC来评价模型效果
    clf.fit(train_x,train_y,eval_set = [(train_x,train_y),(test_x,test_y)],eval_metric = 'auc')
    # 返回的是最好的验证集和训练集的 AUC.  clf LGBMClassifier 模型
    return clf,clf.best_score_['valid_1']['auc'],
lgb_model , lgb_auc  = lgb_test(train_x,train_y,test_x,test_y)
#训练出来最好的是training's auc: 0.839688	valid_1's auc: 0.812833
'''
[LightGBM] [Warning] feature_fraction is set=0.8, colsample_bytree=1.0 will be ignored. Current value: feature_fraction=0.8
[LightGBM] [Warning] min_data_in_leaf is set=6, min_child_samples=20 will be ignored. Current value: min_data_in_leaf=6
[LightGBM] [Warning] bagging_fraction is set=0.6, subsample=1.0 will be ignored. Current value: bagging_fraction=0.6
[LightGBM] [Warning] bagging_freq is set=0, subsample_freq=0 will be ignored. Current value: bagging_freq=0
[1]	training's auc: 0.790509	valid_1's auc: 0.785818
[2]	training's auc: 0.801828	valid_1's auc: 0.791351
[3]	training's auc: 0.804934	valid_1's auc: 0.79404
[4]	training's auc: 0.811643	valid_1's auc: 0.80383
[5]	training's auc: 0.812088	valid_1's auc: 0.804768
[6]	training's auc: 0.819316	valid_1's auc: 0.805852
[7]	training's auc: 0.822285	valid_1's auc: 0.809526
[8]	training's auc: 0.825303	valid_1's auc: 0.811742
[9]	training's auc: 0.825884	valid_1's auc: 0.811141
[10]	training's auc: 0.827035	valid_1's auc: 0.811658
[11]	training's auc: 0.827486	valid_1's auc: 0.810881
[12]	training's auc: 0.828296	valid_1's auc: 0.810599
[13]	training's auc: 0.828793	valid_1's auc: 0.809961
[14]	training's auc: 0.830189	valid_1's auc: 0.810371
[15]	training's auc: 0.830516	valid_1's auc: 0.810961
[16]	training's auc: 0.832925	valid_1's auc: 0.810342
[17]	training's auc: 0.832958	valid_1's auc: 0.809997
[18]	training's auc: 0.833791	valid_1's auc: 0.810946
[19]	training's auc: 0.835088	valid_1's auc: 0.811165
[20]	training's auc: 0.835256	valid_1's auc: 0.812049
[21]	training's auc: 0.836739	valid_1's auc: 0.81188
[22]	training's auc: 0.838448	valid_1's auc: 0.812217
[23]	training's auc: 0.838992	valid_1's auc: 0.812342
[24]	training's auc: 0.839688	valid_1's auc: 0.812833

'''
#feature_importance特征重要性
feature_importance = pd.DataFrame({'name':lgb_model.booster_.feature_name(),'importance':lgb_model.feature_importances_})
feature_importance.sort_values(by=['importance'],ascending = False)

 

name

importance

2

credit_info

95

3

act_info

66

0

person_info

55

5

jxl_score

51

1

finance_info

50

4

td_score

50

7

rh_score

46

6

mj_score

43

#feature_name 返回特征名字
lgb_model.booster_.feature_name()
'''
['person_info',
 'finance_info',
 'credit_info',
 'act_info',
 'td_score',
 'jxl_score',
 'mj_score',
 'rh_score']
'''
lgb_model.feature_importances_
'''
array([55, 50, 95, 66, 50, 51, 43, 46])
'''
#确立新的特征
feature_lst = ['person_info','finance_info','credit_info','act_info']
x = train[feature_lst]
y = train['bad_ind']

val_x =  val[feature_lst]
val_y = val['bad_ind']

lr_model = LogisticRegression(C=0.1,class_weight='balanced')
lr_model.fit(x,y)
y_pred = lr_model.predict_proba(x)[:,1]
fpr_lr_train,tpr_lr_train,_ = roc_curve(y,y_pred)
train_ks = abs(fpr_lr_train - tpr_lr_train).max()
print('train_ks : ',train_ks)

y_pred = lr_model.predict_proba(val_x)[:,1]
fpr_lr,tpr_lr,_ = roc_curve(val_y,y_pred)
val_ks = abs(fpr_lr - tpr_lr).max()
print('val_ks : ',val_ks)
from matplotlib import pyplot as plt
plt.plot(fpr_lr_train,tpr_lr_train,label = 'train LR')
plt.plot(fpr_lr,tpr_lr,label = 'evl LR')
plt.plot([0,1],[0,1],'k--')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC Curve')
plt.legend(loc = 'best')
plt.show()


#train_ks :  0.4482325608488951
#val_ks :  0.4198642457760936
0.4482325608488951-0.4198642457760936
#0.02836831507280152

0.02836831507280152/0.4482325608488951
#0.06328927782282386

lightGBM特征选择(embeded)

  • 训练lightgbm模型
  • 通过模型的lgb_model.feature_importances_ 可以输出模型重要性的得分,通过这个得分可以确定哪个特征比较重要

 

生成模型评估报告

# 生成模型评估报告
model=lr_model
row_num,col_num = 0,0
#将所有的数据按照 预测的坏人的概率进行分箱
bins = 20 #分成二十箱
model.predict_proba(val_x)
'''
array([[0.11136699, 0.88863301],
       [0.60217326, 0.39782674],
       [0.63381413, 0.36618587],
       ...,
       [0.48207382, 0.51792618],
       [0.46982277, 0.53017723],
       [0.1055019 , 0.8944981 ]])
'''
# 取出验证集中 所有样本预测的是坏人的概率
Y_predict = [s[1] for s in model.predict_proba(val_x)] #1代表是坏人,遍历出所有是坏人的概率值

# 验证数据中 真实情况(0,没有逾期,1,逾期)
Y = val_y  #val_y验证集中的坏人
nrows = Y.shape[0] #15975 #验证集中11月份

lis=[(Y_predict[i],Y[i]) for i in range(nrows)] #[(0.88863300866552, 0.0), 验证集中预测坏人的概率,坏人,
ks_lis = sorted(lis,key = lambda x:x[0],reverse = True) #概率从高到低排序,降序排列
bin_num = int(nrows/bins+1) #799  #总共15975份,分成20份,每份多少值
bad = sum([1 for (p,y) in ks_lis if y>0.5]) #328# 计算逾期人数
good = sum([1 for (p,y) in ks_lis if y<0.5]) #15647 # 计算好人的人数
bad_cnt, good_cnt = 0, 0     # 累计坏人人数 ,累计好人的人数。声明行和列
KS = []
BAD = []
GOOD = []
BAD_CNT = []
GOOD_CNT = []
BAD_PCTG = []
BADRATE = []
dct_report = {}
for j in range(bins):
    ds = ks_lis[j*bin_num: min((j+1)*bin_num, nrows)]
    #计算每一组 有多少好人 多少坏人
    bad1 = sum([1 for (p, y) in ds if y > 0.5])
    good1 = sum([1 for (p, y) in ds if y <= 0.5])
    # 到这一组位置 一共出现了多少好人 多少坏人
    bad_cnt += bad1
    good_cnt += good1
    
    bad_pctg = round(bad_cnt/sum(val_y),3)  # 一箱一箱累加 到这一箱一共有多少逾期 占所有逾期的比例 
    #sum(val_y)验证集坏人的总数,bad_cnt遍历过程中,目前出现的坏人数;3是BAD_PCTG出现的小数点位数
    badrate = round(bad1/(bad1+good1),3)  # 一箱一箱累加 计算当前这一箱坏人比例
    ks = round(math.fabs((bad_cnt / bad) - (good_cnt / good)),3) # 计算KS值
    #fabs函数是一个求绝对值的函数,求出x的绝对值
    #ks值计算公式 TPR-FPR差值最大的就是ks值。(纵轴-横轴) 
    KS.append(ks)
    BAD.append(bad1)
    GOOD.append(good1)
    BAD_CNT.append(bad_cnt)
    GOOD_CNT.append(good_cnt)
    BAD_PCTG.append(bad_pctg)
    BADRATE.append(badrate)
    dct_report['KS'] = KS
    dct_report['BAD'] = BAD
    dct_report['GOOD'] = GOOD
    dct_report['BAD_CNT'] = BAD_CNT
    dct_report['GOOD_CNT'] = GOOD_CNT
    dct_report['BAD_PCTG'] = BAD_PCTG
    dct_report['BADRATE'] = BADRATE
val_repot = pd.DataFrame(dct_report)
val_repot
#BADRATE 预测情况是从高到底的,说明还是有优化空间的。希望预测的情况和真实的情况是一致的

 

KS

BAD

GOOD

BAD_CNT

GOOD_CNT

BAD_PCTG

BADRATE

0

0.217

86

713

86

713

0.262

0.108

1

0.299

43

756

129

1469

0.393

0.054

2

0.339

29

770

158

2239

0.482

0.036

3

0.381

30

769

188

3008

0.573

0.038

4

0.398

22

777

210

3785

0.640

0.028

5

0.403

18

781

228

4566

0.695

0.023

6

0.408

18

781

246

5347

0.750

0.023

7

0.398

13

786

259

6133

0.790

0.016

8

0.396

16

783

275

6916

0.838

0.020

9

0.361

5

794

280

7710

0.854

0.006

10

0.332

7

792

287

8502

0.875

0.009

11

0.287

2

797

289

9299

0.881

0.003

12

0.258

7

792

296

10091

0.902

0.009

13

0.225

6

793

302

10884

0.921

0.008

14

0.208

11

788

313

11672

0.954

0.014

15

0.182

8

791

321

12463

0.979

0.010

16

0.137

2

797

323

13260

0.985

0.003

17

0.092

2

797

325

14057

0.991

0.003

18

0.045

1

798

326

14855

0.994

0.001

19

0.000

2

792

328

15647

1.000

0.003

val_repot.BADRATE#BADRATE 预测情况是从高到底的,说明还是有优化空间的。希望预测的情况和真实的情况是一致的
'''
0     0.108
1     0.054
2     0.036
3     0.038
4     0.028
5     0.023
6     0.023
7     0.016
8     0.020
9     0.006
10    0.009
11    0.003
12    0.009
13    0.008
14    0.014
15    0.010
16    0.003
17    0.003
18    0.001
19    0.003
Name: BADRATE, dtype: float64

'''

 

模型报告

  • 根据预测的坏人概率,对验证数据进行排序(降序排列,预测是坏人的概率比较高的排在前面)
  • 将排序后的验证数据进行分箱,计算每一箱中实际上有多少个坏人,计算每一箱坏人的概率
  • 如果模型比较靠谱的话,每一箱坏人人数应该是单调递减的,预测坏人概率比较高的箱,抓坏人的能力要比预测坏人概率比较低的箱能力要强
  • 如果 数据不是单调递减的 模型需要调整
from pyecharts.charts import *
from pyecharts import options as opts
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
np.set_printoptions(suppress=True)
pd.set_option('display.unicode.ambiguous_as_wide', True)
pd.set_option('display.unicode.east_asian_width', True)
line = (
    Line() 
    .add_xaxis([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) #x轴坐标1-19
    .add_yaxis(
        "分组坏人占比",
        list(val_repot.BADRATE),
        yaxis_index=0,
        color="red",
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(title="行为评分卡模型表现"),
    )
    .extend_axis(
        yaxis=opts.AxisOpts(
            name="累计坏人占比",
            type_="value",
            min_=0,
            max_=0.5,
            position="right",
            axisline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(color="red")
            ),
            axislabel_opts=opts.LabelOpts(formatter="{value}"),
        )

    )
    .add_xaxis([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
    .add_yaxis(
        "KS",
        list(val_repot['KS']),
        yaxis_index=1,
        color="blue",
        label_opts=opts.LabelOpts(is_show=False),
    )
)
#这里不能使用render_notebook()导出文件,会是空白情况
line.render(path='行为评分卡模型表现.html') #相对路径输出html文件展示图片

逻辑回归项目实战信用卡欺诈检测ppt 逻辑回归评分卡_逻辑回归项目实战信用卡欺诈检测ppt_02

model.coef_
#array([[ 3.4946237 , 11.40440098,  2.45601882, -1.6844742 ]])

model.intercept_
#array([-0.34578469])

feature_lst
#['person_info', 'finance_info', 'credit_info', 'act_info']

def score(person_info,finance_info,credit_info,act_info):
    xbeta = person_info * ( 3.49460978) + finance_info * ( 11.40051582 ) + credit_info * (2.45541981) + act_info * ( -1.68676079) --0.34484897 
    score = 650-34* (xbeta)/math.log(2)  #  基准分+ 系数* 2^(1-p/p)
    return score
val['score'] = val.apply(lambda x : score(x.person_info,x.finance_info,x.credit_info,x.act_info) ,axis=1)

val['score'].describe()
'''
count    15975.000000
mean       624.928765
std         52.882132
min        174.693564
25%        604.620826
50%        623.758049
75%        662.174834
max        735.528217
Name: score, dtype: float64

'''
#对应评级区间
def level(score):
    level = 0
    if score <= 600:
        level = "D"
    elif score <= 640 and score > 600 : 
        level = "C"
    elif score <= 680 and score > 640:
        level = "B"
    elif  score > 680 :
        level = "A"
    return level
val['level'] = val.score.map(lambda x : level(x) )

val['level'].value_counts()
'''
C    6251
B    3837
D    3581
A    2306
Name: level, dtype: int64
'''
import seaborn as sns
# seaborn绘制直方图 查看评分的分布情况
sns.distplot(val.score,kde=True)

逻辑回归项目实战信用卡欺诈检测ppt 逻辑回归评分卡_决策树_03

val = val.sort_values('score',ascending=True).reset_index(drop=True)
df2=val.bad_ind.groupby(val['level']).sum()  # 每一组逾期的数量
df3=val.bad_ind.groupby(val['level']).count()   # 每一组一共有多少条记录
df2/df3
'''
level
A    0.002168
B    0.008079
C    0.014878
D    0.055571
Name: bad_ind, dtype: float64
'''

 

评分卡模型过程流程

  • 数据准备 特征筛选
  • 创建逻辑回归模型
  • 主要问题集中在模型评估上
  • KS 0.2可以用 0.6以上
  • 跨时间验证 训练集和验证集上 表现的差距(ks值) 不要超过10%
  • 生成评估报告
  • 考察模型按照坏人概率单调排序,抓坏人的能力是不是也是单调变化的
  • 模型的使用
  • 利用逻辑回归模型的 系数和截距给每一个预测结果
  • xbeta = person_info * ( 3.49460978) + finance_info * ( 11.40051582 ) + credit_info * (2.45541981) + act_info * ( -1.68676079) --0.34484897
  • score = 650-34* (xbeta)/math.log(2) 系数可以自己调
  • 可以根据信用分 给一个信用评级