1、问题背景
Kaggle本月月赛是报销交叉销售预测问题,所给数据集是基于原381109条数据集使用模型生成的11504798条训练数据。本文着重探讨大批量数据处理使用及模型优化部分,期待共同商讨,引出更优质解法。
2、问题描述
本次比赛的数据集(训练和测试)是从在健康保险交叉销售预测数据数据集上训练的深度学习模型生成的。特征分布与原始分布接近,但不完全相同。作为本次比赛的一部分,您可以随意使用原始数据集,既可以探索差异,也可以查看在训练中加入原始数据集是否能提高模型性能。
其中原始数据集地址:
Health Insurance Cross Sell Prediction 🏠 🏥 (kaggle.com)
月赛生成数据地址:
Binary Classification of Insurance Cross Selling | Kaggle
关于数据集的探索性分析与特征工程,由于其特征维度与分布于原始数据集十分相似,因此不再复述,感兴趣的可以参考另一篇分析性文章:
大批量数据分析挖掘思路-Kaggle项目:保险销售预测-CSDN博客
3、数据准备
读取包
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
from sklearn.model_selection import train_test_split
import xgboost as xgb
数据集概览
该数据集形状为11504798*11
该数据集非常大,使用pandas直接读取大小达到1053Mb,如此之大的数据量对于模型的训练和参数调整寻优来说将是巨大的时间开销。
df = pd.read_csv("D:\BaiduSyncdisk\python code\数据分析算法学习\Kaggle保险分类比赛\train.csv", index_col = "id", engine="pyarrow")
df.memory_usage().sum()/ 1024 ** 2
此处,参考该文章的数据压缩方法
【Kaggle】练习赛《保险交叉销售的二分类预测》-CSDN博客
将变量的数据类型按照最大值范围进行压缩,尽量将其数据类型压缩到最小,具体方法为:
def reduce_mem_usage(df):
start_mem = df.memory_usage().sum() / 1024 ** 2
print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
for col in df.columns:
col_type = df[col].dtype
if col_type != object:
c_min = df[col].min()
c_max = df[col].max()
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df[col] = df[col].astype(np.int32)
elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
df[col] = df[col].astype(np.int64)
else:
if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
df[col] = df[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
df[col] = df[col].astype(np.float32)
else:
df[col] = df[col].astype(np.float64)
else:
df[col] = df[col].astype('category')
end_mem = df.memory_usage().sum() / 1024 ** 2
print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
return df
由于pandas读取数据默认以最大数据类型存储,整型以int64读取,浮点型以float64读取,而该数据集几乎所有数据根本不需要如此大的数据类型,如Age列最大值为85,Region_Code列最大值为52,简单的int8、float16类型便能存储,将其转换为较小的数据类型可以减少内存压力,增加数据计算速度,将其进行转换,在该数据集可以减少70%内存,以此思路在处理大批量数据时可以作为有力参考。
压缩完成后:
在经过尝试后,发现加入原始数据集训练能有效提高AUC分数,因此使用同样方法,加入原始数据集和该数据集压缩并合并。
data = import_data(r"D:\BaiduSyncdisk\python code\数据分析算法学习\Kaggle保险分类比赛\train.csv", index_col = "id", engine="pyarrow")
data_original =import_data(r"D:\BaiduSyncdisk\python code\数据分析算法学习\kaggle保险交叉销售二元分类\train.csv", index_col = "id", engine="pyarrow")
3、数据编码
将分类特征进行数字编码以作为训练集输入,在该数据集中,Gender、Vehicle_Demage、Vehicle_Age为字符串变量,将其进行编码。Region_code等为浮点型的分类变量,将其转化为整型以加快运算速度.
data['Gender'] = data['Gender'].map({'Male': 1, 'Female': 0}).astype(np.int8)
data['Vehicle_Damage'] = data['Vehicle_Damage'].map({'Yes': 1, 'No': 0}).astype(np.int8)
data['Vehicle_Age'] = data['Vehicle_Age'].map({'< 1 Year': 0, '1-2 Year': 1, '> 2 Years': 2}).astype(np.int8)
data['Region_Code']=data['Region_Code'].astype(np.int16)
data['Policy_Sales_Channel']=data['Policy_Sales_Channel'].astype(np.int16)
data_original['Gender'] = data_original['Gender'].map({'Male': 1, 'Female': 0}).astype(np.int8)
data_original['Vehicle_Damage'] = data_original['Vehicle_Damage'].map({'Yes': 1, 'No': 0}).astype(np.int8)
data_original['Vehicle_Age'] = data_original['Vehicle_Age'].map({'< 1 Year': 0, '1-2 Year': 1, '> 2 Years': 2}).astype(np.int8)
data_original['Region_Code']=data_original['Region_Code'].astype(np.int16)
data_original['Policy_Sales_Channel']=data_original['Policy_Sales_Channel'].astype(np.int16)
4、数据划分
将比赛数据集进行切割,由于数据量较大,考虑只保留0.05数据作为测试数据,切割完毕后将比赛数据集的train部分与原始数据集链接,共同作为训练集。
x=data.drop(columns=['Response'],axis=1)
y=data['Response']
x_original=data_original.drop(columns=['Response'],axis=1)
y_original=data_original['Response']
#分割数据集
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.05,random_state=42)
#x_test和y——test作为最终测试,x_train和y_train和原始数据集作为训练
x_train=pd.concat([x_train,x_original],axis=0)
y_train=pd.concat([y_train,y_original],axis=0)
5、建立模型
建立xgboost模型,该超参数参考cndrip博主,进行了细微修改,在多次测试下,减少了学习率、提升了最大深度,得到了较优效果,其中超参数如下:
xgb_params = {
'eval_metric': 'auc',
'n_estimators': 10000,
'eta': 0.15,
'alpha': 0.10,
# 'alpha': 0.1269124823585012,
'subsample': 0.8345882521794742,
'colsample_bytree': 0.44270196445757065,
'max_depth': 17,
'tree_method': 'hist',
'min_child_weight': 8,
'gamma': 1.308021832047589e-08,
'max_bin': 50000,
'n_jobs': -1,
'device': 'cuda',
'enable_categorical': True,
'early_stopping_rounds': 200,
}
建立模型
model=xgb.XGBClassifier(**xgb_params)
model.fit(
x_train,
y_train,
eval_set=[(x_test, y_test)],
verbose=500
)
经过多次测试,由于学习率较小,虽然减缓了收敛速度,但是取得了较好的效果,在本地测试最终最高AUC值达到了0.89126
6、提交结果
test = import_data(r"D:\BaiduSyncdisk\python code\数据分析算法学习\Kaggle保险分类比赛\test.csv", index_col = "id", engine="pyarrow")
#编码
test['Gender'] = test['Gender'].map({'Male': 1, 'Female': 0}).astype(np.int8)
test['Vehicle_Damage'] = test['Vehicle_Damage'].map({'Yes': 1, 'No': 0}).astype(np.int8)
test['Vehicle_Age'] = test['Vehicle_Age'].map({'< 1 Year': 0, '1-2 Year': 1, '> 2 Years': 2}).astype(np.int8)
y_test=model.predict_proba(test)[:,1]
result_df = pd.DataFrame({
'id': test.index, # 将test的索引作为id列
'Response': y_test # 将y_test的Response列作为Response列
})
result_df.to_csv("submission.csv",index=False)
将结果的概率保存为csv进行提交,以下为提交结果,最高达到0.89203,位次为223/1468。
7、总结
(1)使用适当的数据压缩方法,可以有效的较少内存,加快训练过程。
(2)对于多次训练提升不明显的现象,可以尝试降低学习率,以进行更细致的搜索。
(3)需慎用采样方案,在该数据集中,样本分布不平衡,当使用了不同的采样方案后发现,在该以AUC指标为主的数据集中,欠采样不如somte采样,而somote采样不如随机过采样,当我停止进行采样方案以原本数据集进行训练时,AUC值有较大的提升,可能是由于采样方案改变了原数据集的分布,导致学习效果降低。
8、参考文章
【Kaggle】练习赛《保险交叉销售的二分类预测》-CSDN博客
9、源代码