金融风控训练营赛题理解(task 3 特征工程)学习笔记

本学习笔记为阿里云天池龙珠计划Docker训练营的学习内容,学习链接为:https://github.com/datawhalechina/team-learning-data-mining/tree/master/FinancialRiskControl

学习目标:

  • 学习特征预处理、缺失值、异常值处理、数据分桶等特征处理方法
  • 学习特征交互、编码、选择的相应方法

首先,我们思考为什么要学习特征工程

答:特征工程,就是对原始数据进行一系列工程处理,将其提炼为特征,做为输入供算法和模型使用。特征工程的目的是去除原始数据中的杂质和冗余,设计更高效的特征以刻画求解的问题与预测模型之间的关系。[^1]

举一个例子,现在出一个二分类问题,请你使用逻辑回归,设计一个身材分类器。输入数据X:身高和体重 ,标签为Y:身材等级(胖,不胖)。显然,不能单纯的根据体重来判断一个人胖不胖,姚明很重,他胖吗?显然不是。针对这个问题,一个非常经典的特征工程是,BMI指数,BMI=体重/(身高^2)。 这样,通过BMI指数,就能非常显然地帮助我们,刻画一个人身材如何。甚至,你可以抛弃原始的体重和身高数据。[^2]

一、学习知识概要

  • 决策树一些介绍
  • 数据预处理
    • 缺失值的填充
    • 时间格式处理
    • 对象类型特征转换到数值
  • 异常值处理
    • 基于3σ原则
    • 基于箱型图
  • 数据分箱
    • 固定宽度分箱
    • 分位数分箱
      • 离散数值型数据分箱
      • 连续数值型数据分箱
    • 卡方分箱(选做作业)
  • 特征交互
    • 特征和特征之间组合
    • 特征和特征之间衍生
    • 其他特征衍生的尝试(选做作业)
  • 特征编码
    • one-hot编码
    • label-encoder编码
  • 特征选择
    • 1 Filter
    • 2 Wrapper (RFE)
    • 3 Embedded

二、学习内容

2.1 决策树一些介绍

因为该节内容涉及决策树,所以各位可以先了解一下决策树,它是一个非常常见并且优秀的机器学习算法,它易于理解、可解释性强,其可作为分类算法,也可用于回归模型。

  • 有兴趣深度了解的可以参考知乎博主阿泽的文章
  • 决策树的特性及优缺点可参考博主keepreder

先介绍该节内容主要用到的包:catboost
本质上是决策树算法
适用于:需要特别多的前期数据处理和特征数值化的任务

Catboost 一个超级简单实用的boost算法:https://www.jianshu.com/p/49ab87122562

  • 它自动采用特殊的方式处理类别型特征(categorical features)。首先对categorical features做一些统计,计算某个类别特征(category)出现的频率,之后加上超参数,生成新的数值型特征(numerical features)。这也是我在这里介绍这个算法最大的motivtion,有了catboost,再也不用手动处理类别型特征了。
  • catboost还使用了组合类别特征,可以利用到特征之间的联系,这极大的丰富了特征维度。
  • catboost的基模型采用的是对称树,同时计算leaf-value方式和传统的boosting算法也不一样,传统的boosting算法计算的是平均数,而catboost在这方面做了优化采用了其他的算法,这些改进都能防止模型过拟合。

其中对称树与普通的决策树有些许区别,大部分的GBM模型用的都是普通的决策树,对称树在GBM中主要有三点优势:

1、拟合模式相对简单,因为每一层都是一个判断条件

2、可以提高预测速度

3、对称树的结构本身比普通决策树自由度小,可以看作是加入了penalty,或者看作regularization

2.2 数据预处理

先导入包和数据集,并查找出数据中的对象特征和数值特征

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import datetime ##时间格式库
from tqdm import tqdm  #显示循环的进度条的库
from sklearn.preprocessing import LabelEncoder #提供了许多方便的用于做数据预处理工具
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2 #特征选择库
from sklearn.preprocessing import MinMaxScaler
import xgboost as xgb  #机器学习算法
import lightgbm as lgb  #机器学习算法
from catboost import CatBoostRegressor #机器学习算法
import warnings  #非致命提醒库
from sklearn.model_selection import StratifiedKFold, KFold  ##实现了多个交叉验证器类以及用于学习曲线、数据集分割的函数
from sklearn.metrics import accuracy_score, f1_score, roc_auc_score, log_loss  #混淆矩阵模块
warnings.filterwarnings('ignore')
data_train =pd.read_csv('train.csv')
data_test_a = pd.read_csv('testA.csv')

