623. ISIC 2024 - Skin Cancer Detection with 3D-TBP | isic-2024-challenge
大家好,
我甚至无法解释我现在的心情!?在经历了将近 3.5 年的 Kaggle 竞赛历程后,我终于第一次进入了金牌区,并且是作为个人参赛者做到的。我很高兴我的解决方案在私有榜单(Private LB)上也表现得很稳健。
我要感谢 Kaggle 和组织者创建了这个有趣的挑战,它将有益于世并改善世界各地许多人的生活。也要大声感谢组织者,他们在比赛期间响应非常迅速,这一点非常值得赞赏!
我构建了 2 个(+1 个来自公开)不同的表格数据 pipeline,结合了各种特征、数据和模型组合,并配以来自各种图像模型骨干网络的预测。然后我融合了这些 pipeline 的结果并做出了最终预测。不同 pipeline 的权重通过爬山算法找到。
对于以下每个实验,我都使用了 5 折 StratifiedGroupKfold。对于我添加的每个特征,我首先尝试了 5 种不同的种子组合。如果它们提高了分数,我就提交到榜单。如果在那里也提高了分数,我就保留该特征。
我在这里使用术语"pipeline",是因为这三个 pipeline 中的每一个都使用不同的表格模型,如 LGBM、Catboost 或 XGBoost。除了基础特征外,我还使用了来自我的基线 notebook 的以下特征。
Pipeline 1
这是我的原始脚本,我试图从患者数据中提取尽可能多的上下文特征。我提取了 Z 分数、特征范围、偏度、峰度,以及 feature / max(feature)。我以 0.02 的比例对负样本进行了欠采样。我还添加了 efficient net 预测的平均值作为图像特征(如下所述)。这里我有一个 LGBM 和一个 Catboost。
这个 pipeline 单独在公开榜单上得到了 18.3,CV 为 17.7。
Pipeline 2
使用我自己的代码,我在榜单上的排名在 10 到 20 名之间波动,直到 @greysky 的 notebook 发布。很幸运,我们使用相同的 CV 设置。我想试试融合效果如何,所以我在那个 notebook 中添加了一些我的特征,例如:
.with_columns(
n_images_per_location = pl.col("isic_id").count().over(["patient_id", "tbp_lv_location_simple"])
)
以及我的 swin-transformer 模型的预测作为图像特征。单独来看,这个 pipeline 给出了 17.5 的 CV 和 18.3 的 LB。与 pipeline 1 融合后,我的分数首次显著提升到了 18.6。
Pipeline 3
为了添加第三个图像模型并使集成更加多样化,我创建了另一个具有不同特征设置的 pipeline。我在之前的特征之上创建了排名和不同的 groupby 特征,并使用了不同的数据子集(0.05 负采样比例)。这个 pipeline 单独给出了 17.5 的 CV 和 18.0 的 LB。
我的主要重点是多样性,因此对于每个 pipeline,我选择了不同的骨干网络。
我用不同的数据、骨干网络和优化器设置做了很多实验。以下设置给我的 CV 和 LB 带来了一致的结果。
我只使用了 5% 的负图像,并将正样本过采样 10 倍。我只训练了 3 个 epoch,因为更多的 epoch 会使结果变差。对于 efficient-net 和 swin transformer 模型,我使用了 constant lr:1e-4,对于 convnext 模型使用了以下调度器。对于优化器,我只使用了 Adam。对于损失函数,我使用了 BCE。
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.1)
对于数据增强,我使用了以下设置,类似于之前比赛的获胜者:
train_transforms = A.Compose([
A.VerticalFlip(p=0.5),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(brightness_limit=0.2,contrast_limit=0.2, p=0.75),
A.OneOf([
A.MotionBlur(blur_limit=5),
A.MedianBlur(blur_limit=5),
A.GaussianBlur(blur_limit=5),
A.GaussNoise(var_limit=(5.0, 30.0)),
], p=0.7),
A.OneOf([
A.OpticalDistortion(distort_limit=1.0),
A.GridDistortion(num_steps=5, distort_limit=1.),
A.ElasticTransform(alpha=3),
], p=0.7),
A.CLAHE(clip_limit=4.0, p=0.7),
A.HueSaturationValue(hue_shift_limit=10, sat_shift_limit=20, val_shift_limit=10, p=0.5),
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1, rotate_limit=15, border_mode=0, p=0.85),
A.CoarseDropout(p=0.7),
A.Resize(CFG.img_size, CFG.img_size),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
valid_transforms = A.Compose([
A.Resize(CFG.img_size, CFG.img_size),
A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
ToTensorV2()
])
这里有个有趣的地方,至少对于有视觉背景的人来说,我的图像结果一度卡在 14.8,无论我尝试什么都没用。在检查代码并寻找改进方案时,我意识到 A.Resize 调用位于列表的开头。在着手解决图像部分时,我一开始并没有使用任何增强,只是随着实验append 新的增强。将其移动到归一化之前的末尾,将我的分数提高到了 15.4-15.5 的范围。
为了找到最终模型的权重,我使用了爬山算法。感谢 @cdeotte 在此处的实现 链接。
| 模型 | 权重 |
|---|---|
| pipe3_pred_lgb | 0.407660 |
| pipe3_pred_xgb | 0.272537 |
| pipe1_pred_lgb | 0.152162 |
| pipe1_pred_cat | 0.142441 |
| pipe_2_pred | 0.124199 |
| pipe3_pred_cat | -0.099000 |
这个设置的 CV 是 18.2,LB 是 18.7,这是我同时获得的最好 CV 和 LB。
tbv_lv_y。数据集中有 5 名患者,他们都来自同一家医院,tbv_lv_y 值为负。我添加了每患者的最小值来修复它。CV 稍好,但 LB 没有改善这是我的总结。我会尝试在接下来的几天里添加更多内容。