云积分竞赛:
评分标准
评分算法通过logarithmic loss(记为logloss)评估模型效果,logloss越小越好。
目标:预估用户人群在 规定时间内产生购买行为的概率 1买, 0不买
其中N 表示测试集样本数量,
yi 表示测试集中 第i个样本的真实标签,
pi 表示第 i个样本的预估转化率,
δ 为惩罚系数。
AB榜的划分方式和比例:
【1】评分采用AB榜形式。排行榜显示A榜成绩,竞赛结束后2小时切换成B榜单。B榜成绩以选定的两次提交或者默认的最后两次提交的最高分为准,最终比赛成绩以B榜单为准。
【2】此题目的AB榜数据采用 同时段数据 是随机划分,A榜为随机抽样测试集50%数据,B榜为其余50%的数据。
数据集:https://pan.baidu.com/s/1cPX5jPCuOLDWkEGtCg6OcQ
提取码:uqb5
过程:
-
赛题分析
-
数据去操
-
特征选择、构造:挑选有用的特征,生成一些间接的特征(https://zhuanlan.zhihu.com/p/32749489)
-
模型选择
-
结果评估
数据和特征 决定了机器学习的上限 ,而模型和算法 只是逼近这个上限而已
特征产生过程:产生过程,评估过程,停止条件, 验证过程
特征选择方法分为三大类:
Filter:过滤法,Wrapper:包装法,Embedded:嵌入法
一、Filter:过滤法:
1 去掉取值 变化小的特征 Removing features with low variance
假设某特征的特征值只有0和1,并且在所有输入样本中,95%的实例的该特征取值都是1,那就可以认为这个特征作用不大。如果100%都是1,那这个特征就没意义了。
当特征值都是离散型变量的时候这种方法才能用,如果是连续型变量,就需要将连续变量离散化之后才能用,而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。
2 单变量特征选择 Univariate feature selection
2.1、Pearson相关系数
皮尔森相关系数是一种最简单的,能帮助理解 特征和响应变量 之间关系的方法,该方法衡量的是 变量之间的线性相关性,结果的取值区间为 [-1,1] , -1表示完全的负相关(这个变量下降,那个就会上升), 1表示完全的正相关,0 表示没有线性相关。
Pearson Correlation速度快、易于计算,经常在拿到数据( 经过清洗和特征提取之后的)之后第一时间就执行。Scipy的pearsonr方法能够同时计算相关系数和p-value
2.2、卡方验证
经典的卡方检验是检验定性自变量对定性因变量的相关性。假设自变量有N种取值,因变量有M种取值,考虑自变量等于i 且因变量等于j的样本频数的观察值与期望的差距,构建统计量:
不难发现,这个统计量的含义简而言之就是自变量对因变量的相关性。
2.3、 互信息和最大信息系数 Mutual information and maximal information coefficient (MIC)
经典的互信息也是评价定性自变量对定性因变量的相关性的
-
距离相关系数
-
方差选择法:
过滤特征选择法还有一种方法 不需要度量特征 类别标签 y的信息量。这种方法先要计算 各个特征的方差,然后根据 阈值,选择方差大于阈值的特征。
VarianceThreshold是特征选择的简单基线方法。它删除方差不符合某个阈值的所有特征。默认情况下,它会删除所有零差异特征,即所有样本中具有相同值的特征。
二、Wrapper:包装法:
Wrapper这里指不断地使用不同的特征组合来测试学习算法进行特征选择。先选定特定算法, 一般会选用普遍效果较好的算法, 例如Random Forest, SVM, kNN等等。
前向搜索:前向搜索说白了就是每次增量地从剩余未选中的特征选出一个加入特征集中,待达到 阈值或者 n 时,从所有的 F 中选出错误率最小的.
后向搜索:先将 F 设置为{1,2,... ,n} ,然后每次删除一个特征,并评价,直到达到阈值或者为空,然后选择最佳的 F
递归特征消除法:递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练。
三、Embedded:嵌入法:
基于惩罚项的特征选择法:
通过L1正则项来选择特征:L1正则方法具有稀疏解的特性,因此天然具备 特征选择的特性,但是要注意,L1没有选到的特征不代表不重要,原因是两个具有高相关性的特征可能只保留了一个,如果要确定哪个特征重要应再通过L2正则方法交叉检验。
基于学习模型的特征排序:
用的机器学习算法,针对每个单独的 特征和响应变量 建立预测模型。假如某个特征和响应变量之间的关系是非线性的,可以用基于树的方法(决策树、随机森林)、或者扩展的线性模型等。基于树的方法比较易于使用,因为他们对非线性关系的建模比较好,并且不需要太多的调试。但要注意 过拟合问题,因此树的深度最好不要太大,再就是运用交叉验证。通过这种训练对特征进行打分获得相关性后再训练最终模型。
我们的建模思想:
我们总共构建了75个指标,其中包括52个基本指标,23个重要指标的变换,平方,对数
购买次数: customer_counts
省份: customer_province
城市: customer_city
......
首先对其中的异常值进行处理,包括用众数填充,平均数填充。
之后对数据中的噪声(特殊值)进行处理,删除,改为指定数值,进而缩小数据的范围。
之后对范围较大的数据,做离差标准化处理,映射到(0,1)区间内。
# 此处只是列举了特征选取的一部分
for idx, data in enumerate([train_last, train_all]):
customer_all = pd.DataFrame(data[['customer_id']]).drop_duplicates(['customer_id']).dropna()
data = data.sort_values(by=['customer_id', 'order_pay_time'])
data['count'] = 1
# 一、购买次数
tmp = data.groupby(['customer_id'])['count'].agg({'customer_counts': 'count'}).reset_index()
customer_all = customer_all.merge(tmp, on=['customer_id'], how='left')
# 二、 省份 , last() 由迭代式获取其中的值, reset_index() 重置索引
tmp = data.groupby(['customer_id'])['customer_province'].last().reset_index()
customer_all = customer_all.merge(tmp, on=['customer_id'], how='left')
# 三、城市
tmp = data.groupby(['customer_id'])['customer_city'].last().reset_index()
customer_all = customer_all.merge(tmp, on=['customer_id'], how='left')
# 四、long_time : 在train 训练集中的 last - first 的时长, order_pay_date_last : 统计这个用户的订单最后一次购买时间
last_time = data.groupby(['customer_id'], as_index=False)['order_pay_time'].agg(
{'order_pay_date_last': 'max', 'order_pay_date_first': 'min'}).reset_index()
tmp['long_time'] = pd.to_datetime(last_time['order_pay_date_last']) - pd.to_datetime(last_time['order_pay_date_first'])
tmp['long_time'] = tmp['long_time'].dt.days + 1
del tmp['customer_city']
customer_all = customer_all.merge(tmp, on=['customer_id'], how='left')
自定义loss函数,实现线下、线上loss值偏差不大
# 由 loss 值计算score
def re_loglossv(labels,preds):
deta = 3.45
y_true = labels # you can try this eval metric for fun
y_pred = preds
p = np.clip(y_pred, 1e-10, 1-1e-10)
loss = -1/len(y_true) * np.sum(y_true * np.log(p) * deta + (1 - y_true) * np.log(1-p))
return 're_logloss',loss,False
解决倾斜特征
# 解决倾斜特征
numeric_dtypes = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
numeric = []
for i in last.columns:
if last[i].dtype in numeric_dtypes:
numeric.append(i)
为所有特征绘制箱型线图
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_style('white')
f, ax = plt.subplots(figsize=(8, 7))
ax.set_xscale('log')
ax = sns.boxplot(data=last[numeric], orient='h', palette='Set1')
ax.xaxis.grid(False)
ax.set(ylabel='Feature names')
ax.set(xlabel='Numeric values')
ax.set(title='Numeric Distribution of Features')
sns.despine(trim=True, left=True)
对数据的偏移进行修正,用scipy函数boxcox1p来计算Box-Cox转换,目标是使数据规范化
# 寻找偏弱的特征
from scipy.stats import skew, norm
skew_features = last[numeric].apply(lambda x: skew(x)).sort_values(ascending=False)
high_skew = skew_features[skew_features > 0.5]
skew_index = high_skew.index
skewness = pd.DataFrame({'Skew': high_skew})
skew_features.head(10)
# 用scipy函数boxcox1p来计算Box-Cox转换。我们的目标是找到一个简单的转换方式使数据规范化。
from scipy.special import boxcox1p
from scipy.stats import boxcox_normmax
for i in skew_index:
last[i] = boxcox1p(last[i], boxcox_normmax(last[i] + 1))
# 处理所有的 skewed values
sns.set_style('white')
f, ax = plt.subplots(figsize=(8, 7))
ax.set_xscale('log')
ax = sns.boxplot(data=last[skew_index], orient='h', palette='Set1')
ax.xaxis.grid(False)
ax.set(ylabel='Feature names')
ax.set(xlabel='Numeric values')
# 构造 logs 特征, squares 特征
def logs(res, ls):
m = res.shape[1]
for l in ls:
res = res.assign(newcol=pd.Series(np.log(1.01 + res[l])).values)
res.columns.values[m] = l + '_log'
m += 1
return res
def squares(res, ls):
m = res.shape[1]
for l in ls:
res = res.assign(newcol=pd.Series(res[l] * res[l]).values)
res.columns.values[m] = l + '_sq'
m += 1
return res
XGB模型参数设置:
import xgboost as xgb
# xgb 模型
from sklearn.model_selection import KFold, RepeatedKFold, StratifiedKFold
folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=2019)
# xgb模型参数设置
xgb_params = {"booster": 'gbtree',
'eta': 0.005,
'max_depth': 5,
'subsample': 0.7,
'colsample_bytree': 0.8,
'objective': 'binary:logistic',
'eval_metric': 'logloss',
'silent': True,
'nthread': 8,
'scale_pos_weight': 2.5 # 处理正负样本不均衡
}
# -----------------------------------------------------------------------------------------------------------
oof_xgb = np.zeros(len(X_train))
predictions_xgb = np.zeros(len(X_valid))
watchlist = [(xgb.DMatrix(X_train.as_matrix(), y_train.as_matrix()), 'train'),
(xgb.DMatrix(X_valid.as_matrix(), y_valid.as_matrix()), 'valid_data')]
clf = xgb.train(dtrain=xgb.DMatrix(np.array(X_train), np.array(y_train)), num_boost_round=500, evals=watchlist,
early_stopping_rounds=200,
verbose_eval=100, params=xgb_params, feval=myFeval)
oof_xgb = clf.predict(xgb.DMatrix(X_valid.as_matrix()), ntree_limit=clf.best_ntree_limit)
pred_xgb = clf.predict(xgb.DMatrix(X_all.as_matrix()), ntree_limit=clf.best_ntree_limit)
res = all_data[['customer_id']]
res['result'] = pred_xgb
# 保存 xgb模型
# clf.save_model('./xgb.model_true_false')
# load model
# bst2 = xgb.Booster(model_file='xgb.model1')
XGB模型的基本参数如下: 这些参数用来控制XGBoost的宏观功能
1、booster[默认gbtree] 选择每次迭代的模型,有两种选择: gbtree:基于树的模型 gbliner:线性模型
2、silent[默认0] 当这个参数值为1时,静默模式开启,不会输出任何信息。 一般这个参数就保持默认的0,因为这样能帮我们更好地理解模型。
3、nthread[默认值为最大可能的线程数] 这个参数用来进行多线程控制,应当输入系统的核数。 如果你希望使用CPU全部的核,那就不要输入这个参数,算法会自动检测它。 还有两个参数,XGBoost会自动设置,目前你不用管它。接下来咱们一起看booster参数。
二、booster参数 尽管有两种booster可供选择,我这里只介绍tree booster,因为它的表现远远胜过linear booster,所以linear booster很少用到。
1、eta[默认0.3] 和GBM中的 learning rate 参数类似。 通过减少每一步的权重,可以提高模型的鲁棒性。 典型值为0.01-0.2。
2、min_child_weight[默认1] 决定最小叶子节点样本权重和。 和GBM的 min_child_leaf 参数类似,但不完全一样。XGBoost的这个参数是最小样本权重的和,而GBM参数是最小样本总数。 这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。 但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。
3、max_depth[默认6] 和GBM中的参数相同,这个值为树的最大深度。 这个值也是用来避免过拟合的。max_depth越大,模型会学到更具体更局部的样本。 需要使用CV函数来进行调优。 典型值:3-10
4、max_leaf_nodes 树上最大的节点或叶子的数量。 可以替代max_depth的作用。因为如果生成的是二叉树,一个深度为n的树最多生成n2个叶子。 如果定义了这个参数,GBM会忽略max_depth参数。
5、gamma[默认0] 在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。 这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关,所以是需要调整的。
6、max_delta_step[默认0] 这参数限制每棵树权重改变的最大步长。如果这个参数的值为0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。 通常,这个参数不需要设置。但是当各类别的样本十分不平衡时,它对逻辑回归是很有帮助的。 这个参数一般用不到,但是你可以挖掘出来它更多的用处。
7、subsample[默认1] 和GBM中的subsample参数一模一样。这个参数控制对于每棵树,随机采样的比例。 减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。 典型值:0.5-1
8、colsample_bytree[默认1] 和GBM里面的max_features参数类似。用来控制每棵随机采样的列数的占比(每一列是一个特征)。 典型值:0.5-1
9、colsample_bylevel[默认1] 用来控制树的每一级的每一次分裂,对列数的采样的占比。 我个人一般不太用这个参数,因为subsample参数和colsample_bytree参数可以起到相同的作用。但是如果感兴趣,可以挖掘这个参数更多的用处。
10、lambda[默认1] 权重的L2正则化项。(和Ridge regression类似)。 这个参数是用来控制XGBoost的正则化部分的。虽然大部分数据科学家很少用到这个参数,但是这个参数在减少过拟合上还是可以挖掘出更多用处的。
11、alpha[默认1] 权重的L1正则化项。(和Lasso regression类似)。 可以应用在很高维度的情况下,使得算法的速度更快。
12、scale_pos_weight[默认1] 在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。
三、学习目标参数 这个参数用来控制理想的优化目标和每一步结果的度量方法。
1、objective[默认reg:linear] 这个参数定义需要被最小化的损失函数。最常用的值有: binary:logistic 二分类的逻辑回归,返回预测的概率(不是类别)。 multi:softmax 使用softmax的多分类器,返回预测的类别(不是概率)。 在这种情况下,你还需要多设一个参数:num_class(类别数目)。 multi:softprob 和multi:softmax参数一样,但是返回的是每个数据属于各个类别的概率。
2、eval_metric[默认值取决于objective参数的取值] 对于有效数据的度量方法。 对于回归问题,默认值是rmse,对于分类问题,默认值是error。 典型值有: rmse 均方根误差(∑Ni=1ϵ2N−−−−−√) mae 平均绝对误差(∑Ni=1|ϵ|N) logloss 负对数似然函数值 error 二分类错误率(阈值为0.5) merror 多分类错误率 mlogloss 多分类logloss损失函数 auc 曲线下面积
3、seed(默认0) 随机数的种子 设置它可以复现随机数据的结果,也可以用于调整参数
对于XGB参数的调整
可参加:https://blog.csdn.net/u012735708/article/details/83651832
五折交叉验证:
# 五折交叉验证
for fold_, (trn_idx, val_idx) in enumerate(folds.split(train_x, train_y)):
print("fold n°{}".format(fold_ + 1))
trn_data = xgb.DMatrix(train_x[trn_idx], train_y[trn_idx])
val_data = xgb.DMatrix(train_x[val_idx], train_y[val_idx])
watchlist = [( trn_data, 'train'), (val_data, 'valid_data')]
clf = xgb.train(dtrain=trn_data, num_boost_round=300, evals=watchlist, early_stopping_rounds=200,
verbose_eval=100, params=xgb_params, feval=myFeval)
oof_xgb[val_idx] = clf.predict(xgb.DMatrix(train_x[val_idx]), ntree_limit=clf.best_ntree_limit)
pred_xgb += clf.predict(xgb.DMatrix(X_all.as_matrix()), ntree_limit=clf.best_ntree_limit) / folds.n_splits