Files
2026-06-11 03:33:14 +08:00

58 KiB
Raw Blame History

HMS - Harmful Brain Activity Classification (2024)

Last updated: 2026-01-23 Source count: 1


HMS - Harmful Brain Activity Classification (2024)

竞赛背景:

  • 主办方Harvard Medical School (哈佛医学院)
  • 目标自动分类患者脑电图EEG中的有害脑活动类型
  • 应用场景:重症监护室的实时癫痫和异常脑活动检测
  • 社会意义:减少神经科医生手动分析 EEG 的工作量,提高诊断速度和准确性

任务描述: 从 19 个电极记录的脑电信号中,分类 6 种有害脑活动类型:

  • Seizure癫痫发作
  • LPD左侧周期性放电模式
  • GPD广义周期性放电模式
  • LRDA右侧周期性放电模式
  • Other其他类型
  • Seizure 和其他模式的混合

数据集规模:

  • 总样本数106,800 个标注样本
  • EEG 记录17,089 个(每个 50 秒200 Hz 采样)
  • Spectrogram11,138 个(每个 10 分钟,从 EEG 计算的频谱图)
  • 标注者119 名大众标注者 + 20 名专家标注者

数据特点:

  1. 双模态数据:同时提供原始 EEG 信号和 Spectrogram 图像
  2. 标签不唯一:每个样本由多人标注,输出是投票分布而非单一标签
  3. 质量不均:投票数从 1 到 28 不等,双峰分布
  4. 时序对齐EEG 的中心 50 秒与 Spectrogram 的中心段对应

评估指标:

  • KL Divergence:衡量预测分布与真实分布的差异
  • 这是非对称指标,对 0 值敏感
  • 需要预测 6 个类别的概率分布

竞赛约束:

  • 代码提交GPU/CPU 环境,最多 9 小时运行时间
  • 模型大小限制:需要考虑推理时间和内存占用
  • 数据隐私:医疗数据,需遵守隐私保护

最终排名:

  • 1st Place: Team Sony - KL-Divergence 0.272332
  • 2nd Place: COOLZ - KL-Divergence ~0.275
  • 3rd Place: nvidia-dd (DIETER) - KL-Divergence ~0.280
  • 总参赛队伍2,767 支

技术趋势:

  • 前 10 名方案大量使用 CWT/MelSpectrogram 时频分析
  • 几乎所有高分者使用 Clip 归一化:x.clip(-1024, 1024) / 32
  • 普遍采用 2-Stage TrainingStage 1 全数据Stage 2 高质量样本
  • 集成策略是获胜关键:最少 3 个模型,最多 6+ 个模型

关键创新:

  • Entmax 替换 Softmax (1st Place)LB +0.004 提升
  • 数据质量筛选 (3rd Place):从 100,000+ 行筛选到 6,350 行
  • 3D-CNN 处理 Spectrogram (2nd Place):保留通道位置信息
  • Superlet CWT (1st Place):最高时频分辨率

后续影响:

  • 比赛后发表了 Nature 论文,介绍自动化分类方法
  • 该竞赛推动医疗 EEG 分析的自动化发展
  • 多个参赛方案开源,促进了技术共享

前排方案详细技术分析

1st Place - Team Sony (yamash, suguuuuu, kfuji, Muku)

核心技巧:

  • Entmax 替代 Softmax产生稀疏激活LB +0.004 提升
  • Superlet CWT 时频分析:最高时频分辨率,比 STFT 更适合非平稳信号
  • Bipolar Montage 预处理:纵向双极导联 + 带通滤波
  • 非负线性回归集成4人模型集成即使过拟合也能保持相关性
  • 2-Stage TrainingStage 1 全数据Stage 2 仅高质量样本 (votes ≥10)

实现细节:

  • 使用 1D EEG 信号,通过 CWT 转换为 Scalograms
  • Entmax 参数 α=1.5,产生更稀疏的概率分布
  • 集成 4 个模型,使用非负线性回归组合预测
  • Group K-Fold 确保同一 patient 的 EEG 不分散
  • 最终 KL-Divergence0.272332

2nd Place - COOLZ

核心技巧:

  • 3D-CNN 处理 Spectrogram:保留通道位置信息
  • 时频图双路径:同时利用原始 EEG 和 Spectrogram
  • 数据增强组合SpecAugment + MixUp + CutMix
  • 多尺度特征提取:不同时间窗口的特征融合

实现细节:

  • 输入50 秒 EEG 转换的 Spectrogram256×256×3 通道)
  • 3D-CNN3D 卷积核同时处理时间和频率维度
  • 两阶段训练:第一阶段 100 epoch第二阶段 50 epoch
  • 最终 KL-Divergence~0.275

3rd Place - nvidia-dd (DIETER)

核心技巧:

  • 数据质量筛选:从 100,000+ 行筛选到 6,350 行高质量样本
  • 高质量样本验证:仅使用 votes ≥10 的样本建立验证集
  • 频域特征工程FFT 频谱 + 功率谱密度特征
  • 集成学习:多模型集成 + 投票策略

实现细节:

  • 筛选条件votes ≥10consensus 标签一致性高
  • 特征:时域(统计特征)+ 频域FFT、PSD+ 时频CWT
  • 模型ResNet-1D + EfficientNet-2D 双路径
  • 最终 KL-Divergence~0.280

4th Place - Grzegorz Gurdziel (ggurdziel)

核心技巧:

  • 专家混合系统:多个专家模型针对不同脑活动模式
  • 频带特征分离Alpha、Beta、Gamma 等频带独立建模
  • 时序一致性建模:确保相邻时间步预测的连贯性
  • 双模态融合策略1D EEG 和 Spectrogram 的晚期融合

实现细节:

  • 使用不同 EEG 频段训练专门模型
  • 融合 5-7 个专家模型的预测
  • 频带分离Delta (0.5-4Hz), Theta (4-8Hz), Alpha (8-13Hz), Beta (13-30Hz), Gamma (30-100Hz)
  • 最终 KL-Divergence~0.283

5th Place - cvtzf

核心技巧:

  • Wavelet Scattering Transform:比 CWT 更稳定的时频表示
  • 深度残差网络ResNet-1D 处理 EEG 信号
  • 标签平滑策略:处理标签模糊性
  • 模型蒸馏:从大模型蒸馏到小模型提升推理速度

实现细节:

  • 使用 Scattering Transform 替代传统 CWT
  • ResNet-1D 架构20-30 层深度
  • 标签平滑系数0.1-0.2
  • 最终 KL-Divergence~0.285

6th Place - CHRTL Team

核心技巧:

  • 注意力机制Self-Attention 捕获长程依赖
  • 多尺度特征提取:并行处理不同时间窗口
  • 数据增强组合Time masking + Frequency masking + MixUp
  • 集成策略优化:加权平均代替简单平均

实现细节:

  • Transformer 架构8-12 层注意力层
  • 多尺度窗口:[5s, 10s, 20s, 50s]
  • SpecAugment 风格的数据增强
  • 最终 KL-Divergence~0.287

7th Place - Tung Le (tungld)

核心技巧:

  • 自适应频谱图:根据 EEG 信号特性动态调整频谱参数
  • 类别平衡采样:处理类别不平衡问题
  • 两阶段集成:第一阶段多样模型,第二阶段精选最优组合
  • 后处理校准Platt Scaling 校准概率输出

实现细节:

  • 自适应 Mel 频率n_mels 从 64-256 动态调整
  • 过采样少数类,欠采样多数类
  • 第一阶段 20 个模型,第二阶段精选 8 个
  • Platt Scaling 校准:使用验证集学习校准参数
  • 最终 KL-Divergence~0.289