数据大概长这样

id	loanAmnt	term	interestRate	installment	   grade	subGrade	employmentTitle	employmentLength	homeOwnership	...	 n5	  n6    n7	   n8	  n9	 n10	 n11	n12    n13	 n14
0	35000.0	      5	        19.52	       917.97	     E	       E2	          320.0	         2 years	          2	        ...	9.0	  8.0	4.0	   12.0	  2.0	 7.0	 0.0	0.0	   0.0	 2.0
numerical_fea = list(data_train.select_dtypes(exclude=['object']).columns) #数值列
category_fea = list(filter(lambda x: x not in numerical_fea,list(data_train.columns))) #类别特征列
label = 'isDefault'
numerical_fea.remove(label)
2.21 缺失值的填充

在比赛中数据预处理是必不可少的一部分,对于缺失值的填充往往会影响比赛的结果,在比赛中不妨尝试多种填充然后比较结果选择结果最优的一种;
比赛数据相比真实场景的数据相对要“干净”一些,但是还是会有一定的“脏”数据存在,清洗一些异常值往往会获得意想不到的效果。

  • 把所有缺失值替换为指定的值0

    data_train = data_train.fillna(0)

  • 用缺失值上面的值填充缺失值

    data_train = data_train.fillna(axis=0,method=‘ffill’)
    bfill是用缺失值下面的值填充缺失值
    二维中axis=0表示行,1表示列

  • 纵向用缺失值下面的值填充缺失值,且设置最多只填充两个连续的缺失值

    data_train = data_train.fillna(axis=0,method=‘bfill’,limit=2)

#查看缺失值情况
data_train.isnull().sum() #查看每列有多少缺失值
#以下展示部分结果

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第1张图片

#按照平均数填充数值型特征
data_train[numerical_fea] = data_train[numerical_fea].fillna(data_train[numerical_fea].median())
data_test_a[numerical_fea] = data_test_a[numerical_fea].fillna(data_train[numerical_fea].median())
#按照众数填充对象型特征
data_train[category_fea] = data_train[category_fea].fillna(data_train[category_fea].mode())
data_test_a[category_fea] = data_test_a[category_fea].fillna(data_train[category_fea].mode())
#以下展示部分结果

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第2张图片

值得注意的是结果中只有object变量‘employmentLength’没有被填充,下面的方法是将object对象转化成int或float(取出或替换数值),再进行填充,但还有一种方法是采用下面的代码:
data_train[‘employmentLength’].replace(np.nan,‘10 years’,inplace=True)
data_test_a[‘employmentLength’].replace(np.nan,‘10 years’,inplace=True)
它会将缺失值直接填用别的进行替换,方便后续一并处理

#查看类别特征
category_fea
-----------------------------------
['grade', 'subGrade', 'employmentLength', 'issueDate', 'earliesCreditLine']

category_fea:对象型类别特征需要进行预处理,其中[‘issueDate’]为时间格式特征。

2.22 时间格式处理
#转化成时间格式 将表中的时间转化成python可以处理的时间格式
for data in [data_train, data_test_a]: #因为这两个表有一样的列特征,可以一起处理
    data['issueDate'] = pd.to_datetime(data['issueDate'],format='%Y-%m-%d')
    startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
    #构造时间特征
    data['issueDateDT'] = data['issueDate'].apply(lambda x: x-startdate).dt.days  #返回的是每个时间距开始时间的间隔天数
data_train['employmentLength'].value_counts(dropna=False).sort_index()
# sort_index(axis=0,ascending=True)按索引行排升序,sort_values()按值行排升序

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第3张图片

#employmentLength 的样式如:2 years,是object类型,在上面没有被填充,所以我们应该把数字取出来,将他转化为int或float类型再进行填充
#要将其中的数字取出来
def employmentLength_to_int(s):
    if pd.isnull(s):
        return s
    else:
        return np.int8(s.split()[0]) #将数据转化为int8类型,节省内存
for data in [data_train, data_test_a]:
    data['employmentLength'].replace(to_replace='10+ years', value='10 years', inplace=True)  #数据规整
    data['employmentLength'].replace('< 1 year', '0 years', inplace=True) #replace是替换函数
    data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int) #apply()是应用函数

