Files
BZJZ_Material/文档润色流和知识库构建流/claude-scholar/skills/kaggle-learner/references/knowledge/tabular/amp-parkinsons-2021.md
2026-06-11 03:33:14 +08:00

17 KiB
Raw Blame History

AMP®-Parkinson's Disease Progression Prediction (2023)

Competition Brief

竞赛基本信息

  • 主办方: AMP (Accelerating Medicines Partnership)
  • 时间: 2023年
  • 类型: 表格数据/医疗预测
  • 数据规模: 小样本数据集
  • 评价目标: SMAPE (Symmetric Mean Absolute Percentage Error)

任务描述 预测帕金森病患者的疾病进展情况。使用蛋白质和肽段数据(通过质谱测量脑脊液样本)来预测患者未来的 MDS-UPDRS 评分。

数据特点

  • 蛋白质数据: 227个蛋白质特征
  • 肽段数据: 来自多个质谱实验
  • 时间序列: 每个患者有多次访问记录
  • 样本量: 相对较小(小样本竞赛)
  • 目标变量: MDS-UPDRS 评分的进展

评价指标 SMAPE (Symmetric Mean Absolute Percentage Error)

  • 对称性平均绝对百分比误差
  • 范围 [0, 200],越小越好
  • 对异常值相对鲁棒

Top Solutions Analysis

1st Place - Connecting Dotts (Dmitry Gordeev et al.)

核心策略

  • 模型组合: LightGBM + Neural Network 的简单平均
  • 特征工程: 精心设计的蛋白质和肽段聚合特征
  • 数据处理: 针对神经网络的标准化和二值化

关键技术细节

  1. 特征工程

    • 蛋白质和肽段的聚合统计量(均值、中位数、标准差)
    • 时间序列特征的构造
    • 蛋白质-肽段关系特征的提取
  2. 模型架构

    • LightGBM: 梯度提升树模型
    • Neural Network: 深度学习模型
    • 集成策略: 简单平均
  3. 数据预处理

    • NN专用预处理: 特征缩放
    • 特征二值化处理
    • 缺失值处理策略

代码要点

# 模型集成示例
final_prediction = (lgb_pred + nn_pred) / 2

2nd Place - No Luck, All Skill

核心策略

  • 发布时间: 2023年6月19日
  • 强调特征工程的重要性
  • 多模型集成策略

关键特征

  • 详细的特征工程流程
  • 模型融合技术
  • 验证策略设计

3rd Place - Hajime Tamura

核心策略

  • 发布时间: 2023年5月19日
  • 分组策略: 将数据分成两组分别优化
  • 简洁的解决方案(三个主要函数)

关键创新

  • 数据分组优化
  • 针对性模型训练
  • 简化流程提升效率

