前言
上篇《洪水数据集的回归预测》(上) 介绍了该数据集非常特殊之处,各特征都非常类似,没有特别之处,各特征之间的相关系数几乎为零。同时,各类模型不敏感,最理想的模型居然是线性回归,决定系数 R 2 R^2 R2,也只有 0.846。对这样的一个问题,我们如何突破呢?
方向一
既然线性模型效果相对于其他模型算是较好的,因此我们选择深度学习的MLP【多层感知器(multilayer Perceptron)】模型进行尝试,核心代码如下,
数据归一化
sc = preprocessing.StandardScaler()
X_scaled=sc.fit_transform(X)
X_valid_scaled = sc.transform(X_valid)
test_scaled = sc.transform(test)
建模
model = Sequential()
model.add(Dense(64, kernel_initializer = 'normal', activation = 'relu',
input_shape = (20,)))
model.add(Dense(64, activation = 'relu'))
model.add(Dense(32, activation = 'relu'))
model.add(Dense(1))
第一个版本,中间层只有 64 一层
编译
model.compile(loss = 'mse', optimizer = RMSprop(learning_rate=0.0005), metrics = ['mean_absolute_error']
)
这里选择 RMSprop 做为优化器,我尝试过用 Adam 效果差不多,同样,这里的学习率也可以做适当的调整。
训练
history = model.fit(X_scaled, y, batch_size=128, epochs = 500, verbose = 1, validation_split = 0.2, callbacks = [EarlyStopping(monitor = 'val_loss', patience = 20)]
)
6988/6988 ━━━━━━━━━━━━━━━━━━━━ 13s 2ms/step - loss: 3.5157e-04 - mean_absolute_error: 0.0147 - val_loss: 3.5295e-04 - val_mean_absolute_error: 0.0146
Epoch 147/500
6988/6988 ━━━━━━━━━━━━━━━━━━━━ 13s 2ms/step - loss: 3.5114e-04 - mean_absolute_error: 0.0147 - val_loss: 3.6086e-04 - val_mean_absolute_error: 0.0145
第一版本,训练数据和验证数据分别 用train_test_split方式来进行训练
采用早停的方式来终止训练,这里共训练了147次,MSE 可以达到0.0145~0.0146之前
LOSS 结果
plt.plot(history.epoch,history.history.get('loss'),label="loss")
plt.xlabel("epoch")
plt.ylabel("MSE")
plt.legend()
查看验证结果
y_valid_pred = model.predict(X_valid_scaled)
r2_score(y_valid,y_valid_pred)
0.8628182472445698
0.8628,这个结果大大的超过了预期,第一个版本成绩为0.859,因此就选用了这个标题了,现在所展示的是第二个版本,具体详见 完整代码
以下是我当天提交结果
正当以为找到方案一,调整模型层数和相关参数提升模型效果时,很快发现又出称瓶颈(0.8645),不得不需要找到另外的突破的方式。
方案二
在上篇留了一个坑 ,也在总结 写到"如果直接用上述的数据建模的话, R 2 R^2 R2 的上限不太可能会突破0.85",尽管方案一突破了0.85 ,来到了0.86 这个区别,难道不能上0.87 那个区间吗。因此,我们突破常规思路,必须从数据集入手,有效的进行特征工具(FE),提升这些特征的有效性。
上文提到那些特征没有特色,具体普遍性,而机器学习在寻找特征的基本思想就是
找出与别人不一校样的
东西,如果一组数据没有波动,也不能称之为特征,因此,需要我们找出不一样的波动的东西,顺着这个思路,我们把以上原有特征的统计量给找出来,看看能发现什么。
选择统计量分析
# 求出相关的统计量,并删除原特征
def cleaning(dataset):features = dataset.columns.tolist()dataset['mean_features'] = 0.1*dataset[features].mean(axis=1)dataset['std_features'] = dataset[features].std(axis=1)dataset['max_features'] = dataset[features].max(axis=1)dataset['min_features'] = dataset[features].min(axis=1)dataset['median_features'] = 0.1*dataset[features].median(axis=1)# just keep the descriptive statisticsdataset = dataset.drop(features, axis=1)return dataset
# 将原始数据和训练数据合并
dataset = pd.concat([original_data, train_data.drop('id', axis=1)], ignore_index=True)
X = dataset
X = X.drop(["FloodProbability"], axis=1)
X = cleaning(X)
y = dataset["FloodProbability"]
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats# Plot scatter plots and regression lines for each feature in separate plots
for i, col in enumerate(X.columns):plt.figure(figsize=(6, 4)) # Create a new figure for each feature# Scatter plot and regression linesns.regplot(x=X[col], y=y, color='darkturquoise', line_kws={'color': 'red'}, scatter_kws={'alpha': 0.5}) # Set alpha for dot transparency# Calculate linear regressionslope, intercept, r_value, p_value, std_err = stats.linregress(X[col], y)# Add title including the regression coefficientsplt.title(f'{col} vs Target\nSlope: {slope:.2f}, Intercept: {intercept:.2f}')# Setting labelsplt.xlabel(col)plt.ylabel('Target')plt.show()
我们惊奇的发现,这些统计量与目标值【FloodProbability】居有具较强的相关性。
以此为起点,我们以上述统计量为特征,进行建模分析。
以下没有优化的结果如下
模型 | 原始特征 R 2 R^2 R2 | 统计量特征的 R 2 R^2 R2 |
---|---|---|
LinearRegression | 0.845460 | 0.84561 |
Lasso | -5.51093 | -5.51093 |
Ridge | 0.845460 | 0.84561 |
ElasticNet | -5.51093 | -5.51093 |
SVR | 0.69667 | 0.78454 |
RandomForestRegressor | 0.65394 | 0.86045 |
XGBRegressor | 0.80942 | 0.869125 |
LGBMRegressor | 0.767198 | 0.869094 |
CatBoostRegressor | 0.84669 | 0.869264 |
DL-MLP | 0.862818 | 0.865859 |
以上表数据均为验证集的结果,未进行提交的成绩。
为了取得更好的成绩,我们将最好的三个模型进行融合。
xgb_pred = xgb.predict(test_t)
lgb_pred = lgb.predict(test_t)
cat_pred = cat.predict(test_t)
sample['FloodProbability'] = (xgb_pred+lgb_pred+cat_pred)/3
sample.to_csv("submit_melt.csv",index=None)
详见 notebook
最终,提交到竞赛中得到 0.86887的成绩,截止发稿,最好成绩为0.86932。
到这里暂告一段落,要想继续提升我们的成绩,还是有一定的空间,有几个方向供小伙伴参考
- 上述的三个模型都是采用默认参数的方式,都可以进行 optuna 进行优化,参照我的几篇文章,如《肥胖风险的多类别预测》的 Optuna 进行微调部分。
- 在融合方式中可以用不同权重进行优化。
- 模型训练时可以用5折交叉验证来提升模型的泛化能力。
总结
- 写这篇文章的初衷,是为了一种思维的突破,改变原先常规的特征基础,选用统计量作为特征,这是我在以往所没有碰到过的,基于这一点,拿来分享给大家。
- 这种方法其实是有条件的,并非所有的题目都可以这样,只有当这些特征具有以下特点是,如所有特征具有共同的特性,并其相关性为零,特征量不太少,用一般的树形模型没法提升时。
- 每个数据集基于特征都有一个理论上限,如果已接近这个天花板时,就需要改变原先的特征,像本题,是一种完全改变的方式,除此之外,有扩展,PCA等方式。
以上这些观点是我自己的感受和体会,并非一定正确,如有不想法和建议,欢迎评论。