replace用法

data['employmentLength'].value_counts(dropna=False).sort_index()

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第4张图片

  • 对earliesCreditLine进行预处理
data_train['earliesCreditLine'].sample(5) #随机抽取5个样本

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第5张图片
sample用法

for data in [data_train, data_test_a]:
    data['earliesCreditLine'] = data['earliesCreditLine'].apply(lambda s: int(s[-4:])) #取出年份,apply里的函数是对每一单元格进行操作
2.23 类别特征处理
# 部分类别特征
cate_features = ['grade', 'subGrade', 'employmentTitle', 'homeOwnership', 'verificationStatus', 'purpose', 'postCode', 'regionCode', \
                 'applicationType', 'initialListStatus', 'title', 'policyCode']
for f in cate_features:
    print(f, '类型数:', data[f].nunique())

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第6张图片

for data in [data_train, data_test_a]:
    data['grade'] = data['grade'].map({
     'A':1,'B':2,'C':3,'D':4,'E':5,'F':6,'G':7}) #将等级用数值量化,按道理也可以用replace

# 类型数在2之上,又不是高维稀疏的,且纯分类特征
for data in [data_train, data_test_a]:
    data = pd.get_dummies(data, columns=['subGrade', 'homeOwnership', 'verificationStatus', 'purpose', 'regionCode'], drop_first=True)

get_dummies( ):构建指标变量
对于更大的数据,这种用多成员构建指标变量效率低
#这是以前学习截的图

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第7张图片

2.3 异常值处理

  • 当你发现异常值后,一定要先分清是什么原因导致的异常值,然后再考虑如何处理。首先,如果这一异常值并不代表一种规律性的,而是极其偶然的现象,或者说你并不想研究这种偶然的现象,这时可以将其删除。其次,如果异常值存在且代表了一种真实存在的现象,那就不能随便删除。在现有的欺诈场景中很多时候欺诈数据本身相对于正常数据勒说就是异常的,我们要把这些异常点纳入,重新拟合模型,研究其规律。能用监督的用监督模型,不能用的还可以考虑用异常检测的算法来做。
  • 注意test的数据不能删。
2.31 检测异常的方法一:3σ原则

检测异常的方法一:均方差 (3sigema原则:μ±3σ)
在统计学中,如果一个数据分布近似正态,那么大约 68% 的数据值会在均值的一个标准差范围内,大约 95% 会在两个标准差范围内,大约 99.7% 会在三个标准差范围内。

def find_outliers_by_3segama(data,fea):
    data_std = np.std(data[fea]) #标准差
    data_mean = np.mean(data[fea]) #均值
    outliers_cut_off = data_std * 3
    lower_rule = data_mean - outliers_cut_off #左下界
    upper_rule = data_mean + outliers_cut_off  #右下界
    data[fea+'_outliers'] = data[fea].apply(lambda x:str('异常值') if x > upper_rule or x < lower_rule else '正常值') #往data中增加一列处理过后的列数据
    return data
  • 得到特征的异常值后可以进一步分析变量异常值和目标变量的关系
for fea in numerical_fea:
    data_train = find_outliers_by_3segama(data_train,fea)
    print(data_train[fea+'_outliers'].value_counts()) #统计正常值与异常值的数量
    print(data_train.groupby(fea+'_outliers')['isDefault'].sum())  #返回基于fea_outliers分组后‘isDefault’(是否违约)列的总和,即统计出违约人数
    print('*'*10)
#下面展示部分数据

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第8张图片

#删除异常值
for fea in numerical_fea:
    data_train = data_train[data_train[fea+'_outliers']=='正常值']
    data_train = data_train.reset_index(drop=True)  # Data.set_index([‘a’,’b’])将a、b列作为新索引,创造分层索引 reset_index()拆解分层索引

关于reset_index(),若数据框之前曾经groupby分组过,那么拆解时应该引入drop参数

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第9张图片

2.32 检测异常的方法二:箱型图

-总结一句话:四分位数会将数据分为三个点和四个区间,IQR = Q3 -Q1,下触须=Q1 − 1.5x IQR,上触须=Q3 + 1.5x IQR;

python箱线图代码参考:https://zhuanlan.zhihu.com/p/148306737