4th/5th Place - Ambrosm (#5: Find the Control Group)

核心策略

  • 发布时间: 2023年5月18日
  • 控制组识别: 关键创新点
  • 利用对照组信息改进预测

关键洞察

  • 识别并分离控制组样本
  • 针对不同组别使用不同策略
  • 提升模型区分度

9th Place - Makotu

核心策略

  • 发布时间: 2023年5月18日
  • 详细的特征工程和模型调优

13th Place - FNOA

技术要点

  • 中等排名的稳定方案
  • 实用的特征工程方法

43rd Place - Wojciech Victor Fulmyk (Top 3% Silver)

重要发现

  • XGBoost 和 LightGBM 表现不佳
  • 强调传统树模型在这个数据集上的局限性
  • 探索其他模型方向

技术要点

# 他们的发现表明传统 GBDT 可能不是最佳选择
# 需要考虑其他模型或更复杂的特征工程

89th Place - Giba (Non-Leaky Solution)

核心策略

  • 强调无数据泄露的干净方案
  • 可复现的验证策略

Common Techniques Across Solutions

1. Feature Engineering Patterns

蛋白质/肽段聚合特征

# 时间聚合
protein_stats = train.groupby('patient_id')['protein'].agg([
    'mean', 'median', 'std', 'min', 'max'
])

# 肽段聚合
peptide_stats = train.groupby('patient_id')['peptide'].agg([
    'mean', 'count', 'nunique'
])

时间序列特征

  • 访问间隔时间
  • 进展速度估计
  • 基线和随访差异

蛋白质-肽段关系

  • 蛋白质包含的肽数量
  • 肽段来源的蛋白质信息

2. Model Selection Insights

成功模型

  • LightGBM (部分方案)
  • Neural Networks / MLP
  • 集成方法

需要谨慎的模型

  • XGBoost (43rd方案指出效果不佳)
  • 纯线性模型
  • 单一模型(推荐集成)

3. Validation Strategies

关键原则

  • 避免患者级别的数据泄露
  • 时间基础的分割
  • 分组交叉验证
from sklearn.model_selection import GroupKFold

gkf = GroupKFold(n_splits=5)
for train_idx, val_idx in gkf.split(X, y, groups=patient_ids):
    # 训练和验证

4. Data Leakage Prevention

常见陷阱

  • 同一患者的多次访问分散在训练/验证集
  • 未来信息泄露到训练集
  • 蛋白质/肽段测试集信息泄露

预防措施

  • 严格的患者级别分割
  • 时间有序分割
  • 仔细的特征构造审计

Code Templates

Basic Feature Engineering

import pandas as pd
import numpy as np

def create_protein_features(train_proteins, test_proteins):
    """创建蛋白质聚合特征"""
    def process(df):
        stats = df.groupby('patient_id')['NPX'].agg([
            ('protein_mean', 'mean'),
            ('protein_std', 'std'),
            ('protein_min', 'min'),
            ('protein_max', 'max')
        ]).reset_index()
        return stats

    train_stats = process(train_proteins)
    test_stats = process(test_proteins)

    return train_stats, test_stats

def create_peptide_features(train_peptides, test_peptides):
    """创建肽段聚合特征"""
    def process(df):
        stats = df.groupby('patient_id')['PeptideAbundance'].agg([
            ('peptide_mean', 'mean'),
            ('peptide_std', 'std'),
            ('peptide_count', 'count')
        ]).reset_index()
        return stats

    train_stats = process(train_peptides)
    test_stats = process(test_peptides)

    return train_stats, test_stats

def create_time_features(train_clinical, test_clinical):
    """创建时间序列特征"""
    def process(df):
        df = df.copy()
        df['visit_month'] = df['visit_month'].astype(int)
        df['pred_month'] = df['visit_month'] + df['updrs_test_month']

        # 计算自基线以来的时间
        df['months_since_baseline'] = df.groupby('patient_id')['visit_month'].transform(lambda x: x - x.min())

        return df

    return process(train_clinical), process(test_clinical)

Model Training Template

import lightgbm as lgb
from sklearn.model_selection import GroupKFold
from sklearn.metrics import mean_absolute_error

def smape(y_true, y_pred):
    """SMAPE 评估指标"""
    return 100 * np.mean(2 * np.abs(y_pred - y_true) / (np.abs(y_true) + np.abs(y_pred) + 1e-8))

def train_lightgbm(X_train, y_train, groups, params=None):
    """训练 LightGBM 模型"""
    if params is None:
        params = {
            'objective': 'regression',
            'metric': 'mae',
            'learning_rate': 0.01,
            'num_leaves': 31,
            'max_depth': -1,
            'feature_fraction': 0.8,
            'bagging_fraction': 0.8,
            'bagging_freq': 5,
            'verbose': -1
        }

    gkf = GroupKFold(n_splits=5)
    models = []
    scores = []

    for train_idx, val_idx in gkf.split(X_train, y_train, groups=groups):
        X_tr, X_val = X_train.iloc[train_idx], X_train.iloc[val_idx]
        y_tr, y_val = y_train.iloc[train_idx], y_train.iloc[val_idx]

        train_data = lgb.Dataset(X_tr, label=y_tr)
        val_data = lgb.Dataset(X_val, label=y_val, reference=train_data)

        model = lgb.train(
            params,
            train_data,
            num_boost_round=10000,
            valid_sets=[train_data, val_data],
            callbacks=[lgb.early_stopping(100), lgb.log_evaluation(0)]
        )

        pred = model.predict(X_val)
        score = smape(y_val, pred)

        models.append(model)
        scores.append(score)

    print(f'Average SMAPE: {np.mean(scores):.2f}')

    return models, scores

# 使用示例
# models, scores = train_lightgbm(X_train, y_train, patient_ids)

Neural Network Template

import tensorflow as tf
from sklearn.preprocessing import StandardScaler

def create_nn_model(input_dim, hidden_units=[256, 128, 64]):
    """创建神经网络模型"""
    model = tf.keras.Sequential([
        tf.keras.layers.Input(shape=(input_dim,)),
    ])

    for units in hidden_units:
        model.add(tf.keras.layers.Dense(
            units,
            activation='relu',
            kernel_regularizer=tf.keras.regularizers.l2(0.01)
        ))
        model.add(tf.keras.layers.Dropout(0.3))
        model.add(tf.keras.layers.BatchNormalization())

    model.add(tf.keras.layers.Dense(1, activation='linear'))

    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
        loss='mae',
        metrics=['mae']
    )

    return model

