这一期学习进阶的特征提取与分析,构建深度学习方案,拿下更高分数,冲冲冲。
项目链接:Task3:尝试使用深度学习方案 - 飞书云文档 (feishu.cn)
前两期介绍了代码,没有仔细深化每个优化方向,这里进行补充并尝试新方案。下面是流程图:
之前介绍过优化方向 ,开始实践。
特征优化
增加历史平移。
# 历史平移
for i in range(10,36):
data[f'target_shift{i}'] = data.groupby('id')['target'].shift(i)
创建从 10 到 35 的滞后特征(共 26 个滞后特征),每个都是将 target
列平移 i
个时间步,从而获取上个阶段的信息,效果如下:
使用差分特征。
# 差分特征
for i in range(1,4):
data[f'target_shift10_diff{i}'] = data.groupby('id')['target_shift10'].diff(i)
计算序列中相邻时间步之间的差异,即当前行和前 i
行的差值,对target_shift10
列进行操作,效果如下:
增加窗口统计。
# 窗口统计
for win in [15,30,50,70]:
data[f'target_win{win}_mean'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').mean().values
data[f'target_win{win}_max'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').max().values
data[f'target_win{win}_min'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').min().values
data[f'target_win{win}_std'] = data.groupby('id')['target'].rolling(window=win, min_periods=3, closed='left').std().values
# 历史平移 + 窗口统计
for win in [7,14,28,35,50,70]:
data[f'target_shift10_win{win}_mean'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').mean().values
data[f'target_shift10_win{win}_max'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').max().values
data[f'target_shift10_win{win}_min'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').min().values
data[f'target_shift10_win{win}_sum'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').sum().values
data[f'target_shift710win{win}_std'] = data.groupby('id')['target_shift10'].rolling(window=win, min_periods=3, closed='left').std().values
构建不同的窗口大小,然后基于窗口范围进统计均值、最大值、最小值、中位数、方差的信息,可以反映最近阶段数据的变化情况。效果也是增加几列,不展示了。
模型融合
顾名思义,就是将多个模型的结果进行融合,最常见的就是加权求和。这里选择使用 lightgbm、xgboost 和 catboost 模型,依次跑完这三个模型,然后将三个模型的结果进行 取平均融合。
对于每个模型均选择经典的 K 折交叉验证方法进行离线评估,大体流程如下:
1、K 折交叉验证会把样本数据随机的分成K份;
2、每次随机的选择 K-1 份作为训练集,剩下的 1 份做验证集;
3、当这一轮完成后,重新随机选择 K-1 份来训练数据;
4、最后将 K 折预测结果取平均作为最终提交结果。
# 选择lightgbm模型
lgb_oof, lgb_test = cv_model(lgb, train[train_cols], train['target'], test[train_cols], 'lgb')
# 选择xgboost模型
xgb_oof, xgb_test = cv_model(xgb, train[train_cols], train['target'], test[train_cols], 'xgb')
# 选择catboost模型
cat_oof, cat_test = cv_model(CatBoostRegressor, train[train_cols], train['target'], test[train_cols], 'cat')
# 进行取平均融合
final_test = (lgb_test + xgb_test + cat_test) / 3
需要注意的地方就是它和 Task2 一样,必须修改 lightgbm 模型的参数,不要修改另外两个模型参数!!我以为都要调整一致,结果运行到最后才报错,千万小心,源代码只需要改一处即可。
代码的逻辑很清晰,这里就不解释了,右键运行!
加权平均融合的效果可能一般,另外一种就是 stacking 融合,stacking 是一种分层模型集成框架。以两层为例,第一层由多个基学习器组成,其输入为原始训练集,第二层的模型则是以第一层基学习器的输出作为特征加入训练集进行再训练,从而得到完整的 stacking 模型。
第一层:(类比cv_model函数)
-
划分训练数据为 K 折(5 折为例,每次选择其中四份作为训练集,一份作为验证集);
-
针对各个模型进行 随机森林 RF、极端随机树 ET、梯度提升决策树 GBDT、极限梯度提升 XGB,分别进行 5 次训练,每次训练保留一份样本用作训练时的验证,训练完成后分别对 Validation set,Test set 进行预测,对于 Test set 一个模型会对应 5 个预测结果,将这 5 个结果取平均;对于 Validation set 一个模型经过 5 次交叉验证后,所有验证集数据都含有一个标签。此步骤结束后:5 个验证集(总数相当于训练集全部)在每个模型下分别有一个预测标签,每行数据共有 4 个标签(4 个算法模型),测试集每行数据也拥有四个标签(4 个模型分别预测得到的)
第二层:(类比stack_model函数)
将训练集中的四个标签外加真实标签当作 五列新的特征作为新的训练集,选取一个训练模型,根据新的训练集进行训练,然后应用 测试集的四个标签组成的测试集 进行预测作为最终的result。
本质上,这种方法是在上一种融合的基础上增加了新的模型(下面选择 Ridge 回归模型),再次进行训练并融合。
上面已经讲解过原理了,这里只放关键部分,不再赘述。
显然,进一步的处理得到了更好的结果。后面介绍优化方向。
深度学习方案
深度学习的流程其实挺标准的,都是 读数据、预处理、选择网络架构、评估。
这里给出的代码是残缺的(疯狂报错),只有一个框架,具体细节还需要自己填充,反复试错,终于调试成功。
不要用默认的 keras,安装TensorFlow,我选择的是最新版。
导包以及后续的方法使用,全部进行更新。
from keras.src.optimizers import Adam
import tensorflow as tf
# 定义模型
def build_model(look_back, n_features, n_output):
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.LSTM(50, input_shape=(look_back, n_features)))
model.add(tf.keras.layers.RepeatVector(n_output))
model.add(tf.keras.layers.LSTM(50, return_sequences=True))
model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(1)))
model.compile(loss='mean_squared_error', optimizer=Adam(0.001))
return model
更新数据预处理过程,让它的 id 与数值进行对应,修改为下面的:
# 准备测试数据集
OOT = []
OOT_ids = []
for id, data in datasets.items():
a = data[:100, 3] # 电力数据在第3列
a = np.append(a, np.array([0] * (100 - len(a))))
OOT.append(a[::-1])
OOT_ids.append(data[:10, 0]) # id在第0列
数据结果的保存,结构的规整如下:
# 将预测结果与id和dt结合
result_df = pd.DataFrame({
'id': list(OOT_ids.flatten().tolist()),
'dt': list([10,9,8,7,6,5,4,3,2,1] * 5832),
# 使用 itertools.chain 展平三维列表
'target': list(chain.from_iterable(chain.from_iterable(predicted_values)))
})
# 展示结果
print(result_df.head())
result_df[['id', 'dt', 'target']].to_csv('submit.csv', index=None)
提交官网,看看结果。
这分数出来的时候把我整郁闷了,虽然知道会很低,但是没想到这么低。原因:训练的结果与 id 不匹配,我在最后手动添加,不能确保这一天的数据与这个结果对应。为什么?因为整个训练就用不到其他信息,给定代码中的数据 shape 是 (5832,100) 格式,就没有将 id 与结果关联,是一个巨大的错误。想要改进的话,必须弄清楚两者之间的一一对应关系。我尝试了很久,没成功,后续再尝试。
优化方向
- 在模型融合时,第二层的模型选择是 Ridge,显然可以更换为更高性能的模型
- 模型参数的选择是固定的,应该仿照 Task2 去寻找最优参数,然后进行筛选
- 同理,特征工程的特征也可以增多,不拘泥于那几个窗口统计
- 模型集成不一定非选择上面的三个,可以更换,提供泛化能力
- 深度学习方案中,重中之重就是将 id 加入训练或同步
- 而且不应该切割前 100 行去训练,增加数据的范围
- 预处理的每个 id 只分配了 5 个时间序列,可以扩展
- 每个时间点的特征数只有 1 个,可以增加
- 模型选择的是 LSTM,明显可以更换再尝试
- 同理,参数调优,增加细节,调整粒度。。。
方法很多,我也进行了不同的尝试,后续再放上来。
上一篇文章也有尝试:Datawhale AI 夏令营——电力需求挑战赛——Task2学习笔记-CSDN博客
标签:Task3,target,min,AI,win,模型,Datawhale,data,id From: https://blog.csdn.net/m0_63566347/article/details/140554999