2.4 数据分箱

  • 特征分箱的目的
    • 从模型效果上来看,特征分箱主要是为了降低变量的复杂性,减少变量噪音对模型的影响,提高自变量和因变量的相关度。从而使模型更加稳定。
  • 数据分桶的对象
    • 将连续变量离散化
    • 将多状态的离散变量合并成少状态
  • 分箱的原因
    • 数据的特征内的值跨度可能比较大,对有监督和无监督中如k-均值聚类它使用欧氏距离作为相似度函数来测量数据点之间的相似度。都会造成大吃小的影响,其中一种解决方法是对计数值进行区间量化即数据分桶也叫做数据分箱,然后使用量化后的结果。
  • 分箱的优点
    • 处理缺失值:当数据源可能存在缺失值,此时可以把null单独作为一个分箱。
    • 处理异常值:当数据中存在离群点时,可以把其通过分箱离散化处理,从而提高变量的鲁棒性(抗干扰能力)。例如,age若出现200这种异常值,可分入“age > 60”这个分箱里,排除影响。
    • 业务解释性:我们习惯于线性判断变量的作用,当x越来越大,y就越来越大。但实际x与y之间经常存在着非线性关系,此时可经过WOE变换。
  • 特别要注意一下分箱的基本原则
    • (1)最小分箱占比不低于5%
    • (2)箱内不能全部是好客户
    • (3)连续箱单调
2.41 固定宽度分箱

当数值横跨多个数量级时,最好按照 10 的幂(或任何常数的幂)来进行分组:0-9、10-99、100-999、1000-9999,等等。固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子

# 通过除法映射到间隔均匀的分箱中,每个分箱的取值范围都是loanAmnt/1000
data['loanAmnt_bin1'] = np.floor_divide(data['loanAmnt'], 1000)

## 通过对数函数映射到指数宽度分箱
data['loanAmnt_bin2'] = np.floor(np.log10(data['loanAmnt']))
2.42 分位数分箱
data['loanAmnt_bin3'] = pd.qcut(data['loanAmnt'], 10, labels=False)
2.43 卡方分箱及其他分箱方法的尝试

这个下次补上

2.5 特征交互

  • 特征和特征之间组合
  • 特征和特征之间衍生

交互特征的构造非常简单,使用起来却代价不菲。如果线性模型中包含有交互特征对,那它的训练时间和评分时间就会从 O(n) 增加到 O(n^2),其中 n 是单一特征的数量。
这里给出一些特征交互的思路,但特征和特征间的交互衍生出新的特征还远远不止于此
后续探索…

for col in ['grade', 'subGrade']: 
    temp_dict = data_train.groupby([col])['isDefault'].agg(['mean']).reset_index().rename(columns={
     'mean': col + '_target_mean'})  #返回的是各等级的违约率#
    temp_dict.index = temp_dict[col].values #将行索引改为对应的等级
    temp_dict = temp_dict[col + '_target_mean'].to_dict() #返回大概这样的结果{
     'grade_target_mean': {
     'A': A.mean,' B': B.mean,...}}

    data_train[col + '_target_mean'] = data_train[col].map(temp_dict)
    data_test_a[col + '_target_mean'] = data_test_a[col].map(temp_dict)
# 其他衍生变量 mean 和 std
for df in [data_train, data_test_a]:
    for item in ['n0','n1','n2','n2.1','n4','n5','n6','n7','n8','n9','n10','n11','n12','n13','n14']:
        df['grade_to_mean_' + item] = df['grade'] / df.groupby([item])['grade'].transform('mean')
        df['grade_to_std_' + item] = df['grade'] / df.groupby([item])['grade'].transform('std')  #transform其实和apply很像,只是功能更丰富

transform函数参考

2.6特征编码

one-hot编码与label-encoder编码

可以参考这个博文:独热编码(OneHotEncoder)和标签编码(LabelEncoder)

这个博文也可以看看:one hot 编码及数据归一化

#laberE你coder代码示例
#label-encode:subGrade,postCode,title
# 高维类别特征需要进行转换
for col in tqdm(['employmentTitle', 'postCode', 'title','subGrade']):
    le = LabelEncoder()
    le.fit(list(data_train[col].astype(str).values) + list(data_test_a[col].astype(str).values))
    data_train[col] = le.transform(list(data_train[col].astype(str).values))
    data_test_a[col] = le.transform(list(data_test_a[col].astype(str).values))
print('Label Encoding 完成')

