返回列表

4th Place Solution - Nikhil's Part (Modified Unet + Transformer and Weighted Box Fusion)

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

开始: 2023-09-05 结束: 2023-12-05 健康管理与公共卫生 数据算法赛
第四名解决方案 - Nikhil部分(Modified Unet + Transformer与加权框融合)

第四名解决方案 - Nikhil部分(Modified Unet + Transformer与加权框融合)

作者:NikhilMishra
发布日期:2023年12月6日
竞赛排名:第4名

首先,非常感谢我的队友 @ryotayoshinobu 的出色表现。请阅读他的解决方案部分
我很高兴能与他组队,并从中学到了很多。

也要特别感谢主办方组织了如此有趣的竞赛,这确实是一次非常艰难的挑战。

你可以在这里找到解决方案的完整代码:

https://github.com/nikhilmishradevelop/kaggle-child-mind-institute-detect-sleep-states

解决方案概览

验证策略:按Series ID分组的GroupKFold交叉验证

模型输入:17280 × n_features长度的序列(17280 = 12步/分钟 × 60分钟 × 24小时)
模型输出:17280 × 2(一个用于入睡时刻,一个用于醒来时刻)
模型类型:回归模型
损失函数:三次方损失,即 abs(y_true-y_pred)**3

对于长度小于17280的剩余序列,通过填充使其长度统一为17280。

我的解决方案采用改进版UNET(共4个模型平均,2个LSTM和2个GRU),使用归一化高斯目标,类似于@tolgadincer描述的方法。非常感谢他早期分享的有效方法。

特征工程

  • 原始序列特征:Enmo、Anglez
  • 时间戳特征:小时、星期几
  • 衍生序列特征:Anglez差值、Enmo差值、HDCZA特征等

好的特征有助于更快收敛并获得更好的分数,因此添加有效特征非常重要(请查阅penguin的解决方案了解其他优秀特征)。

我最终4模型集成结果

CV:0.828
Public LB:0.789
Private LB:0.841

模型架构

模型架构

分块处理以减少序列长度

由于17280是一个非常长的序列长度(导致训练非常缓慢且困难),我们通过分块处理来缩短序列长度。

输入:17280 × n_features(34维)
分块大小:在不同模型中分别使用3、4、5或6

修改后序列长度:从17280降至17280//分块大小
修改后特征大小:k × 4 × 特征数量(k为Dense层输出维度)

改进版UNET部分

UNET编码器 → 瓶颈层 → Transformer → GRU或LSTM → UNET解码器

UNET编码器的每一层都与第一层进行了拼接连接

初始输出大小:17280//分块大小, 2 × 分块大小
重塑后输出大小:(17280, 2)

后处理

最初当我独自参赛时,只是简单地使用峰值检测。

我曾想用lightgbm重新排序模型预测,但未能找到好的方法,@kmat2019的帖子对此很有参考价值。

多亏了@ryotayoshinobu,我开始应用NMS(非极大值抑制),并最终试验了一种WBF(加权框融合)算法并使其生效。该WBF算法在最后一天带来了0.79到0.793的分数提升。

但在Private LB上它反而降低了0.001分,所以WBF实际上是有害的。

WBF工作流程

  1. 初始化和卷积:函数首先使用指定的卷积核对数据进行平滑处理。

  2. 峰值检测循环:迭代搜索数据中的峰值。循环持续执行,直到达到最大数量(max_count)或峰值低于阈值(max_thresh)。

  3. 自适应窗口和权重计算

    • 基于当前最大值(curr_max_power)和距离参数(k_dist)动态确定每个峰值周围的窗口大小
    • 根据section_weight_method(对数或线性)计算峰值周围数据各段的权重
  4. 加权平均和分数计算:对每个检测到的峰值计算加权平均以确定其分数。这是通过考虑峰值本身及其k个邻近值完成的。该分数受权重计算方法和其他超参数(如log_base、log_scale、weight_coeff)的影响。

  5. 抑制和更新预测

    • 每次检测到峰值后,函数会抑制邻近值以避免重复检测同一峰值。这由overlap_coeff和preds_reduction_power控制
    • 循环结束后存储并返回检测到的峰值索引和分数

我将分享WBF部分的实现代码,你可以查看调整这些超参数是否对你的模型也有帮助。该函数接受每个series id的预测。

(我通过手动和自动超参数调优相结合的方式找到了这些超参数)

def wbf_nikhil(preds_orig, max_thresh=0.1, max_count=700, hyperparams=None):
    k_dist = hyperparams['k_dist']
    log_base = hyperparams['log_base']
    log_scale = hyperparams['log_scale']
    curr_max_power = hyperparams['curr_max_power']
    weight_coeff = hyperparams['weight_coeff']
    convolution_kernel = hyperparams['convolution_kernel']
    section_weight_method = hyperparams['section_weight_method']
    preds_reduction_power = hyperparams['preds_reduction_power']
    overlap_coeff = hyperparams['overlap_coeff']
    min_distance = hyperparams['min_distance']
    
    preds = preds_orig.copy()
    preds = np.convolve(preds, convolution_kernel, mode='same')

    count = 0
    indices = []
    scores = []

    while count < max_count:
        curr_max_idx = np.argmax(preds)
        curr_max = preds[curr_max_idx]

        if curr_max < max_thresh:
            break

        k = int(k_dist - max(min_distance, (curr_max**curr_max_power)))

        start_idx = max(curr_max_idx - k, 0)
        end_idx = min(curr_max_idx + k + 1, len(preds))

        section = preds[start_idx:end_idx]

        # Different weight calculation methods
        distances = np.abs(np.arange(len(section)) - k)
        if section_weight_method == 'logarithmic':
            weights = 1 / (log_base ** (distances / (k * log_scale)))
        elif section_weight_method == 'linear':
            weights = 1 - (distances / k) * weight_coeff
        # Add more methods as needed

        weighted_avg = np.sum(section * weights) / np.sum(weights)

        scores.append(weighted_avg)
        indices.append(curr_max_idx)

        preds[start_idx:end_idx] *= ((1 - weights * overlap_coeff))**preds_reduction_power

        count += 1

    return indices, scores

最终集成

最终集成是融合我和Penguin的回归预测结果,并使用WBF进行后处理

Final_Sub = WBF(Penguins_Predictions * 0.25 + Nikhil's Predictions * 0.75)

由于我们的预测在回归目标上略有不同的尺度,Penguin的预测首先通过简单的幂变换进行缩放:Penguin's_Predictions ** 0.7

集成结果
CV:0.835
Public LB:0.793
Private LB:0.845

P.S.:这是我第一次没有使用梯度提升(gradient boosting)参加竞赛,即使我知道它效果如此之好,作为表格数据专家的我,这可能是个错误。我热爱lightgbm,下次竞赛一定会使用它。

祝贺所有表现优异的选手,这是一场精彩的较量,直到最后一刻!

同比赛其他方案