理论学习
赛题背景
回顾总结整个比赛的知识,首先是赛题用了AUC作为评价指标,这是一个医学上的指标,通过衡量ROC曲线与坐标轴围成的面积评价模型预测结果,它从对阳性和阴性的预测两方面进行了考虑,在许多情况下更为合理,因为阳性没检测出的后果可能更严重。
特征工程
在面对真实数据时,常常会遇到难以直接使用的数据,将这些数据正则化并筛选出对分析真正有用的数据很重要,可以说是整个比赛最关键的一环。
在粗略了解了数据的规模和大致分布后,先找出数据缺失的值,根据语境进行填补,然后将类别型和离散型数据进行处理,将一些字符串数据进行转换。
对于本次比赛,我没考虑过特征之间的关系,有的方法可以利用特征之间的关系生成高维特征,在之后的比赛中可以尝试它的效果。
数据分箱也是非常实用的方法,可以降低复杂性,减少噪声的影响。
特征选择有卡方检验、相关系数等方法,还有递归消除,前者主要是特征间的相关性,后者是进行多轮训练,不断消除若干权值系数的特征。
调参与融合
本次比赛主要实用了XGBoost模型、LightGBM模型和CatBoost模型,这三个也是比赛中很常见的模型,对于数据集的划分,交叉验证法会比简单的划分效果好,更能避免过拟合。调参方面最直接的方法就是贪心调参,这种方法比较好理解,贝叶斯调参考虑了上一次的参数信息,好处是是一个黑箱,适用面很广。模型融合方面这次我使用了stacking,效果得到了提升,在参与类似的数据挖掘比赛时,由于不用考虑实际生产生活的限制,融合大量的模型往往能得到一个较好的结果,在接下来的比赛中我打算尝试更多的方法进行模型融合,作为一个突破口。
实践
print(f'There are {data_train.isnull().any().sum()} columns in train dataset with missing values.')
# nan可视化
missing = data_train.isnull().sum()/len(data_train)
missing = missing[missing > 0]
missing.sort_values(inplace=True)
missing.plot.bar()
one_value_fea_test = [col for col in data_test_a.columns if data_test_a[col].nunique() <= 1]
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)))
#过滤数值型类别特征
def get_numerical_serial_fea(data,feas):
numerical_serial_fea = []
numerical_noserial_fea = []
for fea in feas:
temp = data[fea].nunique()
if temp <= 10:
numerical_noserial_fea.append(fea)
continue
numerical_serial_fea.append(fea)
return numerical_serial_fea,numerical_noserial_fea
numerical_serial_fea,numerical_noserial_fea = get_numerical_serial_fea(data_train,numerical_fea)
#每个数字特征得分布可视化
f = pd.melt(data_train, value_vars=numerical_serial_fea)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")
plt.figure(figsize=(8, 8))
sns.barplot(data_train["employmentLength"].value_counts(dropna=False)[:20],
data_train["employmentLength"].value_counts(dropna=False).keys()[:20])
plt.show()
#转化成时间格式 issueDateDT特征表示数据日期离数据集中日期最早的日期(2007-06-01)的天数
data_train['issueDate'] = pd.to_datetime(data_train['issueDate'],format='%Y-%m-%d')
startdate = datetime.datetime.strptime('2007-06-01', '%Y-%m-%d')
data_train['issueDateDT'] = data_train['issueDate'].apply(lambda x: x-startdate).dt.days
#按照平均数填充数值型特征
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())
def employmentLength_to_int(s):
if pd.isnull(s):
return s
else:
return np.int8(s.split()[0])
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)
data['employmentLength'] = data['employmentLength'].apply(employmentLength_to_int)
###异常检测 均方差
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 '正常值')
return data