逻辑回归等模型要单独增加的特征工程

  • 对特征做归一化,去除相关性高的特征
  • 归一化目的是让训练过程更好更快的收敛,避免特征大吃小的问题
  • 去除相关性是增加模型的可解释性,加快预测过程。
# 举例归一化过程
#伪代码
for fea in [要归一化的特征列表]:
    data[fea] = ((data[fea] - np.min(data[fea])) / (np.max(data[fea]) - np.min(data[fea])))

2.7 特征选择

  • 特征选择技术可以精简掉无用的特征,以降低最终模型的复杂性,它的最终目的是得到一个简约模型,在不降低预测准确率或对预测准确率影响不大的情况下提高计算速度。特征选择不是为了减少训练时间(实际上,一些技术会增加总体训练时间),而是为了减少模型评分时间。

特征选择的方法:

  • 1 Filter
    • 方差选择法
    • 相关系数法(pearson 相关系数)
    • 卡方检验
    • 互信息法
  • 2 Wrapper (RFE)
    • 递归特征消除法
  • 3 Embedded
    • 基于惩罚项的特征选择法
    • 基于树模型的特征选择
2.71 Filter
  • 基于特征间的关系进行筛选

方差选择法

  • 方差选择法中,先要计算各个特征的方差,然后根据设定的阈值,选择方差大于阈值的特征
from sklearn.feature_selection import VarianceThreshold
#其中参数threshold为方差的阈值
VarianceThreshold(threshold=3).fit_transform(train,target_train)

相关系数法

  • Pearson 相关系数
    皮尔森相关系数是一种最简单的,可以帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性。
    结果的取值区间为 [-1,1] , -1 表示完全的负相关, +1表示完全的正相关,0 表示没有线性相关。
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
#选择K个最好的特征,返回选择特征后的数据
#第一个参数为计算评估特征是否好的函数,该函数输入特征矩阵和目标向量,
#输出二元组(评分,P值)的数组,数组第i项为第i个特征的评分和P值。在此定义为计算相关系数
#参数k为选择的特征个数

SelectKBest(k=5).fit_transform(train,target_train)

卡方检验

  • 经典的卡方检验是用于检验自变量对因变量的相关性。 假设自变量有N种取值,因变量有M种取值,考虑自变量等于i且因变量等于j的样本频数的观察值与期望的差距。 其统计量如下: χ2=∑(A−T)2T,其中A为实际值,T为理论值
  • (注:卡方只能运用在正定矩阵上,否则会报错Input X must be non-negative)
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#参数k为选择的特征个数

SelectKBest(chi2, k=5).fit_transform(train,target_train)

互信息法

  • 经典的互信息也是评价自变量对因变量的相关性的。 在feature_selection库的SelectKBest类结合最大信息系数法可以用于选择特征,相关代码如下:
from sklearn.feature_selection import SelectKBest
from minepy import MINE
#由于MINE的设计不是函数式的,定义mic方法将其为函数式的,
#返回一个二元组,二元组的第2项设置成固定的P值0.5
def mic(x, y):
    m = MINE()
    m.compute_score(x, y)
    return (m.mic(), 0.5)
#参数k为选择的特征个数
SelectKBest(lambda X, Y: array(map(lambda x:mic(x, Y), X.T)).T, k=2).fit_transform(train,target_train)
2.72 Wrapper (RFE)
  • 递归特征消除法 递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。 在feature_selection库的RFE类可以用于选择特征,相关代码如下(以逻辑回归为例):
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
#递归特征消除法,返回特征选择后的数据
#参数estimator为基模型
#参数n_features_to_select为选择的特征个数

RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(train,target_train)
2.73 Embedded
  • 基于惩罚项的特征选择法 使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维。 在feature_selection库的SelectFromModel类结合逻辑回归模型可以用于选择特征,相关代码如下:
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
#带L1惩罚项的逻辑回归作为基模型的特征选择

SelectFromModel(LogisticRegression(penalty="l1", C=0.1)).fit_transform(train,target_train)
  • 基于树模型的特征选择 树模型中GBDT也可用来作为基模型进行特征选择。 在feature_selection库的SelectFromModel类结合GBDT模型可以用于选择特征,相关代码如下:
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import GradientBoostingClassifier
#GBDT作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(train,target_train)

本数据集中我们删除非入模特征后,并对缺失值填充,然后用计算协方差的方式看一下特征间相关性,然后进行模型训练

