风控建模二、特征工程---风控
本节主要将风控中比较常见的特征工程。
目录
目录
一、分箱
1.1 Best-KS
1.2 卡方分箱法(ChiMerge)
二、WOE和IV
2.1 两种woe处理分类问题。
三、共线性
3.1 相关系数 COR:
3.2 方差膨胀系数 VIF
四、PSI
参考文献
一、分箱
分箱是将连续变量离散化,将多状态的离散变量合并成少状态。
分箱的重要性:避免特征中无意义的波动对评分带来的波动(稳定性),避免极端值的影响(健壮性)。
分箱的优势:可以将缺失作为独立的一个箱带入模型中,将所有的变量变换到相似的尺度。
分箱的限制:计算量大,分箱后需要编码。
分箱常用方法:
有监督: Best-KS, ChiMerge
无监督:等频,等距,聚类。
1.1 Best-KS
原理:让分箱后组别的分布的差异最大化。
对于连续变量
1. 排序 x={x1,x2,x3,x4...}
2. 计算每一点的KS值。
3. 选取最大的KS对应的特征值xm,将x分为{xi <= xm}与{xi > xm}两步,对于每一个部分,重复2-3,直到满足终止条件之一。
终止条件:
1. 下一步分箱后,最小的箱的占比低于设定的阈值。
2.下一步分箱后,该箱对应的y类别全部为0或1
3. 下一步分箱后,bad rate 不单调。
对于离散度很高的变量
1.编码
2.依据连续变量的方式进行分享。
在风控中,KS常用于评估模型区分度。区分度越大,说明模型的风险排序能力(ranking ability)越强。KS统计量是基于经验累积分布函数(Empirical Cumulative Distribution Function,ECDF)
建立的,一般定义为:
cum是累加函数。
import mathdef sloveKS(self, model, X, Y, Weight): Y_predict = [s[1] for s in model.predict_proba(X)] nrows = X.shape[0] #还原权重 lis = [(Y_predict[i], Y.values[i], Weight[i]) for i in range(nrows)]#按照预测概率倒序排列 ks_lis = sorted(lis, key=lambda x: x[0], reverse=True) KS = list() bad = sum([w for (p, y, w) in ks_lis if y > 0.5]) good = sum([w for (p, y, w) in ks_lis if y <= 0.5]) bad_cnt, good_cnt = 0, 0 for (p, y, w) in ks_lis: if y > 0.5: #1*w 即加权样本个数 bad_cnt += w else: #1*w 即加权样本个数 good_cnt += w ks = math.fabs((bad_cnt/bad)-(good_cnt/good)) KS.append(ks) return max(KS)
Ks值对模型的评价不受正负样本不均衡问题的干扰,但仅限于模型评价。
1.2 卡方分箱法(ChiMerge)
自底向上(基于合并)的数据离散化方法。依赖于卡方检验,将最小卡方值得相邻区间进行合并,直到满足停止准则。
与Best-Ks相比,ChiMerge可以应用于Multi-Class的情况。
ChiMerge法采取自底向上不断合并的方法完成分箱操作。
在每一步的合并过程中,依靠最小的卡方值来寻找最优的合并项。
其核心思想是,如果某两个区间可以被合并,那么这两个区间的坏样本需要有最接近的分布,进而意味着两个区间的卡方值是最小的。
于是ChiMerge的步骤如下:
1. 将数值变量A排序后分成区间较多的若干组,设为A_1,A_2,…,A_n
2. 计算A_1与A_2合并后的卡方值,A_2与A_3合并后的卡方值,直至A_n−1与A_n合并后的卡方值
3. 找出上一步所有合并后的卡方值中最小的一个,假设为A_i−1与A_i,将其合并形成新的A_i−1
4. 不断重复2和3,直至满足终止条件
通用的ChiMerge的终止条件是:
1. 某次合并后,最小的卡方值的p值超过0.9(或0.95,0.99等),
2. 某侧合并后,总的未合并的区间数达到指定的数目(例如5,10,15等)
m:该因素取值个数; k:类别数
A_ij:因素i组中,k类别的观察频数
E_ij:原假设下A_ij的期望。
当样本总量比较大时,χ2统计量近似服从(m-1)(k-1)个自由度的卡方分布。
例子:
分箱注意点
1. 连续变量(箱数5~10之间)
分箱后bad_rate不满足单调性,仍需要合并。直到bad_rate满足单调性,箱数最少为2.
2. 类别性变量
类别较少,则不需要分箱。
有几个类bad为0,需要合并。
3. ChiMerge算法,建议使用0.90,0.95,0.99置信度,最大区间数10~15之间。
二、WOE和IV
是这个组中正例样本占整个样本中正例样本的比例,是这个组中负例样本占整个样本中负例样本的比例,是这个组中正例样本的数量,是这个组中负例样本的数量,是整个样本中所有正例样本的数量,是整个样本中所有负例样本的数量。
WOE(Weight of Evidence)常用于特征变换,IV(Information Value)则用来衡量特征的预测能力。
WOE可以这么理解,表示的是当前这个组中响应的客户和未响应客户的比值,和所有样本中这个比值的差异。每个分箱里的坏好比(Odds)相对于总体的坏好比之间的差异性。WOE越大,这种差异越大,这个分组里的样本响应的可能性就越大(这组数据分到正例的概率越大),WOE越小,差异越小,这个分组里的样本响应的可能性就越小。
IV: 在筛选变量的时候, 我们需要一些具体的量化指标来衡量每自变量的预测能力,并根据这些量化指标的大小,来确定哪些变量进入模型。IV就是这样一种指标,他可以用来衡量自变量(特征)的预测能力。
- 我们假设在一个分类问题中,目标变量的类别有两类:Y1,Y2。对于一个待预测的样本A,要判断A属于Y1还是Y2,我们是需要一定的信息,假设这个信息总量是I,而这些所需要的信息,就蕴含在所有的待预测样本的特征C1,C2,C3,……,Cn中,那么,对于其中的一个特征Ci来说,其蕴含的信息越多,那么它对于判断A属于Y1还是Y2的贡献就越大,Ci的信息价值就越大,Ci的IV就越大,它就越应该进入到入模变量列表中。
下面例子是利用决策树的信息增益最大化思想来实现变量的最优分箱。
from sklearn.tree import DecisionTreeClassifier
import pandas as pd
import numpy as npdata = pd.read_csv('woe_data/cs-training.csv')
print(data.shape)
data.head()def optimal_binning_boundary(x: pd.Series, y: pd.Series, nan: float = -999.) -> list:'''利用决策树获得最优分箱的边界值列表'''boundary = [] # 待return的分箱边界值列表x = x.fillna(nan).values # 填充缺失值y = y.valuesclf = DecisionTreeClassifier(criterion='entropy', #“信息熵”最小化准则划分max_leaf_nodes=6, # 最大叶子节点数min_samples_leaf=0.05) # 叶子节点样本数量最小占比clf.fit(x.reshape(-1, 1), y) # 训练决策树n_nodes = clf.tree_.node_countchildren_left = clf.tree_.children_leftchildren_right = clf.tree_.children_rightthreshold = clf.tree_.thresholdprint(children_left)print(children_right)print(threshold)for i in range(n_nodes):if children_left[i] != children_right[i]: # 获得决策树节点上的划分边界值boundary.append(threshold[i])boundary.sort()min_x = x.min()max_x = x.max() + 0.1 # +0.1是为了考虑后续groupby操作时,能包含特征最大值的样本boundary = [min_x] + boundary + [max_x]return boundaryoptimal_binning_boundary(x=data['RevolvingUtilizationOfUnsecuredLines'],y=data['SeriousDlqin2yrs'])def feature_woe_iv(x: pd.Series, y: pd.Series, nan: float = -999.) -> pd.DataFrame:'''计算变量各个分箱的WOE、IV值,返回一个DataFrame'''x = x.fillna(nan)boundary = optimal_binning_boundary(x, y, nan) # 获得最优分箱边界值列表df = pd.concat([x, y], axis=1) # 合并x、y为一个DataFrame,方便后续计算df.columns = ['x', 'y'] # 特征变量、目标变量字段的重命名df['bins'] = pd.cut(x=x, bins=boundary, right=False) # 获得每个x值所在的分箱区间grouped = df.groupby('bins')['y'] # 统计各分箱区间的好、坏、总客户数量result_df = grouped.agg([('good', lambda y: (y == 0).sum()), ('bad', lambda y: (y == 1).sum()),('total', 'count')])result_df['good_pct'] = result_df['good'] / result_df['good'].sum() # 好客户占比result_df['bad_pct'] = result_df['bad'] / result_df['bad'].sum() # 坏客户占比result_df['total_pct'] = result_df['total'] / result_df['total'].sum() # 总客户占比result_df['bad_rate'] = result_df['bad'] / result_df['total'] # 坏比率result_df['woe'] = np.log(result_df['good_pct'] / result_df['bad_pct']) # WOEresult_df['iv'] = (result_df['good_pct'] - result_df['bad_pct']) * result_df['woe'] # IVprint(f"该变量IV = {result_df['iv'].sum()}")return result_dffeature_woe_iv(x=data['RevolvingUtilizationOfUnsecuredLines'], y=data['SeriousDlqin2yrs'])
变量RevolvingUtilizationOfUnsecuredLines,分箱WOE趋势单调,bad_rate风险排序性较好,IV值>1.0则说明该变量预测能力很强。
2.1 两种woe处理分类问题。
一、分箱的原则是负样本占比差异最大化。通常分箱的总数一般在5-10箱,每一箱之间的负样本占比差值应该尽可能大。
分箱具体分三个步骤
(1)先按照每个类型的badrate大小,从小到大排序;
(2)计算与后一箱的badrate差异,将差异最小的合并;
(3)如果某一箱的数量占比不超过5%,或者总箱数超过10箱,就需要继续合并,如果是第一箱或者最后一箱不满足,就与相邻的合并;如果是中间的箱不满足,就与badrate差异最小的合并。
#!/usr/bin/env python
# coding: utf-8# In[5]:# 数据来源: https://www.kaggle.com/c/home-credit-default-risk/data.# In[2]:import pandas as pd
df_cat = pd.read_csv("./application_train.csv",engine='c')# In[4]:variable_cat = "NAME_INCOME_TYPE"
x_cat = df_cat[variable_cat].values
y_cat = df_cat.TARGET.values# In[5]:df_cat[variable_cat].value_counts()# In[9]:df_cat.shape[0]*0.8# In[15]:data_dict = {}
data_dict['dev'] = df_cat.iloc[:184506,:][['NAME_INCOME_TYPE','TARGET']]
data_dict['val'] = df_cat.iloc[184506:246008,:][['NAME_INCOME_TYPE','TARGET']]
data_dict['off'] = df_cat.iloc[246008:,:][['NAME_INCOME_TYPE','TARGET']]# In[27]:import math
#离散型变量 WOE编码
class charWoe(object): def __init__(self, datasets, dep, weight, vars): #数据集字典,{'dev':训练集,'val':测试集,'off':跨时间验证集} self.datasets = datasets self.devf = datasets.get("dev", "") #训练集 self.valf = datasets.get("val", "") #测试集 self.offf = datasets.get("off", "") #跨时间验证集 self.dep = dep #标签 self.weight = weight #样本权重 self.vars = vars #参与建模的特征名 self.nrows, self.ncols = self.devf.shape #样本数,特征数 def char_woe(self): #得到每一类样本的个数,且加入平滑项使得bad和good都不为0 dic = dict(self.devf.groupby([self.dep]).size()) good = dic.get(0, 0) + 1e-10bad = dic.get(1, 0) + 1e-10 #对每一个特征进行遍历。 for col in self.vars: #得到每一个特征值对应的样本数。 data = dict(self.devf[[col, self.dep]].groupby([col, self.dep]).size()) ''' 当前特征取值超过100个的时候,跳过当前取值。 因为取值过多时,WOE分箱的效率较低,建议对特征进行截断。 出现频率过低的特征值统一赋值,放入同一箱内。 ''' if len(data) > 100: print(col, "contains too many different values...")continue #打印取值个数 print(col, len(data)) dic = dict() #k是特征名和特征取值的组合,v是样本数 for (k, v) in data.items(): #value为特征名,dp为特征取值 value, dp = k #如果找不到key设置为一个空字典 dic.setdefault(value, {}) #字典中嵌套字典 dic[value][int(dp)] = v for (k, v) in dic.items(): dic[k] = {str(int(k1)): v1 for (k1, v1) in v.items()} dic[k]["cnt"] = sum(v.values()) bad_rate = round(v.get("1", 0)/ dic[k]["cnt"], 5) dic[k]["bad_rate"] = bad_rate #利用定义的函数进行合并。 dic = self.combine_box_char(dic) #对每个特征计算WOE值和IV值 for (k, v) in dic.items(): a = v.get("0", 1) / good + 1e-10 b = v.get("1", 1) / bad + 1e-10 dic[k]["Good"] = v.get("0", 0) dic[k]["Bad"] = v.get("1", 0) dic[k]["woe"] = round(math.log(a / b), 5)dic[k]["iv"] = (a - b) * dic[k]["woe"]''' 按照分箱后的点进行分割, 计算得到每一个特征值的WOE值, 将原始特征名加上'_woe'后缀,并赋予WOE值。 ''' for (klis, v) in dic.items(): for k in klis.split(","): #训练集进行替换 self.devf.loc[self.devf[col]==k,"%s_woe" % col] = v["woe"]#测试集进行替换 if not isinstance(self.valf, str): self.valf.loc[self.valf[col]==k,"%s_woe" % col] = v["woe"]#跨时间验证集进行替换 if not isinstance(self.offf, str): self.offf.loc[self.offf[col]==k, "%s_woe" % col] = v["woe"]#返回新的字典,其中包含三个数据集。 return {"dev": self.devf, "val": self.valf, "off": self.offf},dicdef combine_box_char(self, dic): ''' 实施两种分箱策略。 1.不同箱之间负样本占比差异最大化。 2.每一箱的样本量不能过少。 ''' #首先合并至10箱以内。按照每一箱负样本占比差异最大化原则进行分箱。 while len(dic) >= 10: #k是特征值,v["bad_rate"]是特征值对应的负样本占比bad_rate_dic = {k: v["bad_rate"] for (k, v) in dic.items()} #按照负样本占比排序。因为离散型变量 是无序的,#可以直接写成负样本占比递增的形式。 bad_rate_sorted = sorted(bad_rate_dic.items(),key=lambda x: x[1])#计算每两箱之间的负样本占比差值。#准备将差值最小的两箱进行合并。 bad_rate = [bad_rate_sorted[i+1][1]-bad_rate_sorted[i][1] for i in range(len(bad_rate_sorted)-1)]min_rate_index = bad_rate.index(min(bad_rate)) #k1和k2是差值最小的两箱的key. k1, k2 = bad_rate_sorted[min_rate_index][0],\bad_rate_sorted[min_rate_index+1][0] #得到重新划分后的字典,箱的个数比之前少一。 dic["%s,%s" % (k1, k2)] = dict() dic["%s,%s" % (k1, k2)]["0"] = dic[k1].get("0", 0)\+ dic[k2].get("0", 0)dic["%s,%s" % (k1, k2)]["1"] = dic[k1].get("1", 0) \+ dic[k2].get("1", 0)dic["%s,%s" % (k1, k2)]["cnt"] = dic[k1]["cnt"]\+ dic[k2]["cnt"]dic["%s,%s" % (k1, k2)]["bad_rate"] = round(dic["%s,%s" % (k1, k2)]["1"] / dic["%s,%s" % (k1, k2)]["cnt"],5) #删除旧的key。 del dic[k1], dic[k2] ''' 结束循环后,箱的个数应该少于10。 下面实施第二种分箱策略。 将样本数量少的箱合并至其他箱中,以保证每一箱的样本数量不要太少。 ''' #记录当前样本最少的箱的个数。 min_cnt = min([v["cnt"] for v in dic.values()]) #当样本数量小于总样本的5%或者总箱的个数大于5的时候,对箱进行合并 while min_cnt < self.nrows * 0.05 and len(dic) > 5: min_key = [k for (k, v) in dic.items() if v["cnt"] == min_cnt][0] bad_rate_dic = {k: v["bad_rate"] for (k, v) in dic.items()} bad_rate_sorted = sorted(bad_rate_dic.items(),key=lambda x: x[1]) keys = [k[0] for k in bad_rate_sorted] min_index = keys.index(min_key) ''''' 同样想保持合并后箱之间的负样本占比差异最大化。 由于箱的位置不同,按照三种不同情况进行分类讨论。 ''' #如果是第一箱,和第二项合并 if min_index == 0: k1, k2 = keys[:2] #如果是最后一箱,和倒数第二箱合并 elif min_index == len(dic) - 1: k1, k2 = keys[-2:] #如果是中间箱,和bad_rate值相差最小的箱合并 else: bef_bad_rate = dic[min_key]["bad_rate"]\-dic[keys[min_index - 1]]["bad_rate"]aft_bad_rate = dic[keys[min_index+1]]["bad_rate"] - dic[min_key]["bad_rate"]if bef_bad_rate < aft_bad_rate: k1, k2 = keys[min_index - 1], min_keyelse: k1, k2 = min_key, keys[min_index + 1]#得到重新划分后的字典,箱的个数比之前少一。 dic["%s,%s" % (k1, k2)] = dict() dic["%s,%s" % (k1, k2)]["0"] = dic[k1].get("0", 0) \+ dic[k2].get("0", 0)dic["%s,%s" % (k1, k2)]["1"] = dic[k1].get("1", 0)\+ dic[k2].get("1", 0)dic["%s,%s" % (k1, k2)]["cnt"] = dic[k1]["cnt"]\+dic[k2]["cnt"]dic["%s,%s" % (k1, k2)]["bad_rate"] = round(dic["%s,%s" % (k1, k2)]["1"] / dic["%s,%s" % (k1, k2)]["cnt"],5)#删除旧的key。 del dic[k1], dic[k2] #当前最小的箱的样本个数 min_cnt = min([v["cnt"] for v in dic.values()]) return dic # In[28]:dep = 'TARGET'
weight = 0
vars = ['NAME_INCOME_TYPE']
charWoe_obj = charWoe(data_dict,dep, weight,vars)result, woe_dict = charWoe_obj.char_woe()result['dev']# In[31]:ddf = pd.DataFrame(woe_dict)
ddf# In[34]:new_ddf = ddf.transpose()
new_ddf# In[35]:new_ddf['iv'].sum()# In[ ]:
输出:
二、 调用库 Tutorial: optimal binning with binary target — optbinning 0.15.0 documentation
Tutorial: optimal binning with binary target — optbinning 0.15.0 documentation
三、共线性
3.1 相关系数 COR:
在做很多基于空间划分思想的模型的时候,我们必须关注变量之间的相关性。单独看两个变量的时候我们会使用皮尔逊相关系数。
df_train.corr()orimport seaborn as sns
sns.set(color_codes=True)
np.random.seed(sum(map(ord, "distributions")))
sns.pairplot(df_train)#对角线上是单维度分布
3.2 方差膨胀系数 VIF
在多元回归中,我们可以通过计算方差膨胀系数VIF来检验回归模型是否存在严重的多重共线性问题。定义:
其中,为自变量 对其余自变量作回归分析的负相关系数。方差膨胀系数是容忍度1-R2的倒数。 方差膨胀系数VIF越大,说明自变量之间存在共线性的可能性越大。一般来讲,如果方差膨胀因子超过10,则回归模型存在严重的多重共线性。又根据Hair(1995)的共线性诊断标准,当自变量的容忍度大于0.1,方差膨胀系数小于10的范围是可以接受的,表明白变量之间没有共线性问题存在。
比如:
from statsmodels.stats.outliers_influence import variance_inflation_factor
import numpy as npdata = [[1,2,3,4,5],[2,4,6,8,9],[1,1,1,1,1],[2,4,6,4,7]]
X = np.array(data).Tvariance_inflation_factor(X,0)
四、PSI
稳定性主要考虑 群体稳定性指标(population stability index)
def var_PSI(dev_data, val_data):dev_cnt, val_cnt = sum(dev_data), sum(val_data)if dev_cnt * val_cnt == 0:return NonePSI = 0for i in range(len(dev_data)):dev_ratio = dev_data[i] / dev_cntval_ratio = val_data[i] / val_cnt + 1e-10psi = (dev_ratio - val_ratio) * math.log(dev_ratio/val_ratio)PSI += psireturn PSI
下面将举一个例子,使用ks和poi进行特征筛选。
import mathdef sloveKS(self, model, X, Y, Weight): Y_predict = [s[1] for s in model.predict_proba(X)] nrows = X.shape[0] #还原权重 lis = [(Y_predict[i], Y.values[i], Weight[i]) for i in range(nrows)]#按照预测概率倒序排列 ks_lis = sorted(lis, key=lambda x: x[0], reverse=True) KS = list() bad = sum([w for (p, y, w) in ks_lis if y > 0.5]) good = sum([w for (p, y, w) in ks_lis if y <= 0.5]) bad_cnt, good_cnt = 0, 0 for (p, y, w) in ks_lis: if y > 0.5: #1*w 即加权样本个数 bad_cnt += w else: #1*w 即加权样本个数 good_cnt += w ks = math.fabs((bad_cnt/bad)-(good_cnt/good)) KS.append(ks) return max(KS) def slovePSI(self, model, dev_x, val_x): dev_predict_y = [s[1] for s in model.predict_proba(dev_x)] dev_nrows = dev_x.shape[0] dev_predict_y.sort() #等频分箱成10份 cutpoint = [-100] + [dev_predict_y[int(dev_nrows/10*i)] for i in range(1, 10)] + [100] cutpoint = list(set(cutpoint)) cutpoint.sort()val_predict_y = [s[1] for s in list(model.predict_proba(val_x))] val_nrows = val_x.shape[0] PSI = 0 #每一箱之间分别计算PSI for i in range(len(cutpoint)-1): start_point, end_point = cutpoint[i], cutpoint[i+1] dev_cnt = [p for p in dev_predict_y if start_point <= p < end_point] dev_ratio = len(dev_cnt) / dev_nrows + 1e-10 val_cnt = [p for p in val_predict_y if start_point <= p < end_point] val_ratio = len(val_cnt) / val_nrows + 1e-10 psi = (dev_ratio - val_ratio) * math.log(dev_ratio/val_ratio)PSI += psi return PSI import xgboost as xgb
from xgboost import plot_importance class xgBoost(object): def __init__(self, datasets, uid, dep, weight, var_names, params, max_del_var_nums=0):self.datasets = datasets #样本唯一标识,不参与建模 self.uid = uid #二分类标签 self.dep = dep #样本权重 self.weight = weight #特征列表 self.var_names = var_names #参数字典,未指定字段使用默认值 self.params = params #单次迭代最多删除特征的个数 self.max_del_var_nums = max_del_var_nums self.row_num = 0 self.col_num = 0 def training(self, min_score=0.0001, modelfile="", output_scores=list()): lis = self.var_names[:] dev_data = self.datasets.get("dev", "") #训练集 val_data = self.datasets.get("val", "") #测试集 off_data = self.datasets.get("off", "") #跨时间验证集#从字典中查找参数值,没有则使用第二项作为默认值 model = xgb.XGBClassifier(learning_rate=self.params.get("learning_rate", 0.1),n_estimators=self.params.get("n_estimators", 100), max_depth=self.params.get("max_depth", 3), min_child_weight=self.params.get("min_child_weight", 1),subsample=self.params.get("subsample", 1), objective=self.params.get("objective", "binary:logistic"),nthread=self.params.get("nthread", 10), scale_pos_weight=self.params.get("scale_pos_weight", 1),random_state=0, n_jobs=self.params.get("n_jobs", 10), reg_lambda=self.params.get("reg_lambda", 1), missing=self.params.get("missing", None) ) while len(lis) > 0: #模型训练 model.fit(X=dev_data[self.var_names], y=dev_data[self.dep]) #得到特征重要性 scores = model.feature_importances_ #清空字典 lis.clear() ''' 当特征重要性小于预设值时, 将特征放入待删除列表。 当列表长度超过预设最大值时,跳出循环。 即一次只删除限定个数的特征。 ''' for (idx, var_name) in enumerate(self.var_names): #小于特征重要性预设值则放入列表 if scores[idx] < min_score: lis.append(var_name) #达到预设单次最大特征删除个数则停止本次循环 if len(lis) >= self.max_del_var_nums: break #训练集KS devks = self.sloveKS(model, dev_data[self.var_names],dev_data[self.dep], dev_data[self.weight])#初始化ks值和PSI valks, offks, valpsi, offpsi = 0.0, 0.0, 0.0, 0.0 #测试集KS和PSI if not isinstance(val_data, str): valks = self.sloveKS(model,val_data[self.var_names], val_data[self.dep], val_data[self.weight]) valpsi = self.slovePSI(model,dev_data[self.var_names],val_data[self.var_names])#跨时间验证集KS和PSI if not isinstance(off_data, str): offks = self.sloveKS(model,off_data[self.var_names],off_data[self.dep],off_data[self.weight]) offpsi = self.slovePSI(model,dev_data[self.var_names],off_data[self.var_names]) #将三个数据集的KS和PSI放入字典 dic = {"devks": float(devks), "valks": float(valks),"offks": offks, "valpsi": float(valpsi),"offpsi": offpsi} print("del var: ", len(self.var_names), "-->", len(self.var_names) - len(lis),"ks: ", dic, ",".join(lis))self.var_names = [var_name for var_name in self.var_names if var_name not in lis]plot_importance(model) #重新训练,准备进入下一循环 model = xgb.XGBClassifier(learning_rate=self.params.get("learning_rate", 0.1),n_estimators=self.params.get("n_estimators", 100),max_depth=self.params.get("max_depth", 3), min_child_weight=self.params.get("min_child_weight",1),subsample=self.params.get("subsample", 1), objective=self.params.get("objective", "binary:logistic"), nthread=self.params.get("nthread", 10), scale_pos_weight=self.params.get("scale_pos_weight",1),random_state=0, n_jobs=self.params.get("n_jobs", 10), reg_lambda=self.params.get("reg_lambda", 1), missing=self.params.get("missing", None))
参考文献
- 基于sklearn决策树的最优分箱与IV值计算-Python实现 - 知乎
- 机器学习-WOE&IV编码&分箱 、样本类别分布不均衡处理(过抽样、欠抽样) - 闲酒肆中听风吟 - 博客园
- 七月在线之金融风控实战入门
风控建模二、特征工程---风控相关推荐
- 向前logistic回归与向后筛选出一样的变量_风控建模之特征筛选与建模(python)...
特征筛选 在建模之前的一步就是将特征工程做好的变量进行筛选,在风控项目中筛选分为两步:初步筛选.逐步回归筛选以及稳定性筛选. 初步筛选 通常评分卡模型的特征筛选主要从以下4个角度出发: 缺失率(一般变 ...
- 风控建模二:建模方案拟定
风控建模二:建模方案拟定 引言 一.是否需要分客群建模 二.全时段建模一定更好吗 三.进件vs规则过件vs放款件 四.怎么选训练集 五.还原真实好坏比例 引言 好坏标签定义完成并整理好数据集并不意味着 ...
- 金融风控实战入门-特征工程
1. 业务建模流程介绍 * 将业务抽象为分类or回归问题 * 定义标签,得到y * 选取合适的样本,并匹配出全部的信息作为特征的来源 * 特征工程 + 模型训练 + 模型评价与调优(相互之间可能会有交 ...
- 机器学习中的特征建模(特征工程)和算法选型建模 - 以暴力破解识别为例
catalogue 1. 特征工程是什么?有什么作用? 2. 特征获取方案 - 如何获取这些特征? 3. 特征观察 - 运用各种统计工具.图标等工具帮助我们从直观和精确层面认识特征中的概率分布 4. ...
- 【算法竞赛学习】金融风控之贷款违约预测-特征工程
Task3 特征工程 此部分为零基础入门金融风控的 Task3 特征工程部分,带你来了解各种特征工程以及分析方法,欢迎大家后续多多交流. 赛题:零基础入门数据挖掘 - 零基础入门金融风控之贷款违约 项 ...
- 风控建模 python 知乎_风控建模基本要求及面试问题小结
(本文是写给风控入门同学的建议!) 一 准备工作 根据核心职业CD法则,找工作前,你得先清楚自己有什么,自己要什么:面试单位有什么,面试单位要什么.就笔者近一年的学习及工作经验总结而言: 科班同学,最 ...
- 风控建模六:变量相关性分析及筛选方法
风控建模六:变量相关性分析及筛选方法 一.变量相关性分析 二.变量相关性筛选 三.代码实现 不论是开发逻辑回归评分卡,还是GBM机器学习模型,变量的相关性分析和筛选都是必不可少的一步,因为这个过程会帮 ...
- 风控建模七:拒绝推断
风控建模七:拒绝推断 1.为什么要做拒绝推断 解决建模时的样本偏差问题 方便策略下探时风险评估 2.拒绝推断的几种方法 开放部分测试集 借助外部数据 简单数据扩充 打包法 模糊扩增法 聚类法 3.如何 ...
- 风控建模三:变量筛选原则
风控建模二:变量筛选 一 变量自身分布稳定性 psi 长期趋势图 二 变量和目标值的强相关关系 IV值 变量数的选择 三 变量和目标值相关关系的稳定性 各个数据集上趋势一致 变量预测效果不衰减 变量预 ...
最新文章
- 使用Leangoo管理Sprint Backlog
- SAP WM初阶根据Group Number来查询与之有关的TO单
- php mysql事务处理回滚操作
- Docker容器日志集中收集(client-server模式)
- 重磅公开课推荐 | 如何搭建聊天机器人:技术架构剖析
- 安装hadoop-2.3.0-cdh5.1.2全过程
- C#线程同步(1)- 临界区&Lock .
- testng.xml文件配置
- java 多态与重载的区别_java实现多态 方法的重写和重载的区别
- 从零开始搭建Ubuntu 环境下的Android 源码开发环境
- 熊猫烧香能破坏计算机硬件吗,熊猫烧香病毒会伤害电脑硬件吗?
- sd卡卡槽_还在傻傻分不清楚SD卡、Micro SD、TF卡?卡槽马上都要取消了
- Linux安装配置FTP(pure-ftpd)
- 如何在组装或维修计算机时避免静电,组装电脑怎么避免手上的静电
- 人工智能换脸python_Python史诗级P图换脸小程序,AI换脸的简易版
- 四色定理(DFS深搜)
- 英飞凌硅麦可以接收到超声波吗?
- neo4j安装_Neo4J OGM与Quarkus
- 阿里巴巴为何坚持对混沌工程的研发迭代?
- uniapp微信小程序支付券或商家券插件
热门文章
- 2021款 联想拯救者R7000P
- JavaScript获取屏幕高度和宽度等信息
- 换热器综合实验matlab数据处理
- 基于51单片机的WIFI模块的简单通信
- MyBatis(用于简化JDBC开发)
- 【STM32+cubemx】0030 HAL库开发:DDS芯片AD9833实现简单的波形发生器
- 微博运营 如何提升企业微博影响力
- 上班能做什么副业?上班太闲做个什么副业?
- 移动端事件(touchstart+touchmove+touchend)
- html 手机 touch,手机的html上,touchstart、touchmove、touchend同时存在时,touchmove和touchend失效...