# 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 采样）
- Spectrogram：11,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 Training：Stage 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 Training**：Stage 1 全数据，Stage 2 仅高质量样本 (votes ≥10)

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

**2nd Place - COOLZ**

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

实现细节：
- 输入：50 秒 EEG 转换的 Spectrogram（256×256×3 通道）
- 3D-CNN：3D 卷积核同时处理时间和频率维度
- 两阶段训练：第一阶段 100 epoch，第二阶段 50 epoch
- 最终 KL-Divergence：~0.275

**3rd Place - nvidia-dd (DIETER)**

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

实现细节：
- 筛选条件：votes ≥10，consensus 标签一致性高
- 特征：时域（统计特征）+ 频域（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
- BiLSTM：2 层双向，隐藏层 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](https://www.kaggle.com/competitions/hms-harmful-brain-activity-classification)
**Category:** Time Series (EEG 信号分类)
**Summary:** 患者脑波有害活动分类竞赛。数据包含 1D EEG 信号（50秒，200Hz）和 2D Spectrogram（10分钟），需要预测专家投票分布。**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)
```python
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通道EEG，50秒
# scalogram = create_scalogram(eeg_data)
# vertical_stack = np.vstack(scalograms)  # 拼接后resize到512x512
```

### Bipolar Montage 预处理 (yamash's approach)
```python
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
```python
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)
```

### 非负线性回归集成
```python
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 训练流程
```python
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 验证
```python
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)
```python
# 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)
```python
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 Training**：Stage 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% 缺失（-1），THM 约 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 提升

**实现：**
```python
def entmax(x, alpha=1.5, dim=-1):
    return torch.softmax(x * alpha, dim=dim)
```

#### 双模态数据的时间对齐

**数据结构：**
- **Spectrogram**：10 分钟（低时间分辨率，高频率信息）
- **EEG**：50 秒中心段（高时间分辨率，低频率信息）
- 两者中心 50 秒是同一数据

**洞察：**
- Spectrogram 提供全局上下文（10分钟趋势）
- EEG 提供精细时序信息（50 秒细节）
- 这是**同一数据的两种表示**，不是独立信息

**处理策略：**
- 大多数获胜者**专注于 EEG**（2nd, 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 名方案，推荐的数据预处理流程：

```python
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 的周期性检测方法：**
```python
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 评估指标

**评估方式：**
```python
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_319`（5个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 处理创新：**
```python
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）：**
```python
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：**
```python
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 阶段进行 mixup（2nd Place）
- **个体 normalization**：按 subject 做归一化
- **类别平衡**：确保每个类别有足够样本

#### 测试集变化

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

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

**前排应对策略：**

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

**推荐策略：**
```python
# 训练时模拟测试集情况
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 的传感器数据，发现异常模式

**处理代码：**
```python
# 异常 subject 黑名单
ANOMALY_SUBJECTS = ['SUBJ_019262', 'SUBJ_045235']

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

#### 左手系 vs 右手系对齐

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

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

**解决方案（前排通用）：**
```python
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 | 利用约束（匈牙利算法） |