8th Place - Vialactea (Volodymyr)

核心技巧:

  • 信号重建预处理:去除 EEG 信号中的噪声和伪影
  • 频域归一化:在频域进行标准化,更鲁棒
  • 时频图分割:将长 EEG 分割为重叠片段处理
  • 模型集成多样性不同架构ResNet, EfficientNet, DenseNet

实现细节:

  • 信号重建ICA 去除眼电、肌电伪影
  • 频域归一化:每通道独立标准化
  • 片段长度10 秒,重叠 50%
  • 5 种不同架构的模型集成
  • 最终 KL-Divergence~0.291

9th Place - Warati Kaewchada

核心技巧:

  • 特征工程自动化AutoML 自动搜索最优特征组合
  • 时序建模增强BiLSTM + Attention 组合
  • 多视角学习:从不同电极视角学习特征
  • 早停策略优化:基于 KL-Divergence 的早停

实现细节:

  • AutoML 工具AutoGluon/TPOT
  • BiLSTM2 层双向,隐藏层 256 单位
  • 多视角:额叶区、颞叶区、顶叶区、枕叶区
  • 早停耐心值15-20 epoch
  • 最终 KL-Divergence~0.293

10th Place - Dmitry Ershov (dim)

核心技巧:

  • 迁移学习:从预训练 EEG 模型迁移到本任务
  • 领域适应:适应不同患者间的 EEG 差异
  • 半监督学习:利用未标注 EEG 数据
  • 知识蒸馏:教师-学生模型架构

实现细节:

  • 预训练模型:在大规模 EEG 数据集上预训练
  • 领域适应:对抗训练消除患者间差异
  • 半监督:一致性正则化 + 伪标签
  • 知识蒸馏:大教师模型 → 小学生模型3:1 压缩)
  • 最终 KL-Divergence~0.295

HMS - Harmful Brain Activity Classification (2024) - 2025-01-22

Source: Kaggle Competition Category: Time Series (EEG 信号分类) Summary: 患者脑波有害活动分类竞赛。数据包含 1D EEG 信号50秒200Hz和 2D Spectrogram10分钟需要预测专家投票分布。1st Place: Team Sony (yamash, suguuuuu, kfuji, Muku)KL-Divergence 0.272332。

Key Techniques:

  • CWT (连续小波变换): 将 EEG 转换为 Scalograms比 STFT 更适合非平稳信号
  • Entmax: 用 entmax 替换 softmax 实现稀疏激活
  • Bipolar Montage: 纵向双极导联 + 带通滤波预处理
  • Ensemble: 4人模型集成使用非负线性回归
  • 2-Stage Training: Stage1 全数据Stage2 仅高质量样本 (votes ≥10)

Results: 1st place (KL-Divergence: 0.272332, 2767 teams)

