每个算法工程师都有自己建模的习惯,因此在建模流程上会有所不同。本文主要介绍了一般的建模流程,有些步骤的先后顺序可能会有所差异,具体还需结合自己的实际相结合。
1.明确需求,确定y: 这是建模第一步需要做的,y定义的合理程度很大程度的影响模型乃至策略的评估。一方面需要经验的支撑,另一方面需要结合业务知识来确定。例如在信贷中A卡一般根据vintage曲线或者滚动率来确定好人与坏人的标签。
2.寻找与y相关的底层变量: 在确定y的业务基础上,寻找相关变量可以是传统相关的业务变量,可以是相关的数据埋点,可以是三方公司的数据,可以是用户授权所爬取的相关数据等。(注意:每个变量的具体业务含义必须明确,下面流程需要评估合理性与衍生)
3.数据EDA: 这部分主要是对数据有一个大体的了解。主要包含变量的均值、中位数、缺失率(缺失率高的是否考虑删除)、同一值(取值distinct较少是否删除)、异常值、分布、相关性(这一步也可以放在后面降维步骤后再进行一波降维)等。
4.变量衍生: 在拿到且理解了基础的变量之后,需要做变量衍生处理(涉及到空值时需要注意,结合业务)。可以通过四则运算、结合时间维度、PCA等模型的方法、结合业务知识等。
5.数据编码(确保数据可入模): 不同的模型对含有缺失值、字符型变量的容忍度各不相同,因此需要对数据做编码(可以看成数据清洗的一种)来适应不同的模型,确保模型的顺利训练。对于涉及计算距离的模型数据需要进行标准化/归一化处理;对分类变量可以进行one-hot(独热编码会导致维度急剧增加,可通过one-hot+PCA)、LaberEncoder、woe编码等;对于缺失值需要删除或者填充。
6.降维: 根据缺失率与同一值做初步剔除;单变量分析进行一波降维(包含分箱看趋势及IV等);PCA等降维模式;机器学习方法输出变量重要性等降维;变量相关性做降维。
7.模型调参、评估指标: 模型调参主要根据每个模型本身进行调参。主要涉及解决样本不平衡、过拟合与欠拟合的问题。调参可以调用网格搜索、贝叶斯调参等方法。评估指标主要是根据不同的应用场景来确定具体的评估指标,例如A卡会关注模型衰减度(稳定性)、ROC、AUC、KS、swap分析等。
8.模型部署监控: 模型的监控一方面保证生产上顺利进行,另一方面是观察模型的效果。在信贷中A卡一般监控:进件量、评分卡均值、评分卡分10档分组占比、评分PSI波动;变量均值、缺失率、PSI波动,变量分bin;后端:每周放款件及对应逾期率、auc、ks、Lift图、变量分bin、变量iv、变量TP图等
import pandas as pd
import Logistic_model as lm #自己定义的包,主要涉及EDA、等频分箱、卡方分箱、变量IV、一些树模型的变量重要性自动化
import os
os.chdir("E:/wyz/Desktop/模型学习/data/")
data = pd.read_excel("E:/wyz/Desktop/模型学习/data/data.xlsx")
#eda
data_eda = lm.eda_data(data)
data_eda.to_csv('EDA.csv',encoding = 'utf-8-sig')
#根据eda做清洗
data_drop = lm.eda_drop(data_eda,data,0.7,2,'target')
#单变量分箱(包含等频分箱和卡方分箱,需要手工挑选有效变量)
page = lm.single_variable(data_drop,'target',5,10,10)
page.render('单变量分箱分析.html')
#变量重要性分析(可以在单变量分箱之后,也可以拿全变量进行分析)
page = lm.variable_imp(data_drop,'target')
page.render('变量重要性分析.html')
#相关性(变量较少可以直接运行,变量较多可以前几布降维后输出)
heatmap = lm.cor_chart(data_drop,'target')
heatmap.render('相关性.html')
#挑选出有效变量
data_drop_new = data_drop[['target','var1','var2','var3','var4','var5']]
#将变量进行woe编码
var1 = [0.82,0.849,0.928,11.795]
data_drop1 = lm.Con_data_process(data_drop_new,"var1",var1,'bin5')
var2 = [0.0788,0.108,0.176,27]
data_drop1 = lm.Con_data_process(data_drop1,"var2",var2,'bin4')
var3 = [-0.253,-0.0232,0.0291,2]
data_drop1 = lm.Con_data_process(data_drop1,"var3",var3,'bin1')
var4 = [0.0776,0.16,0.327,97]
data_drop1 = lm.Con_data_process(data_drop1,"var4",var4,'bin1')
var5 = [0.163673,0.334605,0.505539,0.678744,22]
data_drop1 = lm.Con_data_process(data_drop1,"var5",var5,'bin1')
#取出woe编码后的数据
data_model = data_drop1[['target','var1','var2','var3','var4','var5']]
from statsmodels.stats.outliers_influence import variance_inflation_factor
def vif_value(df):
vif = pd.DataFrame()
vif["VIF Factor"] = [variance_inflation_factor(df.values, i) for i in range(df.shape[1])]
vif["features"] = df.columns
return vif
vif_value(data_model.drop('target', axis=1))
from statsmodels.formula.api import ols
import statsmodels.api as sm#最小二乘
from sklearn.model_selection import train_test_split
data_model['intercept'] = 1 #statsmodels需要价格截距项
y = data_model['target']
x = data_model.drop('target', axis=1)
x_train, x_test, y_train, y_test = train_test_split(x, y,random_state=0,train_size=0.7)
#训练模型
logit=sm.Logit(y_train,x_train)
model_logit=logit.fit()
#假设性检验输出
model_logit.summary()
from sklearn.metrics import roc_auc_score
#计算ks
def cal_ks(y_true,y_pred,bins):
'''
y_true:真实标签
y_pred:预测的概率
bins:划分的组
'''
df1 = pd.DataFrame(y_true).join(pd.DataFrame(y_pred))
df1.columns = ['t7_cnt','score']
factor1,bins1=pd.qcut(df1["score"],bins,retbins=True,precision=6,duplicates='drop')
bins = []
bad_rate = []
good_rate = []
bad_total_num = df1.loc[df1.t7_cnt == 1].t7_cnt.count()
good_total_num = df1.loc[df1.t7_cnt == 0].t7_cnt.count()
for i in range(len(bins1[1:])):
bin_group = 'bin'+str(i+1)
if i != len(bins1[1:])-1:
bad_num = df1.loc[df1.score <= bins1[1:][i]].t7_cnt.sum()
good_num = df1.loc[df1.score <= bins1[1:][i]].t7_cnt.count()-df1.loc[df1.score <= bins1[1:][i]].t7_cnt.sum()
bins.append(bin_group)
bad_rate.append(bad_num/bad_total_num)
good_rate.append(good_num/good_total_num)
else:
bad_num = df1.loc[df1.score <= 1].t7_cnt.sum()
good_num = df1.loc[df1.score <= 1].t7_cnt.count()-df1.loc[df1.score <= 1].t7_cnt.sum()
bins.append(bin_group)
bad_rate.append(good_num/good_total_num)
good_rate.append(good_num/good_total_num)
df2 = pd.DataFrame(bad_rate,index = bins,columns = ['bad_rate']).join(pd.DataFrame(good_rate,index = bins,columns = ['good_rate']))
df2["D_value"] = df2["good_rate"] - df2["bad_rate"]
ks = max(df2["D_value"])
return ks,df2
test_pre_pro = model.predict(x_test)#输出预测为1的概率(其它评估指标可能需要将概率转化为类别)
ks_test,ks_dataframe_test = lm.cal_ks(y_test,test_pre_pro,10)#计算ks
print('test_AUC: %.6f'%roc_auc_score(y_test,test_pre_pro))#AUC,预测为概率
print('ks_test: %.6f'%ks_test)
from sklearn.externals import joblib
# 保存模型
joblib.dump(model_logit,'Logistic.pkl')
#读取数据,一般都是原始数据
data_pre = pd.read_excel("E:/wyz/Desktop/论文/predict/pre_sample.xlsx")
#将原始数据以训练集中相同方式woe编码
col_list = ['var1','var2','var3','var4','var5']
data_pre_model= lm.woe_pre(data_pre ,5,col_list,'target')
#从本地读取模型
clf = joblib.load("Logistic.pkl")
#添加截距项并进行预测
data_pre_model['intercept'] = 1
clf.predict(data_pre_model)