# 删除不需要的数据
for data in [data_train, data_test_a]:
    data.drop(['issueDate'], axis=1,inplace=True)

#"纵向用缺失值上面的值替换缺失值"
data_train = data_train.fillna(axis=0,method='ffill')

x_train = data_train
#计算协方差
data_corr = x_train.corrwith(data_train.isDefault) #计算相关性
result = pd.DataFrame(columns=['features', 'corr'])
result['features'] = data_corr.index
result['corr'] = data_corr.values

# 当然也可以直接看图
data_numeric = data_train[numerical_fea]
correlation = data_numeric.corr()

f , ax = plt.subplots(figsize = (7, 7))
plt.title('Correlation of Numeric Features with Price',y=1,size=16)
sns.heatmap(correlation,square = True,  vmax=0.8)

金融风控训练营赛题理解(task 3 特征工程)学习笔记_第10张图片
金融风控训练营赛题理解(task 3 特征工程)学习笔记_第11张图片

features = [f for f in data_train.columns if f not in ['id','issueDate','isDefault'] and '_outliers' not in f]
x_train = data_train[features]
x_test = data_test_a[features]
y_train = data_train['isDefault']
def cv_model(clf, train_x, train_y, test_x, clf_name):
    folds = 5
    seed = 2020
    kf = KFold(n_splits=folds, shuffle=True, random_state=seed)

    train = np.zeros(train_x.shape[0])
    test = np.zeros(test_x.shape[0])

    cv_scores = []

    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        print('************************************ {} ************************************'.format(str(i+1)))
        trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]

        if clf_name == "lgb":
            train_matrix = clf.Dataset(trn_x, label=trn_y)
            valid_matrix = clf.Dataset(val_x, label=val_y)

            params = {
     
                'boosting_type': 'gbdt',
                'objective': 'binary',
                'metric': 'auc',
                'min_child_weight': 5,
                'num_leaves': 2 ** 5,
                'lambda_l2': 10,
                'feature_fraction': 0.8,
                'bagging_fraction': 0.8,
                'bagging_freq': 4,
                'learning_rate': 0.1,
                'seed': 2020,
                'nthread': 28,
                'n_jobs':24,
                'silent': True,
                'verbose': -1,
            }

            model = clf.train(params, train_matrix, 50000, valid_sets=[train_matrix, valid_matrix], verbose_eval=200,early_stopping_rounds=200)
            val_pred = model.predict(val_x, num_iteration=model.best_iteration)
            test_pred = model.predict(test_x, num_iteration=model.best_iteration)
            
            # print(list(sorted(zip(features, model.feature_importance("gain")), key=lambda x: x[1], reverse=True))[:20])
                
        if clf_name == "xgb":
            train_matrix = clf.DMatrix(trn_x , label=trn_y)
            valid_matrix = clf.DMatrix(val_x , label=val_y)
            
            params = {
     'booster': 'gbtree',
                      'objective': 'binary:logistic',
                      'eval_metric': 'auc',
                      'gamma': 1,
                      'min_child_weight': 1.5,
                      'max_depth': 5,
                      'lambda': 10,
                      'subsample': 0.7,
                      'colsample_bytree': 0.7,
                      'colsample_bylevel': 0.7,
                      'eta': 0.04,
                      'tree_method': 'exact',
                      'seed': 2020,
                      'nthread': 36,
                      "silent": True,
                      }
            
            watchlist = [(train_matrix, 'train'),(valid_matrix, 'eval')]
            
            model = clf.train(params, train_matrix, num_boost_round=50000, evals=watchlist, verbose_eval=200, early_stopping_rounds=200)
            val_pred  = model.predict(valid_matrix, ntree_limit=model.best_ntree_limit)
            test_pred = model.predict(test_x , ntree_limit=model.best_ntree_limit)
                 
        if clf_name == "cat":
            params = {
     'learning_rate': 0.05, 'depth': 5, 'l2_leaf_reg': 10, 'bootstrap_type': 'Bernoulli',
                      'od_type': 'Iter', 'od_wait': 50, 'random_seed': 11, 'allow_writing_files': False}
            
            model = clf(iterations=20000, **params)
            model.fit(trn_x, trn_y, eval_set=(val_x, val_y),
                      cat_features=[], use_best_model=True, verbose=500)
            
            val_pred  = model.predict(val_x)
            test_pred = model.predict(test_x)
            
        train[valid_index] = val_pred
        test = test_pred / kf.n_splits
        cv_scores.append(roc_auc_score(val_y, val_pred))
        
        print(cv_scores)
        
    print("%s_scotrainre_list:" % clf_name, cv_scores)
    print("%s_score_mean:" % clf_name, np.mean(cv_scores))
    print("%s_score_std:" % clf_name, np.std(cv_scores))
    return train, test