def train_nn(X_train, y_train, groups, epochs=100, batch_size=32):
    """训练神经网络"""
    # 标准化
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)

    gkf = GroupKFold(n_splits=5)
    models = []
    scores = []

    for train_idx, val_idx in gkf.split(X_train_scaled, y_train, groups=groups):
        X_tr, X_val = X_train_scaled[train_idx], X_train_scaled[val_idx]
        y_tr, y_val = y_train.iloc[train_idx], y_train.iloc[val_idx]

        model = create_nn_model(X_train.shape[1])

        early_stop = tf.keras.callbacks.EarlyStopping(
            monitor='val_loss',
            patience=10,
            restore_best_weights=True
        )

        history = model.fit(
            X_tr, y_tr,
            validation_data=(X_val, y_val),
            epochs=epochs,
            batch_size=batch_size,
            callbacks=[early_stop],
            verbose=0
        )

        pred = model.predict(X_val).flatten()
        score = smape(y_val, pred)

        models.append((model, scaler))
        scores.append(score)

    print(f'Average SMAPE: {np.mean(scores):.2f}')

    return models, scores

Ensemble Template

def ensemble_predictions(lgb_models, nn_models, X_test):
    """集成多个模型的预测"""
    # LightGBM 预测
    lgb_preds = np.mean([model.predict(X_test) for model in lgb_models], axis=0)

    # NN 预测(需要标准化)
    _, scaler = nn_models[0]
    X_test_scaled = scaler.transform(X_test)
    nn_preds = np.mean([
        model.predict(X_test_scaled).flatten()
        for model, _ in nn_models
    ], axis=0)

    # 简单平均
    final_pred = (lgb_preds + nn_preds) / 2

    return final_pred

Best Practices

1. Data Understanding

蛋白质数据特性

  • 227个蛋白质可能来自不同通路
  • 部分蛋白质可能高度相关
  • 需要探索蛋白质-疾病关系

肽段数据特性

  • 肽数量远大于蛋白质数
  • 多个肽段可能来自同一蛋白质
  • 肽段丰度需要归一化

临床数据特性

  • MDS-UPDRS 评分范围 0-260
  • 不同子评分(第一部分到第四部分)
  • 访问时间间隔不均匀

2. Feature Engineering Guidelines

DOs

  • 创建患者级别的聚合特征
  • 利用时间序列信息
  • 探索蛋白质-肽段关系
  • 考虑蛋白质生物学意义
  • 使用领域知识构造特征