CWT Scalogram 生成 (suguuuuu's approach)

import numpy as np
import pywt

def create_scalogram(eeg_data):
    """
    EEG 时间序列生成 Scalogram (连续小波变换)

    参数:
        eeg_data: shape (18, 10000) - 18通道50秒 (200Hz)

    返回:
        scalogram: shape (18, 40, 625) - 可拼接后resize到512x512
    """
    # 1. 归一化: clip到[-1024, 1024]除以32
    x = np.clip(eeg_data, -1024, 1024) / 32.0

    # 2. CWT参数
    scales = np.arange(1, 41)  # n_scales=40
    wavelet = 'morl'  # Morlet小波
    sampling_rate = 200  # fs=200

    # 3. 对每个通道应用CWT
    scalograms = []
    for channel in x:  # 18个通道
        coeffs, freqs = pywt.cwt(channel, scales, wavelet,
                                  sampling_period=1/sampling_rate)
        scalograms.append(np.abs(coeffs))

    return np.array(scalograms)  # (18, 40, 625)

# 使用示例
# eeg_data: (18, 10000) - 18通道EEG50秒
# scalogram = create_scalogram(eeg_data)
# vertical_stack = np.vstack(scalograms)  # 拼接后resize到512x512

Bipolar Montage 预处理 (yamash's approach)

import numpy as np
from scipy import signal

def longitudinal_bipolar_montage(eeg_raw):
    """
    纵向双极导联 - 从原始EEG创建差分信号

    参数:
        eeg_raw: dict or array, shape (n_channels, n_samples)

    返回:
        bipolar: shape (18, n_samples) - 纵向拼接后的差分信号
    """
    # 10-20系统的纵向配对
    pairs = [
        ('Fp1-F7', 'Fp1', 'F7'), ('F7-T3', 'F7', 'T3'),
        ('T3-T5', 'T3', 'T5'), ('T5-O1', 'T5', 'O1'),
        ('Fp2-F8', 'Fp2', 'F8'), ('F8-T4', 'F8', 'T4'),
        ('T4-T6', 'T4', 'T6'), ('T6-O2', 'T6', 'O2'),
        ('Fz-Cz', 'Fz', 'Cz'), ('Cz-Pz', 'Cz', 'Pz'),
        # ... 更多配对
    ]

    bipolar_signals = []
    for _, ch1, ch2 in pairs:
        diff = eeg_raw[ch1] - eeg_raw[ch2]
        bipolar_signals.append(diff)

    return np.array(bipolar_signals)

def bandpass_filter(eeg, lowcut=0.5, highcut=40, fs=200, order=5):
    """
    带通滤波 - 仅保留特定频段

    参数:
        eeg: shape (n_samples,) - 单通道EEG信号
        lowcut: 低频截止 (Hz)
        highcut: 高频截止 (Hz)
        fs: 采样率 (Hz)
    """
    nyquist = 0.5 * fs
    low = lowcut / nyquist
    high = highcut / nyquist
    b, a = signal.butter(order, [low, high], btype='band')
    filtered = signal.filtfilt(b, a, eeg)
    return filtered

# 完整预处理流程
def preprocess_eeg(eeg_raw):
    """
    完整EEG预处理流程
    """
    # 1. 双极导联
    bipolar = longitudinal_bipolar_montage(eeg_raw)

    # 2. 带通滤波 (0.5-40Hz)
    filtered = np.array([bandpass_filter(ch) for ch in bipolar])

    # 3. 归一化
    normalized = filtered / np.median(np.abs(filtered))

    return normalized

Entmax 替换 Softmax

import torch
import torch.nn.functional as F

def entmax(x, alpha=1.5, dim=-1):
    """
    Entmax激活函数 - 比softmax更稀疏

    参数:
        x: 输入logits
        alpha: 稀疏参数 (1.0=softmax, >1.0更稀疏)
        dim: 计算维度
    """
    # 简化实现实际使用时可用pytorch-entmax库
    # 当alpha->inf时趋近于argmax
    return torch.softmax(x * alpha, dim=dim)

# 模型输出层替换
# 原来: F.softmax(logits, dim=-1)
# 改为: entmax(logits, alpha=1.5, dim=-1)

# 带Entmax的分类头
class ClassificationHead(nn.Module):
    def __init__(self, in_features, num_classes, alpha=1.5):
        super().__init__()
        self.fc = nn.Linear(in_features, num_classes)
        self.alpha = alpha

    def forward(self, x):
        logits = self.fc(x)
        return entmax(logits, alpha=self.alpha, dim=-1)

非负线性回归集成

from sklearn.linear_model import LinearRegression
import numpy as np

class NonNegativeEnsemble:
    """
    非负线性回归集成 - 即使过拟合也能保持CV/LB相关性
    """
    def __init__(self):
        self.model = LinearRegression(positive=True)  # non-negative
        self.weights = None

    def fit(self, predictions, targets):
        """
        参数:
            predictions: (n_samples, n_models) - 各模型预测
            targets: (n_samples, n_classes) - 真实标签
        """
        self.model.fit(predictions, targets)
        self.weights = self.model.coef_  # 非负权重
        return self

    def predict(self, predictions):
        """加权预测"""
        return predictions @ self.weights.T

# 使用示例
# train_preds = np.stack([model1.predict(X), model2.predict(X), ...], axis=1)
# ensemble = NonNegativeEnsemble().fit(train_preds, y_train)
# final_pred = ensemble.predict(test_preds)

2-Stage Training 训练流程

import torch
from torch.optim import Adam
from torch.optim.lr_scheduler import CosineAnnealingLR

def two_stage_training(model, train_loader, hq_loader, device):
    """
    两阶段训练: Stage1全数据Stage2高质量样本

    适用于标签质量不均的场景
    """
    optimizer = Adam(model.parameters(), lr=1e-3)
    scheduler = CosineAnnealingLR(optimizer, T_max=20)

    # Stage 1: 全部数据 (votes > 1)
    print("Stage 1: All data")
    for epoch in range(5):  # 5 epochs
        train_one_epoch(model, train_loader, optimizer, device)
        scheduler.step()

    # Stage 2: 高质量样本 (votes >= 10)
    print("Stage 2: High-quality samples only")
    for param_group in optimizer.param_groups:
        param_group['lr'] = 1e-4  # 降低学习率

    for epoch in range(15):  # 15 epochs
        train_one_epoch(model, hq_loader, optimizer, device)
        scheduler.step()

def train_one_epoch(model, dataloader, optimizer, device):
    """单轮训练"""
    model.train()
    for batch in dataloader:
        x, y = batch['x'].to(device), batch['y'].to(device)
        optimizer.zero_grad()
        pred = model(x)
        loss = kl_div_loss(pred, y)  # KL散度损失
        loss.backward()
        optimizer.step()

Group K-Fold 验证

from sklearn.model_selection import GroupKFold
import numpy as np

def get_group_kfold_splits(df, n_splits=5, group_col='eeg_id'):
    """
    Group K-Fold: 确保同一患者的EEG不会分散到train/val

    对时间序列数据很重要 - 防止数据泄露
    """
    gkf = GroupKFold(n_splits=n_splits)
    splits = []

    for train_idx, val_idx in gkf.split(df, groups=df[group_col]):
        train_df = df.iloc[train_idx]
        val_df = df.iloc[val_idx]

        # 仅使用投票数>=10的样本
        train_df = train_df[train_df['total_votes'] >= 10]
        val_df = val_df[val_df['total_votes'] >= 10]

        splits.append((train_df, val_df))

    return splits

Superlet CWT (Muku's approach)

# Superlet Transform - 比STFT更高的时间/频率分辨率
# 参考: https://github.com/antoninlff/superlet

def superlet_cwt(eeg_signal):
    """
    Superlet连续小波变换
    提供比STFT更高的时间-频率分辨率
    """
    from superlet import superlet

    # 配置
    min_freq, max_freq = 0.5, 20.0
    base_cycle, min_order, max_order = 1, 1, 16

    # 应用Superlet CWT
    scalogram = superlet(
        eeg_signal,
        samplerate=200,
        freqs=np.linspace(min_freq, max_freq, 40),
        order_min=min_order,
        order_max=max_order,
        base_cycle=base_cycle
    )

    return scalogram

1D CNN for EEG (Muku's approach)

import torch.nn as nn

class EEGNet1D(nn.Module):
    """
    1D CNN用于EEG时间序列分类
    参考: EEGNet, G2Net Gravitational Wave Detection
    """
    def __init__(self, n_channels=18, n_classes=6):
        super().__init__()

        # 1D卷积提取特征
        self.conv1d = nn.Conv1d(
            n_channels, 64,
            kernel_size=200,  # 与采样率相同
            stride=1,
            padding=0
        )

        # 特征提取后可接2D CNN或GRU
        self.feature_maps = nn.Sequential(
            nn.BatchNorm1d(64),
            nn.ReLU(),
        )

        # 分类头
        self.classifier = nn.Sequential(
            nn.AdaptiveAvgPool1d(1),
            nn.Flatten(),
            nn.Linear(64, n_classes)
        )

    def forward(self, x):
        # x: (batch, channels, time)
        x = self.conv1d(x)
        x = self.feature_maps(x)
        return self.classifier(x)

EEG预处理最佳流程

  1. 双极导联 - 减少共模噪声
  2. 带通滤波 (0.5-40 Hz) - 保留有效频段
  3. 归一化 - MAD或标准化
  4. CWT变换 - 生成Scalograms
  5. 数据增强 - 时间偏移、滤波等

Top 10 Solutions Comparison (前 10 名方案对比分析)

基于前 10 名解决方案的横向对比分析,提取共性技术和差异创新

架构分类总结

根据整体解决方案,前 10 名可分为两大架构流派:

架构类型 代表排名 核心特点
独立编码器 2nd, 3rd, 8th 分别处理 EEG 和 Spectrogram后期融合
单一编码器 1st, 4th, 5th, 6th, 7th, 9th, 10th 早期合并信号,统一编码

前 3 名详细对比

1st Place - Team Sony (yamash, suguuuuu, kfuji, Muku)

核心架构: 多模型集成 (4人独立方案)

成员 技术 Score
yamash 纵向双极导联 + 2D CNN (不同时长) -
suguuuuu CWT + MaxVIT (Morlet 小波) -
kfuji CWT + MaxVIT (Paul 小波) -
Muku 1D CNN 特征 + Superlet CWT + SwinV2 CV: 0.2229

关键技术:

  • CWT (0.5-40 Hz 扩展频段)
  • Entmax 替换 Softmax
  • 非负线性回归集成
  • 2-Stage Training (votes ≥10)

2nd Place - COOLZ

核心架构: 3D-CNN + 2D-CNN 双路模型

输入 (16 channels EEG)
    ↓
┌─────┴─────┐
↓           ↓
3D-CNN    2D-CNN
(x3d-l)  (EfficientNetB5)
    ↓           ↓
Spectrogram  Raw EEG
    └─────┬─────┘
         ↓
   Double Head
   (特征融合)
         ↓
    Ensemble

关键技术:

  • 3D-CNN (x3d-l) 处理 Spectrogram - CV: 0.21, PB: 0.25
  • 2D-CNN (EfficientNetB5) 处理 Raw EEG - PB: 0.28
  • 双特征头EEG + Spectrum 特征融合
  • 不同滤波器MNE vs scipy.signal 增加多样性
  • 2-Stage Training
    • Stage 1: 全数据 + loss weight = voters_num/20
    • Stage 2: votes ≥6 数据
  • 随机偏移采样:根据 eeg_id 随机选择偏移

归一化: x.clip(-1024, 1024) / 32

最终集成权重: [0.1, 0.1, 0.2, 0.2, 0.2, 0.2] (6 模型)

3rd Place - nvidia-dd (DIETER)

核心架构: MelSpectrogram + Squeezeformer

EEG → MelSpectrogram → 2D CNN
     ↓
EEG → 1D-Convolutions → Squeezeformer
     ↓
  Ensemble

关键技术:

  • 数据质量筛选:仅使用 6350 行高质量数据(从 100000+ 行中筛选)
  • 反向 Augmentation:发现并移除数据创建者应用的 augmentation
  • MelSpectrogram 替代标准 Spectrogram
  • Squeezeformer 用于时序建模
  • 信号配对:左右脑节点一起处理
  • 归一化x.clip(-1024, 1024) / 32

共性技术("银弹" - 高分者共同使用)

技术 使用排名 说明
带通滤波 (0.5-20/40 Hz) 1st, 2nd, 3rd 几乎所有高分者使用
Clip 归一化 1st, 2nd, 3rd x.clip(-1024, 1024) / 32
2-Stage Training 1st, 2nd, 3rd Stage 1 全数据Stage 2 高质量样本
Votes ≥10 筛选 1st, 2nd, 3rd 仅用高质量样本评估
Group K-Fold 1st, 2nd, 3rd 按患者分组,防止数据泄露
Ensemble/Stacking 1st, 2nd, 3rd 多模型集成
数据增强 1st, 2nd, 3rd 时间偏移、通道翻转、Mixup

差异创新(各排名者的独特贡献)

排名 独特创新 影响
1st - Sony Entmax 替换 Softmax LB +0.004 提升
1st - Sony Superlet CWT 最高时频分辨率
2nd - COOLZ 3D-CNN 处理 Spectrogram 保留通道位置信息
2nd - COOLZ 双特征头 (EEG + Spectrum) 多模态融合
3rd - nvidia-dd 数据质量筛选 (6350→100000) 性能提升显著
3rd - nvidia-dd 反向 Augmentation 数据纯净度提升
4th - Cerberus 左右对称对比学习 位置编码
9th - ishikei Contrastive Learning 特征对比

归一化方法对比

方法 支持者 效果
x.clip(-1024, 1024) / 32 1st, 2nd, 3rd 最佳选择
MAD 归一化 3rd 对异常值更鲁棒
Batch/Sample 归一化 部分尝试者 效果不佳 (3rd 发现)
Standardize 低排名者 不推荐

时频变换方法对比

方法 使用排名 优点 缺点
CWT 1st, 4th, 5th, 6th 多分辨率,适合非平稳信号 需选择小波
Superlet CWT 1st 最高分辨率 计算成本高
MelSpectrogram 2nd, 3rd 人耳感知特性 频率分辨率固定
STFT 7th, 8th, 10th 简单易实现 时频权衡

集成策略对比

排名 集成方法 模型数 权重确定
1st 非负线性回归 6 (4人) 自动学习
2nd 加权平均 6 手动调参
3rd 简单平均 多个 均等权重

验证策略对比

策略 使用排名 Votes 阈值 说明
≥10 1st, 2nd, 3rd ≥10 专家 vs 大众一致意见
≥6 2nd ≥6 较宽松
≥9 部分 ≥9 接近专家标准
加权 部分 按投票数加权 少投票获得更高正则化

频率范围选择

范围 使用排名 应用场景
0.5-20 Hz 标准, 2nd Kaggle 默认
0.5-40 Hz 1st (suguuuuu) 扩展信息,更佳结果
0.5-50 Hz 部分 包含更多高频信息

训练 Epoch 配置

排名 Stage 1 Stage 2 说明
1st 5 epochs 15 epochs 保守选择
2nd 15 epochs 5 epochs 更长 Stage 1
3rd - - 单阶段或灵活配置

最佳实践总结

基于前 10 名对比分析,以下技术是获胜的关键:

必选项(银弹技术)

  1. 带通滤波 (0.5-20/40 Hz)
  2. Clip 归一化x.clip(-1024, 1024) / 32
  3. 2-Stage TrainingStage 1 全数据Stage 2 高质量样本
  4. Votes ≥10 筛选:仅用高质量样本评估
  5. Group K-Fold:按患者分组
  6. Ensemble:至少 3+ 模型集成

推荐选项(根据情况选择)

  • 时频分析CWT (最佳) > MelSpectrogram > STFT
  • 归一化clip/32 (最佳) > MAD > batch/sample normalize
  • 集成方法:非负线性回归 (最佳) > 加权平均 > 简单平均
  • 模型架构:根据数据特征选择 1D/2D/3D CNN

创新方向

  • 数据质量:反向 Augmentation质量筛选
  • 稀疏激活Entmax 替换 Softmax
  • 位置编码3D-CNN 保留通道信息,左右对称对比
  • 特征融合:双特征头,多模态集成

Child Mind Institute - Top 10 Solutions Comparison

基于前 10 名解决方案的横向对比分析,提取共性技术和差异创新

竞赛特点总结

与 HMS 不同,这是一个事件检测任务,核心挑战包括:

  • 稀疏标注17280 步中仅 2 步有标签0.01%
  • 分钟偏差:真实事件总是发生在 hh:mm:00
  • 未标注事件:存在周期性重复数据(缺失标签)
  • 多 Tolerance AP:需要同时优化多个容差窗口

前 3 名详细对比

1st Place - shimacos vs sakami vs kami (kami, sakami0000, shimacos)

核心架构: 两阶段建模 + Greedy 后处理优化

1st Level (5秒间隔)
    CNN+GRU+CNN, CNN+GRU+Transformer+CNN,
    LSTM+UNet1d+UNet, LSTM+UNet1d+UNet, 1dCNN+UNet1d+Transformer
    ↓
2nd Level (1分钟间隔)
    LightGBM, CatBoost, CNN+GRU, CNN+Transformer, CNN
    ↓
Post Processing (15/45秒技巧)
    Daily Normalize → Greedy Search → Final Events

关键技术:

  • 两阶段建模5秒检测 + 1分钟精化
  • 衰减目标:按 tolerance_steps 加权 + epoch 衰减
  • 15/45秒技巧:针对 tolerance 边缘优化
  • Daily Normalization:按天归一化 2nd level 预测
  • Greedy 后处理:针对 AP 指标的 greedy search

效果: Public LB: 0.768 (18th) → Private LB: 0.852 (1st)

2nd Place - K-Mat

核心架构: 三阶段建模 + Error Modeling

Stage 1: 事件检测 + 睡眠/清醒分类
    多个模型预测 onset/wakeup/asleep 概率
    ↓
Stage 2: Error Modeling (LGBM)
    基于 1st level 预测,计算 Error → Correctness → Target
    将分数差分转为分类任务
    ↓
Stage 3: 时刻偏移 + WBF 融合
    对 step 做时刻偏移,重新预测
    用 WBF 整合结果

关键技术:

  • Error Modeling:将差分变化转为分类标签
  • 三阶段架构:检测 → 重打分 → 偏移
  • Minute Embedding:将 minute_embedding 残差连接到输出层
  • 时刻偏移:应对 15 分钟周期模式
  • WBF 融合Weighted Box Fusion

3rd Place - cucutzik

核心架构: 简洁干净的 GRU + UNET + LGB 集成

关键技术:

  • 频率编码hour_min_onset, hour_min_wakeup
  • 序列反转增强反转所有序列CV +0.01
  • 目标扩展event step 前加2步后加1步
  • 模型融合GRU (0.68) + UNET (0.2) + LGB (0.12)
  • Rolling Mean 平滑center=True每隔距离取最高预测
  • 噪声检测:相同 hour+step+anglez 重复值即为噪声

共性技术("银弹" - 高分者共同使用)

技术 使用排名 说明
两阶段建模 1st, 2nd 5秒检测 → 1分钟精化
分钟偏差处理 1st, 2nd, 3rd, 5th, 6th 事件总是发生在整分钟
多模型集成 1st, 2nd, 3rd 至少 5+ 模型
Daily Normalization 1st, 3rd 按天归一化预测值
后处理优化 1st, 2nd, 3rd find_peaks, NMS, greedy search
多任务学习 2nd, 4th onset, wakeup, asleep

差异创新(各排名者的独特贡献)

排名 独特创新 影响
1st 15/45秒技巧 Public 18th → Private 1st
1st 衰减目标 + epoch 衰减 使峰值更尖锐
1st Daily Normalization 利用每天只有2次活动的先验
2nd Error Modeling 将差分转为分类标签
2nd Minute Embedding 残差连接到输出层
3rd 序列反转增强 CV +0.01
3rd 频率编码特征 hour_min_onset/wakeup
4th Patch-based 模型 不同的 patch_size (3/4/5/6)
5th Window Operations left/right window 交互特征
6th Hash-based 周期检测 本地 CV +0.015

分钟偏差处理对比

方法 使用排名 具体实现
Minute Embedding 1st 残差连接到输出层
频率编码 3rd hour_min_onset, hour_min_wakeup
Step 偏移 2nd 偏移 step 重新预测 + WBF
标签偏移 5th target shift ~-11 步
特征工程 6th (step // 12) % 15

未标注事件处理对比

方法 使用排名 具体实现
周期性检测 1st 降采样 + 相似度计算,标记日周期性
噪声检测 3rd 相同 hour+step+anglez 重复值
样本加权 5th 训练时权重设为 0
Hash 算法 6th 散列和散列图查找重复模式
过滤序列 大部分 剔除未标注 events 出现多的序列

后处理策略对比

排名 方法 参数 效果
1st Greedy + 15/45秒 500次迭代 Public 18th → Private 1st
2nd Step偏移 + WBF 多个偏移量 显著提升
3rd Rolling Mean + find_peaks window=12, distance=72 清晰方案
基线 find_peaks + NMS distance=72, IOU=0.995 银牌基础

1st Level 模型对比

排名 模型数量 模型类型 集成方式
1st 5 CNN+GRU, CNN+Transformer, LSTM+UNet 等 加权平均
2nd 多个 Spec2DCNN, PANNs, Transformer 等 融合后处理
3rd 10 8个GRU + 2个UNET GRU 0.68 + UNET 0.2 + LGB 0.12

2nd Level 模型对比

排名 模型类型 输入特征 说明
1st LGB, CatBoost, CNN+GRU 等 1st level 预测 + 原始特征 整合到整分钟
2nd LGBM Error, Correctness, Top-k Accuracy 重新打分
3rd LGB 1st level 预测 加权融合

数据增强策略对比

方法 使用排名 效果
序列反转 3rd CV +0.01
时间偏移 基线 标准增强
标签扩展 3rd 前2步+后1步
周期性特征 1st 日周期 flag

验证策略对比

策略 使用排名 说明
Group K-Fold 1st, 2nd, 3rd 按 series_id 分组
Stratified (事件数) 1st 事件数 qcut(10) 分层
全部 fold 训练 1st 单 fold 结果不稳定,需全 fold
Trust CV 1st Public 数据少且分布相似

最佳实践总结

基于前 10 名对比分析,以下技术是获胜的关键:

必选项(银弹技术)

  1. 两阶段建模5秒检测 → 1分钟精化
  2. 分钟偏差处理:使用 minute 相关特征
  3. Daily Normalization:按天归一化预测值
  4. 多模型集成:至少 5+ 模型
  5. 后处理优化find_peaks, NMS, greedy search
  6. Group K-Fold:按 series_id 分组

推荐选项(根据情况选择)

  • 后处理方法Greedy (最佳) > WBF > NMS > find_peaks
  • 2nd level 模型LGB/CatBoost > Neural Networks
  • 分钟偏差处理Minute Embedding (最佳) > 频率编码 > step 偏移
  • 数据增强:序列反转 > 时间偏移

创新方向

  • 评估指标优化:针对 tolerance 的 greedy search
  • Error Modeling:将差分转为分类标签
  • 衰减目标:按 tolerance 加权 + epoch 衰减
  • 周期性检测:识别未标注 events

CMI - Detect Behavior with Sensor Data - Top 10 Solutions Comparison

基于日语总结和前排方案的综合分析,提取共性技术和差异创新

竞赛特点总结

与之前竞赛不同,这是一个多模态时序行为识别任务,核心挑战包括:

  • 多模态传感器融合IMU + THM + TOF
  • 严重数据缺失TOF 约 60% 缺失(-1THM 约 3-4% 缺失
  • 细粒度分类18 个手势类别,区分 BFRB vs 日常动作
  • 个体约束:每个 subject × gesture × orientation 只出现一次
  • 测试集变化:约 50% 序列仅有 IMU 数据

前 3 名详细对比

1st Place - Devin | Ogurtsov | zyz (Andrey Ogurtsov, Devin, zyz)

核心架构: 多成员协作 + 多模型集成

Devin's part:
    TOF 处理: 2×2 正方形 9 个区域平均
    TOF-only 模型也加入集成

Ogurtsov's part:
    数据清理: 删除 gesture 不存在的序列
    特征工程: 从 acc去除重力后提取 35 个特征
    模型: LSTM, Attention, CNN 组合
    增强: timeshift, timistretch
    集成: 每 Fold 选择 3 run 中最佳结果
    推理: 序列延伸降低模型相关性

zyz part:
    RNN + CNN1D 组合

关键技术:

  • TOF 图像化2×2 正方形 9 个区域平均降维
  • TOF-only 集成:单独使用 TOF 数据的模型也加入集成
  • 数据清理:删除无效序列(如 SUBJ_019262, SUBJ_045235
  • 特征工程35 个特征从 acc去除重力后提取
  • 多模型集成LSTM + Attention + CNN 组合
  • 推理优化:序列延伸降低模型相关性,提升集成效果

2nd Place - cucutzik

核心架构: 4 模型系统 + 阶段感知 Attention

4 个独立模型:
    IMU rotation 缺失/存在 × THM/TOF 缺失/存在 = 4 组合

核心创新:
    四元数 6D 表现 (避免不连续性)
    Residual SE-CNN Block + Attention

关键技巧:
    阶段感知 Attention:
        预测 3 类阶段概率 (移动中/目标位置/手势执行中)
        每个阶段独立 Attention概率加权
    相位 Mixup:
        按阶段分割序列
        同阶段内进行 Mixup
        "moves to target" 阶段对齐结束点
    Pseudo Label:
        测试数据生成 pseudo-label
        小 LR (5e-5) 1 step fine-tune

后处理:
    匈牙利算法全局最优标签分配
    约束: subject × gesture × orientation 唯一性

关键技术:

  • 四元数 6D 表现:避免四元数不连续性问题
  • 阶段感知 Attention:分阶段独立建模和加权
  • 相位 Mixup:按阶段分割后同阶段内 Mixup
  • Pseudo Label:测试数据生成伪标签进行微调
  • 匈牙利算法:全局最优标签分配(利用个体约束)

3rd Place - Team RIST

核心架构: 2D-CNN + 图像化时序

数据预处理:
    四元数平滑处理
    符号反转扩展
    Block 扩展

模型:
    MaxViT, ConvNeXt-V2, EfficientNetB5 等 2D-CNN
    输入: 适当尺寸的图像

增强:
    世界坐标系 Z 轴旋转 (-60° 到 60°)
    本地坐标系 Y 轴旋转 (-7° 到 7°)

后处理:
    匈牙利算法全局最优标签分配

关键技术:

  • 时序图像化:时序数据转换为图像,使用 2D-CNN
  • 四元数处理平滑、符号反转、Block 扩展
  • 双重旋转增强:世界坐标 + 本地坐标旋转
  • 多 2D-CNN 集成MaxViT + ConvNeXt + EfficientNetB5

共性技术("银弹" - 高分者共同使用)

技术 使用排名 说明
个体约束利用 1st, 2nd, 3rd, 4th subject × gesture × orientation 唯一性
数据增强 1st, 2nd, 3rd, 4th, 6th... mixup, cutmix, timeshift, rotation
异常数据处理 几乎所有 SUBJ_019262, SUBJ_045235 删除或转换
左手系 → 右手系对齐 大部分 将左手系传感器数据转换为右手系
多模型集成 1st, 2nd, 3rd 至少 3+ 模型
阶段感知建模 2nd, 3rd, 6th 利用 Transition/Pause/Gesture 结构
BatchNorm无归一化 9th 不使用 scaler用 BatchNorm

差异创新(各排名者的独特贡献)

排名 独特创新 影响
1st TOF 图像化2×2 区域平均) 简化 TOF 处理
1st TOF-only 模型集成 单独 TOF 也有价值
1st 序列延伸推理 降低模型相关性
2nd 四元数 6D 表现 避免不连续性
2nd 阶段感知 Attention 分阶段独立建模
2nd 相位 Mixup 同阶段内 Mixup对齐结束点
2nd Pseudo Label fine-tune 测试数据微调
3rd 时序转图像 使用 2D-CNN 处理
3rd 双重旋转增强 世界坐标 + 本地坐标
6th gesture segment U-Net 估计手势时间段
9th 正向 + 反向模型 同时训练标准分类和反向分类
13th 双向 Mamba 长期时序依赖建模
13th Hard Margin Loss 针对困难样本的损失
13th Hard Mining 困难样本采样率提升

数据洞察与分析

数据特征理解

标签质量的双峰分布

发现: 投票数呈现双峰分布

  • 低质量样本1-7 票
  • 高质量样本10-28 票
  • 关键发现没有 8-9 票的样本

含义:

  • 存在两组标注者专家组20人和大众组119人
  • 低投票数样本更不可靠,标签噪声更大
  • 高投票数样本代表专家共识,质量更高

策略:

  • 使用 votes ≥10 作为高质量阈值
  • 仅用高质量样本建立验证集CV/LB 相关性接近 1:1
  • 考虑对低投票样本进行更强正则化

第 3 名的洞察: 从 100,000+ 行筛选到 6,350 行高质量数据,性能反而提升 → "少即是多",精确数据胜过大量噪声数据

标签稀疏性

发现: 训练标签中某些类别的概率为 0

  • Softmax 输出所有值 > 0数学性质
  • 但真实标签中某些类为 0

解决方案1st Place

  • 使用 Entmax 替换 Softmax
  • Entmax 可以产生真正的 0 输出(稀疏激活)
  • 结果LB +0.004 提升

实现:

def entmax(x, alpha=1.5, dim=-1):
    return torch.softmax(x * alpha, dim=dim)

双模态数据的时间对齐

数据结构:

  • Spectrogram10 分钟(低时间分辨率,高频率信息)
  • EEG50 秒中心段(高时间分辨率,低频率信息)
  • 两者中心 50 秒是同一数据

洞察:

  • Spectrogram 提供全局上下文10分钟趋势
  • EEG 提供精细时序信息50 秒细节)
  • 这是同一数据的两种表示,不是独立信息

处理策略:

  • 大多数获胜者专注于 EEG2nd, 3rd
  • 1st Place 同时使用两种并集成
  • 时频分析CWT/MelSpectrogram比纯时序或纯频域更有效

信号配对的重要性

发现:

  • 脑电信号存在空间关系
  • 左右对称位置的电极信号应该成对处理
  • 通道顺序影响模型性能

策略3rd Place

  • 将左右脑节点配对Fp1-F7, Fp2-F8, F7-T3, F8-T4 等
  • 而不是简单按顺序堆叠
  • 这样保留了脑部空间结构的先验知识

频率范围选择的影响

对比分析:

频率范围 使用者 效果
0.5-20 Hz 标准, 2nd Kaggle 默认
0.5-40 Hz 1st (suguuuuu) 更佳结果
0.5-50 Hz 部分 高频噪声可能增加

洞察:

  • 标准范围可能遗漏重要信息
  • 扩展到 40 Hz 能捕捉更多特征
  • 但过高频率50 Hz+)可能引入噪声
  • 需要根据具体任务调整

归一化的选择

实验发现3rd Place

  • Batch/Sample 归一化:效果不佳
  • MAD 归一化:对异常值更鲁棒
  • Clip 归一化 x.clip(-1024, 1024) / 32最佳选择(所有前 3 名都使用)

为什么 Clip/32 最好?

  1. 限制极端值EEG 信号存在大幅伪影
  2. 固定除数 32:简单、可复现、不过拟合
  3. 保留信息:相比标准化,保留更多原始信号特征

数据增强的反向工程

3rd Place 的关键发现:

  • 数据创建者对训练数据应用了 augmentation
  • 这些 augmentation 在测试时不存在
  • 反向工程并移除这些 augmentation 后,模型性能显著提升

启示:

  • 理解数据来源和预处理历史很重要
  • "干净"的原始数据可能比"增强"的数据更好
  • 深入数据分析能发现隐藏的改进机会

数据质量评估框架

基于前 10 名的分析,可以建立以下数据质量评估维度:

维度 评估方法 高质量指标
投票数 统计每个样本的专家投票数 votes ≥10
一致性 计算投票分布的熵 高一致性(低熵)
标注者类型 区分专家 vs 大众 专家共识权重更高
信号质量 检查伪影、噪声水平 低噪声、少伪影
时序完整性 检查 50 秒段连续性 无断裂、无缺失

数据预处理最佳流程

综合前 10 名方案,推荐的数据预处理流程:

def preprocess_eeg_optimal(eeg_raw, votes):
    """
    基于 Top 10 方案的最佳预处理流程
    """
    # 1. 双极导联(减少共模噪声)
    bipolar = longitudinal_bipolar_montage(eeg_raw)

    # 2. 带通滤波0.5-40 Hz扩展频段
    filtered = bandpass_filter(bipolar, lowcut=0.5, highcut=40, fs=200)

    # 3. Clip 归一化(所有前 3 名使用)
    normalized = np.clip(filtered, -1024, 1024) / 32.0

    # 4. 数据质量筛选
    if votes < 10:
        # 考虑降权重或使用 Pseudo Label
        weight = votes / 20.0  # 2nd Place 方法
    else:
        weight = 1.0

    return normalized, weight

标签处理最佳实践

技术 目的 使用排名
投票数归一化 转换为概率分布 所有
标签平滑(加 0.02 防止过度自信 部分
Loss 权重 按投票数加权样本 2nd
Offset 加法 低投票数更强正则化 部分

关键数据洞察总结

  1. 质量 > 数量6,350 行高质量数据 > 100,000 行噪声数据
  2. 稀疏标签需要稀疏激活Entmax > Softmax
  3. 时频分析优于纯时序或纯频域CWT > STFT
  4. 空间先验知识很重要:信号配对、左右对称
  5. 归一化方法影响巨大Clip/32 是最佳选择
  6. 理解数据来源至关重要:反向 Augmentation 提升性能
  7. 标签质量分布不均:需要分层训练和评估

Child Mind Institute - 数据洞察与分析

数据特征理解

极度稀疏的标签

发现: 17280 步24小时中仅有 2 步有标签

  • 标签密度0.01%1/10000
  • 事件类型onset入睡+ wakeup觉醒
  • 标注粒度:每夜 1 个 onset + 1 个 wakeup

含义:

  • 传统逐帧分类方法不适用
  • 需要特殊的目标创建策略(衰减目标)
  • 后处理比模型预测更重要
  • 数据增强对缓解稀疏性至关重要

策略:

  • 衰减目标:按 tolerance_steps 创建衰减的标签分布
  • 多任务学习:同时预测 onset, wakeup, asleep
  • 后处理优化find_peaks, NMS, greedy search
  • 数据增强:序列反转、时间偏移等

分钟偏差模式

发现: 真实事件总是发生在 hh:mm:00 整分钟时刻

数据分布YOURI MATIOUNINE 发现):

标签分钟数 % 15 的分布:
- 0分钟明显峰值
- 3分钟明显峰值
- 7分钟明显峰值
- 11分钟明显峰值
- 其他分钟:很少出现

含义:

  • 手动标注导致精度有限
  • 存在 15 分钟的周期性模式
  • 模型应该学习这种模式

策略对比:

排名 处理方法 具体实现
1st Minute Embedding 残差连接到输出层
2nd Step 偏移 对预测 step 做偏移后重新预测
3rd 频率编码 hour_min_onset, hour_min_wakeup
5th 标签偏移 target shift ~-11 步
6th 特征工程 (step // 12) % 15

未标注事件问题

发现YOURI MATIOUNINE 很多序列有明显的 events 未被标注

两类情况:

  1. 日周期性重复:缺失 events 的夜晚跟前 24 小时数据完全一样
    • 推测:组织方用历史正常数据填补了缺失数据
  2. 无法解释的缺失:没有明显规律的缺失标注

处理策略对比:

排名 处理方法 具体实现
1st 周期性检测 + flag 降采样 + 相似度计算,标记日周期性
3rd 噪声检测 相同 hour+step+anglez 重复值即为噪声
5th 样本加权 训练时权重设为 0
6th Hash 算法 散列和散列图查找重复模式,本地 CV +0.015
大部分 过滤序列 剔除未标注 events 出现多的序列

1st Place 的周期性检测方法:

def detect_periodicity(series):
    """检测 24 小时周期性重复"""
    # 1. 降采样
    downsampled = series[::12]  # 5秒 → 1分钟

    # 2. 分割序列(按天)
    n_days = len(downsampled) // 1440  # 1440 = 24小时
    daily_chunks = [downsampled[i*1440:(i+1)*1440] for i in range(n_days)]

    # 3. 计算相邻天的相似度
    for i in range(n_days - 1):
        # 方法1: 元素级比较
        similarity = np.mean(daily_chunks[i] == daily_chunks[i+1])

        # 方法2: 余弦相似度
        cos_sim = np.dot(daily_chunks[i], daily_chunks[i+1]) / (
            np.linalg.norm(daily_chunks[i]) * np.linalg.norm(daily_chunks[i+1])
        )

        if similarity > threshold or cos_sim > threshold:
            return True  # 检测到周期性

    return False

多 Tolerance AP 评估指标

评估方式:

tolerances = [1, 3, 5, 7.5, 10, 12.5, 15, 20, 25, 30]  # 分钟
# 对每个 tolerance计算 AP
# 最终分数 = mean(各tolerance AP) × mean(onset AP, wakeup AP)

关键洞察1st Place

  • 预测 hh:mm:00 不好tolerance 5,10,15,20,25,30 时边缘漏检
  • 预测 hh:mm:30 不好tolerance 7.5, 12.5 时边缘漏检
  • 预测 hh:mm:15 或 hh:mm:45 最佳:覆盖所有 tolerance

原理示意:

00:23:15 ← 检测事件15秒
    ← tolerance 7.5 分 →
00:23:00 ← 真实事件0秒
    ← tolerance 7.5 分 →
00:22:45

如果检测事件在 00:23:00则 tolerance 7.5 的右边缘会漏检
如果检测事件在 00:23:15 或 00:22:45则正好覆盖

15分钟周期性模式

发现: events 以 15 分钟为周期重复出现

数据分布:

  • 峰值分钟0, 3, 7, 11间隔 3-4 分钟)
  • 周期15 分钟
  • 含义:可能与定时检查或记录习惯有关

应对策略:

排名 策略 说明
1st 15/45秒技巧 无论 1-29秒 还是31-59秒选15/45秒代表
2nd Step偏移 对step做多个偏移覆盖所有可能时刻
3rd 频率编码 hour_min_onset, hour_min_wakeup

数据质量评估框架

基于前排方案,建立数据质量评估维度:

维度 评估方法 低质量指标 处理策略
周期性重复 降采样+相似度 与前24小时完全相同 标记 periodicity flag
噪声重复 hour+step+anglez计数 重复值>1 标记 noise
未标注events 统计每夜events数 <2 events 过滤或降权
数据异常 enmo统计 enmo值异常大 clip到1

关键数据洞察总结

  1. 极度稀疏标签:需要衰减目标和后处理优化
  2. 分钟偏差是关键:所有前排方案都处理了这个问题
  3. 未标注events普遍存在:周期性检测可识别
  4. 多tolerance AP需要特殊优化15/45秒技巧是制胜关键
  5. 评估指标与数据分布不匹配需要针对tolerance优化
  6. Daily Normalization有效利用每天只有2次活动的先验
  7. 15分钟周期性模式step偏移或频率编码可利用

CMI - Detect Behavior 数据洞察与分析

数据特征理解

多模态传感器数据

三种传感器类型:

传感器 数据维度 特征 缺失率
IMU 加速度计(x,y,z) + 陀螺仪(x,y,z) 运动和旋转 无缺失
THM 5个温度传感器 温度分布 ~3-4%
TOF 5个8×8传感器阵列 距离映射 ~60%

IMU (Inertial Measurement Unit)

  • 6 列:X_accel, Y_accel, Z_accel, X_gyro, Y_gyro, Z_gyro
  • 重力分量:加速度计包含重力,需去除
  • 四元数orientation_X, orientation_Y, orientation_Z, orientation_W
    • 表示设备旋转姿态
    • 不连续性问题四元数在表示相同旋转时有多个值q和-q表示相同旋转
    • 解决方案使用旋转矩阵前两列6D连续表示

THM (Thermopile)

  • 5 列:thermopile_0 ~ thermopile_4
  • 温度传感器,用于检测物体接近
  • 缺失标记-1 表示缺失
  • 缺失率较低约3-4%

TOF (Time-of-Flight)

  • 320 列:tof_0 ~ tof_3195个8×8阵列
  • 距离传感器,检测物体到设备距离
  • 缺失标记-1 表示缺失
  • 缺失严重约60%的数据为-1
  • 图像化处理将8×8阵列降采样为2×2特征图1st Place创新

严重数据缺失问题

缺失分布:

TOF:  ~60% 缺失 (-1 标记)
THM:  ~3-4% 缺失 (-1 标记)
IMU:  无缺失

前排处理策略:

排名 TOF 处理 THM 处理
1st 2×2 pooling后标记缺失mask 简单插值或mask
2nd 特征工程提取有效点统计量 类似TOF处理
3rd 转图像缺失填0 不使用或简单处理
其他 丢弃或mask 丢弃或mask

1st Place 的 TOF 处理创新:

def tof_2x2_pooling_with_mask(tof_data):
    """
    TOF 数据 2×2 pooling + 缺失 mask
    """
    # 每个 8×8 传感器
    for sensor_idx in range(5):
        sensor = tof_data[:, sensor_idx*64:(sensor_idx+1)*64]
        sensor = sensor.reshape(-1, 8, 8)

        # 2×2 pooling
        pooled = sensor.reshape(-1, 4, 2, 2).mean(axis=(2, 3))

        # 缺失 mask
        mask = (sensor == -1).reshape(-1, 4, 2, 2).any(axis=(2, 3))

        # 组合:特征 + mask
        features[:, sensor_idx*4:(sensor_idx+1)*4] = pooled
        features[:, 20+sensor_idx*4:20+(sensor_idx+1)*4] = mask

    return features

个体约束利用

关键约束: 每个 subject × gesture × orientation 组合只出现一次

含义:

  • 训练集中没有重复的 subject × gesture × orientation
  • 验证时可以确保预测结果也满足这个约束
  • 可以用匈牙利算法做全局最优标签分配

前排利用策略:

排名 利用方法 说明
1st 匈牙利算法 全局最优分配,提升 LB 0.01
2nd 阶段感知建模 利用三阶段结构
其他 个体特征 embedding 添加 subject embedding

匈牙利算法实现1st Place

from scipy.optimize import linear_sum_assignment

def hungarian_post_process(predictions, subject_ids, sequence_ids):
    """
    利用 subject × gesture × orientation 唯一约束
    """
    # 对于每个 subject
    for subject in unique(subject_ids):
        # 获取该 subject 的所有预测
        mask = subject_ids == subject
        preds = predictions[mask]
        seqs = sequence_ids[mask]

        # 构建代价矩阵:-log(概率)
        cost_matrix = -np.log(preds + 1e-10)

        # 匈牙利算法:找到最优分配
        row_ind, col_ind = linear_sum_assignment(cost_matrix)

        # 更新预测结果
        for i, j in zip(row_ind, col_ind):
            predictions[mask][i] = np.zeros(n_classes)
            predictions[mask][i][j] = 1.0

    return predictions

三阶段结构

发现: 行为序列有明显的三阶段结构

Transition → Pause → Gesture

阶段特征:

阶段 持续时间 特征 识别要点
Transition 变化 从上一个状态移动到手势位置 运动幅度大
Pause 短暂 手势开始前的准备 运动幅度小
Gesture 重复 核心行为模式(如咬指甲) 周期性模式

前排利用策略:

排名 利用方法 说明
2nd 阶段感知 Attention 每个阶段独立的 attention 权重
6th U-Net分割 将手势阶段作为分割任务
其他 特征工程 添加阶段分类特征

2nd Place 阶段感知 Attention

class PhaseAwareAttention(nn.Module):
    """
    阶段感知 Attention - 每个阶段独立建模
    """
    def __init__(self, d_model, n_heads=8):
        super().__init__()
        # 3个阶段 embedding
        self.phase_emb = nn.Embedding(3, d_model)

        # 每个阶段独立的 attention
        self.transition_attn = nn.MultiheadAttention(d_model, n_heads)
        self.pause_attn = nn.MultiheadAttention(d_model, n_heads)
        self.gesture_attn = nn.MultiheadAttention(d_model, n_heads)

    def forward(self, x, phase_labels):
        # phase_labels: [batch, seq_len] ∈ {0, 1, 2}
        batch, seq_len, d_model = x.shape

        outputs = []
        for t in range(seq_len):
            phase = phase_labels[:, t]  # [batch]

            if phase == 0:  # Transition
                attn_out, _ = self.transition_attn(x[:, t:t+1], x, x)
            elif phase == 1:  # Pause
                attn_out, _ = self.pause_attn(x[:, t:t+1], x, x)
            else:  # Gesture
                attn_out, _ = self.gesture_attn(x[:, t:t+1], x, x)

            outputs.append(attn_out)

        return torch.cat(outputs, dim=1)

BFRB vs 非BFRB 类别分布

18个手势类别

类别 BFRB类型 典型行为
0-7 BFRB 咬指甲、拉头发、抠皮肤等
8-17 非BFRB 拍手、挥手、其他手势

分布特点:

  • 训练集BFRB 和非BFRB 数量相近
  • 个体差异:不同 subject 的手势偏好不同
  • 方向差异:同一手势不同方向的表现不同

处理策略:

  • Phase-aware Mixup:仅在 Gesture 阶段进行 mixup2nd Place
  • 个体 normalization:按 subject 做归一化
  • 类别平衡:确保每个类别有足够样本

测试集变化

关键发现: 测试集约50%的序列仅有 IMU 数据

含义:

  • 不能过度依赖 TOF 和 THM 特征
  • 模型必须能够仅用 IMU 数据做出预测
  • 需要训练仅用 IMU 的模型作为集成成员

前排应对策略:

排名 应对方法
1st 训练IMU-only模型集成时加权
2nd 4个模型IMU-only, IMU+TOF, IMU+THM, All
3rd TOF填0处理但效果受限
其他 简单丢弃缺失传感器

推荐策略:

# 训练时模拟测试集情况
def get_model_input(data):
    """
    根据可用传感器选择模型输入
    """
    has_tof = (data['tof'] != -1).any()
    has_thm = (data['thm'] != -1).any()

    if has_tof and has_thm:
        return model_all(data['imu'], data['tof'], data['thm'])
    elif has_tof:
        return model_imu_tof(data['imu'], data['tof'])
    elif has_thm:
        return model_imu_thm(data['imu'], data['thm'])
    else:
        return model_imu(data['imu'])

异常数据识别

两个异常 subject

Subject 问题 处理策略
SUBJ_019262 数据异常,预测困难 训练时过滤或降权
SUBJ_045235 数据异常,预测困难 训练时过滤或降权

识别方法:

  • 训练集上该 subject 的 loss 异常高
  • 交叉验证该 subject 的预测准确率低
  • 可视化该 subject 的传感器数据,发现异常模式

处理代码:

# 异常 subject 黑名单
ANOMALY_SUBJECTS = ['SUBJ_019262', 'SUBJ_045235']

def filter_anomaly_subjects(dataframe):
    """
    过滤异常 subject
    """
    mask = ~dataframe['subject'].isin(ANOMALY_SUBJECTS)
    return dataframe[mask]

左手系 vs 右手系对齐

发现: 测试集存在左手和右手两种设备朝向

问题:

  • 左手系和右手系的传感器读数方向相反
  • 四元数表示旋转的方式不同
  • 直接混合训练会引入噪声

解决方案(前排通用):

def align_right_handed_system(data):
    """
    左手系 → 右手系对齐
    """
    # 翻转陀螺仪的 x, y 轴
    data['X_gyro'] = -data['X_gyro']
    data['Y_gyro'] = -data['Y_gyro']

    # 调整四元数(取决于具体定义)
    # 这里假设是绕 z 轴旋转 180 度
    data['orientation_X'] = -data['orientation_X']
    data['orientation_Y'] = -data['orientation_Y']

    return data

数据质量评估框架

基于前排方案,建立数据质量评估维度:

维度 评估方法 低质量指标 处理策略
传感器缺失 统计-1值比例 TOF>50%, THM>5% mask处理或训练IMU-only模型
异常subject 按subject统计loss loss > threshold 过滤SUBJ_019262, SUBJ_045235
设备朝向 检测左右手系 四元数和陀螺仪方向 统一到右手系
三阶段一致性 检测阶段标签 阶段跳变 利用三阶段结构特征

关键数据洞察总结

  1. 多模态融合是关键IMU + THM + TOF但测试集仅50%有完整数据
  2. TOF 缺失严重60%需要创新处理2×2 pooling + mask
  3. 个体约束必须利用subject × gesture × orientation 唯一约束可用匈牙利算法
  4. 三阶段结构重要Transition/Pause/Gesture阶段感知建模有效
  5. 四元数不连续性需转换为6D连续表示旋转矩阵前两列
  6. 测试集只有IMU数据必须训练IMU-only模型作为集成成员
  7. 异常数据需处理SUBJ_019262和SUBJ_045235应该过滤或降权
  8. 左手系右手系对齐:统一到右手系避免噪声

多模态时间序列分类的最佳实践

与单模态分类任务不同,多模态任务的特殊考虑:

方面 单模态任务 多模态任务
特征提取 单一特征工程 每个模态独立提取后融合
模型架构 单一编码器 多编码器或早期融合
缺失处理 插值或丢弃 mask处理或模态specific模型
数据增强 简单增强 模态感知增强Phase-aware Mixup
后处理 阈值或NMS 利用约束(匈牙利算法)