def lgb_model(x_train, y_train, x_test):
    lgb_train, lgb_test = cv_model(lgb, x_train, y_train, x_test, "lgb")
    return lgb_train, lgb_test

def xgb_model(x_train, y_train, x_test):
    xgb_train, xgb_test = cv_model(xgb, x_train, y_train, x_test, "xgb")
    return xgb_train, xgb_test

def cat_model(x_train, y_train, x_test):
    cat_train, cat_test = cv_model(CatBoostRegressor, x_train, y_train, x_test, "cat")
lgb_train, lgb_test = lgb_model(x_train, y_train, x_test)

返回结果如下

************************************ 1 ************************************
Training until validation scores don't improve for 200 rounds
[200]	training's auc: 0.74899	valid_1's auc: 0.729686
[400]	training's auc: 0.764681	valid_1's auc: 0.730508
[600]	training's auc: 0.778892	valid_1's auc: 0.730374
Early stopping, best iteration is:
[440]	training's auc: 0.767697	valid_1's auc: 0.73054
[0.7305399905125283]
************************************ 2 ************************************
Training until validation scores don't improve for 200 rounds
[200]	training's auc: 0.74894	valid_1's auc: 0.731142
[400]	training's auc: 0.764652	valid_1's auc: 0.731723
[600]	training's auc: 0.77816	valid_1's auc: 0.731763
Early stopping, best iteration is:
[509]	training's auc: 0.77206	valid_1's auc: 0.731929
[0.7305399905125283, 0.7319289931459529]
************************************ 3 ************************************
Training until validation scores don't improve for 200 rounds
[200]	training's auc: 0.748202	valid_1's auc: 0.732441
[400]	training's auc: 0.763979	valid_1's auc: 0.733223
Early stopping, best iteration is:
[378]	training's auc: 0.762425	valid_1's auc: 0.733328
[0.7305399905125283, 0.7319289931459529, 0.73332775976538]
************************************ 4 ************************************
Training until validation scores don't improve for 200 rounds
[200]	training's auc: 0.749456	valid_1's auc: 0.727989
[400]	training's auc: 0.764402	valid_1's auc: 0.728576
Early stopping, best iteration is:
[390]	training's auc: 0.763724	valid_1's auc: 0.728673
[0.7305399905125283, 0.7319289931459529, 0.73332775976538, 0.7286733163044455]
************************************ 5 ************************************
Training until validation scores don't improve for 200 rounds
[200]	training's auc: 0.748363	valid_1's auc: 0.732701
[400]	training's auc: 0.76389	valid_1's auc: 0.733229
Early stopping, best iteration is:
[348]	training's auc: 0.76005	valid_1's auc: 0.733396
[0.7305399905125283, 0.7319289931459529, 0.73332775976538, 0.7286733163044455, 0.7333959358768154]
lgb_scotrainre_list: [0.7305399905125283, 0.7319289931459529, 0.73332775976538, 0.7286733163044455, 0.7333959358768154]
lgb_score_mean: 0.7315731991210244
lgb_score_std: 0.0017890937427853105

三、学习问题与解答

学习最大的问题是很多代码看不明白,需要大量的时间,由于时间不充裕,所以特征交互开始只是粗略的看了知识,后续补上代码

四、学习思考与总结

特征工程是机器学习,甚至是深度学习中最为重要的一部分,在实际应用中往往也是所花费时间最多的一步。各种算法书中对特征工程部分的讲解往往少得可怜,因为特征工程和具体的数据结合的太紧密,很难系统地覆盖所有场景。本章学习主要是通过一些常用的方法来做介绍,例如缺失值异常值的处理方法详细对任何数据集来说都是适用的。但对于分箱等操作本章学习给出了具体的几种思路,需要我们自己探索。
[^1]:https://www.jianshu.com/p/b34b8df3a3e1
[^2]:https://www.zhihu.com/question/29316149

你可能感兴趣的:(python,金融风控,机器学习,python,算法,机器学习)