- 一、概述
- 二、解题过程
- 2.1 数据
- 2.2 构建基线
- 2.3 进阶思路一
- 2.4 进阶思路二
- 2.5 进阶思路三
- 2.6 融合
- 2.7 调优提分过程
- 2.8 其他工作
- 三、结语
这是我第二次参加大数据类型的竞赛,也是第一次接触金融类的题目,这一题可以称作CCF BDCI这一年的究极卷王题,3200个队伍参加,1300多个队伍提交,比其他赛题多了几倍,最后尽力也只拿下B榜73/3246的名次。
在次记录自己的解题过程以及所思所想。
赛题地址:https://www.datafountain.cn/competitions/530
本次的数据由3个表组成,分别为个人贷款违约记录数据train_public.csv,某网络信用贷产品违约记录数据train_internet_public.csv,测试集test_public.csv。
数据说明请参考:https://www.datafountain.cn/competitions/530/datasets
通过查看数据,我们可以得知,这三个表可以大致理解为:和测试集分布较为接近的训练集(一万条)train_public,和测试集分布不同的训练集补充train_inte(70多万条),测试集。
很显然,本题的意思在于从补充测试集中找寻合适的数据扩充到原本的数据集中进行训练,然后预测提分,怎么样找到合适的数据成为本题的关键。
对于原始的数据,有一些初步的处理:
- 对于时间类型的数据,转化为统一的time格式,并可以细化为年,月,周等
- 对于string类型表达的数据,如工作类型等编码为数字类型
- 将空值用均值进行填充
对三张表均进行上述 *** 作,构建最基本的三张表
2.2 构建基线参考社区分享的基线,构建LGBM模型
clf = LGBMClassifier( n_estimators=4000, learning_rate=0.03, num_leaves=2 ** 5, colsample_bytree=.65, subsample=.9, max_depth=5, # max_bin=250, reg_alpha=.3, reg_lambda=.3, min_split_gain=.01, min_child_weight=2, )
首先测试仅将train_public一万条数据导入lgbm中进行训练并测试,采用5折交叉验证,最终的结果为0.8786,这即为最初的基线,在我看来,跑通代码是比赛的第一步,也是较为关键的一步o(╥﹏╥)o
2.3 进阶思路一同样参考社区提出的方法,第一个思路在于先使用train_public训练lgbm,然后使用训练得到的lgbm预测扩充数据集train_inte,设定一个筛选阈值,从中选择预测较为准确的样本。举个例子,我们根据原始训练集训练了lgbm,然后使用该lgbm预测train_inte,发现预测值<0.08的值(预测为0)其真实标签也为0,即预测全对,则我们将预测值<0.08的样本全部纳入原本训练集中进行扩充,此时数据来到7-10万条,代码大致如下:
#首先训练原始样本 oof_preds, IntePre, importances = train_model(train_data, train_inteSame, y, folds) IntePre['isDef'] = train_inte['is_default'] from sklearn.metrics import roc_auc_score roc_auc_score(IntePre['isDef'], IntePre.isDefault) ## 选择阈值0.05,从internet表中提取预测小于该概率的样本,并对不同来源的样本赋予来源值 InteId = IntePre.loc[IntePre.isDefault < 0.08, 'loan_id'].tolist() train_data['dataSourse'] = 1 test_public['dataSourse'] = 1 train_inteSame['dataSourse'] = 0 train_inteSame['isDefault'] = train_inte['is_default'] use_te = train_inteSame[train_inteSame.loan_id.isin(InteId)].copy() #扩充数据 data = pd.concat([train_data, test_public, use_te]).reset_index(drop=True) del data del train_data, test_public print(train.columns) print(test.columns) y = train['isDefault'] #再度训练以及预测 folds = KFold(n_splits=15, shuffle=True, random_state=2021) oof_preds, test_preds, importances = train_model(train, test, y, folds)
使用该方法可以有效扩充数据集,线上分来到0.8852
2.4 进阶思路二使用伪标签,该方法即先使用2.3中得到的模型预测测试集,然后再次设定一个阈值,将符合阈值的测试集数据纳入到训练集中,标签根据预测结果给定,此举可以进一步扩充数据集,使得模型看到更多类型的数据。
但值得注意的是,该方法要看赛事官方是否允许,本题中并未禁止
使用该方法后,线上分来到0.8925
第三个思路我们在最后几天才想到,即使用聚类的思想,将三张表凑在一起进行聚类,将与测试集聚类在一起的样本挑出来作为训练样本。
经过实验我们发现聚类的结果将大部分训练集和测试集分为一类,可见其分布一致,而train_inte中的数据被分为几类,从中挑选与测试集聚类结果尽量相同的样本,经过我们的挑选,在40万-60万之间(多次实验,记不清了)。
参考代码(此处我们使用了Kmeans,但实际上我们还使用过BIRCH,最后时间紧迫来不及详细测试孰优孰劣):
# 综合分类数据集 from numpy import where from sklearn.datasets import make_classification from sklearn.cluster import Birch,MiniBatchKMeans,KMeans from matplotlib import pyplot import pandas as pd import re import numpy as np from sklearn.preprocessing import LabelEncoder # 定义数据集 train_data = pd.read_csv(r'D:比赛CCF_BDCI_Moneyraw_datatrain_public.csv') test_public = pd.read_csv(r'D:比赛CCF_BDCI_Moneyraw_datatest_public.csv') train_inte = pd.read_csv(r'D:比赛CCF_BDCI_Moneyraw_datatrain_internet.csv') def findDig(val): fd = re.search('(d+-)', val) if fd is None: return '1-'+val return val + '-01' work_year_dict = { '< 1 year': 0, '1 year': 1, '2 years': 2, '3 years': 3, '4 years': 4, '5 years': 5, '6 years': 6, '7 years': 7, '8 years': 8, '9 years': 9, '10+ years': 10, } class_dict = { 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, } timeMax = pd.to_datetime('1-Dec-21') train_data['work_year'] = train_data['work_year'].map(work_year_dict) test_public['work_year'] = test_public['work_year'].map(work_year_dict) train_inte['work_year'] = train_inte['work_year'].map(work_year_dict) train_data['class'] = train_data['class'].map(class_dict) test_public['class'] = test_public['class'].map(class_dict) train_inte['class'] = train_inte['class'].map(class_dict) train_data['earlies_credit_mon'] = pd.to_datetime(train_data['earlies_credit_mon'].map(findDig)) test_public['earlies_credit_mon'] = pd.to_datetime(test_public['earlies_credit_mon'].map(findDig)) train_inte['earlies_credit_mon'] = pd.to_datetime(train_inte['earlies_credit_mon'].map(findDig)) cat_cols = ['employer_type', 'industry'] for col in cat_cols: lbl = LabelEncoder().fit(train_data[col]) train_data[col] = lbl.transform(train_data[col]) test_public[col] = lbl.transform(test_public[col]) train_inte[col] = lbl.transform(train_inte[col]) col_to_drop = ['issue_date', 'earlies_credit_mon','post_code','region'] train_data = train_data.drop(col_to_drop, axis=1) test_public = test_public.drop(col_to_drop, axis=1 ) train_inte = train_inte.drop(col_to_drop, axis = 1) train_inte = train_inte.drop(['sub_class'], axis = 1) same_cols = list(set(train_data.columns.tolist()) & set(test_public.columns.tolist()) & set(train_inte.columns.tolist())) data = pd.Dataframe() import pdb for col in same_cols: data[col] = pd.concat([train_data[col], test_public[col], train_inte[col]]) data[col] = data[col].fillna(data[col].mean()) y = np.zeros(len(data)) i = 0 print(data.shape) data = data.sample(765000) data = data.reset_index(drop = True) feat =[f for f in data.columns if f not in ['loan_id', 'user_id', 'isDefault']] label_csv = pd.Dataframe() label_csv['loan_id'] = data['loan_id'] label_csv['user_id'] = data['user_id'] col_name = 0 model = KMeans(n_clusters=3 , max_iter=1000) model.fit(data[feat]) label_csv['kmeans_predict'] = model.predict(data[feat]) # print(label_csv) label_csv.to_csv('聚类测试_3_no_batch.csv',index=False)
最后聚类得到的文件经过lgbm训练也能达到0.895附近
2.6 融合最后我们将几种思路用过线上分加权平均,得到最终结果
2.7 调优提分过程在具体比赛中,很多结果不是一蹴而就的,而是经过了多次调参的结果,在 *** 作中,以下参数对结果影响较大:
- 学习率大小
- 思路中提到的阈值
- 在调模型过程中,我仔细对比了数据,发现部分对于借贷而言是没有作用的,故删除,这一举措对线上分影响较大,我删去的特征有借贷具体的月、周、日,最早确立信贷的月份,邮编,地区等。当然具体问题具体分析,每道题都有不相关的特征,删除这些或有奇效。
- Kfold折数,15折效果明显优于5折
在比赛中,我们还做了许多其他方面的努力,但对于最终的结果并未产生足够的效果,列举为:
- 我们发现,数据中有一些噪声,如借贷人最早确立信贷的时间,居然有2070年的,我们查看了测试集的分布,添加时间约束,将样本筛选时间在1996-2002之间,但却使得线上分下降
- 我们做了一些新的特征,如,我们根据债务收入比和其借贷金额算出其工资等,但对于线上分的影响也是负面的
- 我们尝试使用xgboost,catboost,随机森林等集成学习模型训练融合,但结果不尽人意
感谢你能看到这里,希望我的经历能帮助到你。
竞赛新手,深知才疏学浅,如有错误之处欢迎评论区指出,在此不胜感激。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)