这是我第二次参加大数据类型的竞赛,也是第一次接触金融类的题目,这一题可以称作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多万条),测试集。
很显然,本题的意思在于从补充测试集中找寻合适的数据扩充到原本的数据集中进行训练,然后预测提分,怎么样找到合适的数据成为本题的关键。
对于原始的数据,有一些初步的处理:
对三张表均进行上述操作,构建最基本的三张表
参考社区分享的基线,构建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
同样参考社区提出的方法,第一个思路在于先使用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.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_Money\raw_data\train_public.csv')
test_public = pd.read_csv(r'D:\比赛\CCF_BDCI_Money\raw_data\test_public.csv')
train_inte = pd.read_csv(r'D:\比赛\CCF_BDCI_Money\raw_data\train_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附近
最后我们将几种思路用过线上分加权平均,得到最终结果
在具体比赛中,很多结果不是一蹴而就的,而是经过了多次调参的结果,在操作中,以下参数对结果影响较大:
在比赛中,我们还做了许多其他方面的努力,但对于最终的结果并未产生足够的效果,列举为:
感谢你能看到这里,希望我的经历能帮助到你。
竞赛新手,深知才疏学浅,如有错误之处欢迎评论区指出,在此不胜感激。