DON'Ts

  • 在测试集上计算统计量
  • 混合不同患者的未来信息
  • 忽略数据的时间顺序
  • 过度使用目标编码(容易泄露)

3. Model Selection Strategy

推荐流程

  1. 从简单模型开始(线性模型、决策树)
  2. 尝试 LightGBM部分方案有效
  3. 探索神经网络1st方案使用
  4. 集成多个模型
  5. 针对性调整超参数

模型选择考虑

  • 数据量小 → 简单模型或强正则化
  • 特征多 → 特征选择或降维
  • 时序特性 → 考虑时间序列模型
  • 集成收益 → 尝试模型融合

4. Validation Strategy

推荐方法

# 患者级别的 Group K-Fold
from sklearn.model_selection import GroupKFold

gkf = GroupKFold(n_splits=5)
for fold, (train_idx, val_idx) in enumerate(gkf.split(X, y, groups=patient_ids)):
    print(f'Fold {fold + 1}')
    # 训练和验证

时间序列分割

from sklearn.model_selection import TimeSeriesSplit

tscv = TimeSeriesSplit(n_splits=5)
for fold, (train_idx, val_idx) in enumerate(tscv.split(X)):
    # 确保验证集在时间上晚于训练集

5. Common Pitfalls

数据泄露

  • 将同一患者的多次访问分散到训练和验证集
  • 在分割前计算全局统计量
  • 使用未来信息预测过去

过拟合

  • 特征过多而样本过少
  • 过度调参导致验证集泄露
  • 复杂模型在小数据集上

评估偏差

  • 使用错误的评估指标
  • 忽略 SMAPE 的对称性
  • 不关注预测的分布特性

6. Domain Knowledge Integration

帕金森病相关

  • MDS-UPDRS 评分的临床意义
  • 蛋白质标志物的生物学作用
  • 疾病进展的非线性特性

蛋白质组学

  • 质谱数据的技术变异
  • 蛋白质-肽段的定量关系
  • 缺失值的含义

7. Hyperparameter Tuning

LightGBM 关键参数

params = {
    'learning_rate': 0.01,      # 降低学习率
    'num_leaves': 31,           # 控制复杂度
    'max_depth': -1,            # 不限制深度
    'min_data_in_leaf': 20,     # 小数据集增大此值
    'feature_fraction': 0.8,    # 特征采样
    'bagging_fraction': 0.8,    # 数据采样
    'bagging_freq': 5,
    'lambda_l1': 0.1,           # L1 正则化
    'lambda_l2': 0.1,           # L2 正则化
}

神经网络关键参数

# 小数据集推荐
hidden_units = [128, 64, 32]   # 减少层数和单元数
dropout_rate = 0.3              # 增加 dropout
l2_reg = 0.01                   # L2 正则化
learning_rate = 0.001           # 适中学习率
batch_size = 32                 # 小批量

Key Takeaways

  1. 小样本竞赛特点

    • 特征工程比模型复杂度更重要
    • 避免过拟合是关键
    • 简单模型集成可能优于复杂单模型
  2. 医疗数据特殊性

    • 需要理解领域知识
    • 数据泄露风险更高
    • 评估指标的临床意义
  3. 成功的共同点

    • 仔细的特征工程
    • 严格的验证策略
    • 模型集成
    • 避免数据泄露
  4. 需要注意的陷阱

    • XGBoost/LightGBM 不是万能的43rd方案发现
    • 数据泄露容易但难以发现
    • 小样本的过拟合风险
  5. 推荐的学习路径

    • 从 1st, 2nd, 3rd 方案学习顶级思路
    • 从 5th, 9th 方案学习实用技巧
    • 从 43rd 方案学习失败经验
    • 综合多个方案形成自己的方法

Resources

Official Writeups

External Resources

Competition Pages

Code Notebooks