返回列表

7th Place Solution - Wavenet and Some Tricks

586. Child Mind Institute - Detect Sleep States | child-mind-institute-detect-sleep-states

开始: 2023-09-05 结束: 2023-12-05 健康管理与公共卫生 数据算法赛
第7名解决方案 - Wavenet 和一些技巧

第7名解决方案 - Wavenet 和一些技巧

比赛排名: 第7名
作者: Ahmet Erdem 和 Rob Mulla
发布时间: 2023-12-06

首先,感谢我的队友 @robikscube。这是一次有趣的旅程。我们注意到公共排行榜(Public LB)不太可靠,因此重点关注了交叉验证(CV)。最终我们的交叉验证得分约为 0.826。

我们的主要模型

我们的主要模型是一个经过修改的 wavenet。 它处理的是按分钟聚合的 3 天时间序列数据。

class SleepModel(nn.Module):
    def __init__(self, inch, kernel_size):
        super(SleepModel, self).__init__()
        emb_size = 4
        
        self.minute_emb = nn.Embedding(15, embedding_dim=emb_size)
        
        self.bn = nn.BatchNorm1d(inch-1)
        self.wave_block1 = Wave_Block(inch-1+emb_size, 32, 8, kernel_size)
        self.wave_block2 = Wave_Block(32, 32, 7, kernel_size, base=2.25)
        self.wave_block3 = Wave_Block(32, 64, 4, kernel_size)
        self.wave_block4 = nn.Sequential(nn.Conv1d(128, 64, kernel_size=5, dilation=DAY_LEN//6), 
                                         nn.BatchNorm1d(64), nn.LeakyReLU(),
                                         nn.Conv1d(64, 64, kernel_size=5, dilation=DAY_LEN//3), 
                                         nn.BatchNorm1d(64), nn.LeakyReLU()
                                        )
        self.top = nn.Conv1d(64, 3, kernel_size=1)
        self.top2 = nn.Conv1d(64, 2, kernel_size=1)

        self.gn1 = nn.GroupNorm(4, 32)
        self.gn2 = nn.GroupNorm(4, 32)
        
        self.avgpool = nn.AvgPool1d(kernel_size=DAY_LEN+1, padding=DAY_LEN//2, stride=1)
        self.maxpool = nn.MaxPool1d(kernel_size=DAY_LEN+1, padding=DAY_LEN//2, stride=1)

    def forward(self, x):
        x = torch.cat([x[:, -DAY_LEN:]*0, x, x[:, :DAY_LEN]*0], axis=1)
        x = x.permute(0, 2, 1)
        minute, x = x[:, -1, :], x[:, :-1, :]
        
        x[:, -1, :] += minute / 60 # hour + minute/60
        x = self.bn(x)
        
        minute_emb = self.minute_emb(torch.fmod(minute, 15).long()) # minute % 15 is an important feature
        x = torch.cat([x, minute_emb.permute(0, 2, 1)], axis=1)

        x = self.wave_block1(x)
        x = self.gn1(x)
        x = self.wave_block2(x)
        x = self.gn2(x)
        x = self.wave_block3(x)
                
        x = torch.cat([x, self.avgpool(x[:, :32]), self.maxpool(x[:, 32:])], axis=1)

        x = self.wave_block4(x)
        x, x2 = self.top(x), self.top2(x)
        return x, x2

特征工程

使用的特征包括:

["target", "idx", "anglez_mean", "anglez_std", "enmo_mean", "enmo_std", "same_anglez_prev_min", "same_anglez_next_min"] + volatility_cols + ["hour", "minute"]

波动率特征(Volatility columns)是在三个不同时间窗口(5、30、480步)上对 anglez 绝对差值取中位数得到的。
Same anglez 前后特征是 anglez 与前一天同一分钟的差值。

训练细节

  • 目标设置:因为预测时有 +3 步的偏移,所以将目标及其相邻分钟也设为 1,以覆盖下一分钟的预测
  • 损失函数中忽略接近命中:将目标周围第 2 和第 3 分钟的标签设为 -1,这样不会惩罚这些位置的阳性预测
  • 双输出头:一个用于实际目标(分类交叉熵),一个用于 15 分钟窗口(二元交叉熵 BCE)
  • 在线困难样本挖掘:使用前 50% 的困难样本
  • 学习率策略:6 个 epochs,学习率递减。在 RTX3090 上每个 epoch 耗时 18 秒(总提交时间约 30 分钟)
  • 迭代策略:2 次不同种子的迭代以增加多样性。第一次迭代排除一些有问题的序列,第二次迭代的增强非常少

预测细节

  • 预测方式:使用 3 天跨度进行 1 天滑动预测,仅使用中间天的结果
  • 后处理:从最大预测值开始,将其位置作为预测。得分为该位置的概率以及在 ±4 分钟窗口内的第二高概率。将这些窗口置零,然后将周围 ±20 分钟窗口的概率乘以 0.5 并继续。之后,对高分预测在 ±18 步范围内采样极低概率的预测
  • LightGBM 堆叠:使用大量 anglez 绝对差值中位数特征的 LightGBM,并加入神经网络概率和基于这些概率的入睡/醒来时间特征。AUC 有显著提升,但比赛指标提升很小(仅 0.001)
  • 模型集成:我们有另一个 LSTM+Transformer 模型,加入后获得了 0.001 的提升
同比赛其他方案