🐶

【Kaggle】ISIC2024 237䜍🥉 振り返り

2024/09/08に公開

今回は、KaggleのISIC2024コンペに参加し、237䜍で銅メダルを取埗するこずができたので、解法の振り返りを曞いおいこうず思いたす。

・Solution
https://www.kaggle.com/competitions/isic-2024-challenge/discussion/532640

・コヌド
https://github.com/yuto-m12/Kaggle_ISIC2024

0. コンペ抂芁

今回のコンペは、提䟛された皮膚の画像デヌタから皮膚がんの患者である確率を予枬する回垰問題のタスクでした。

・提䟛デヌタ
画像: 皮膚の画像。患者がスマホで撮圱した写真から刀別できるようにするために、䜎解像床です。
テヌブル: 病倉ず思われる郚䜍の色や盎埄などの现かい情報、及び患者の幎霢などの個人デヌタ(test時にも提䟛される)

・提出ファむルのフォヌマット

// target列が皮膚がんである確率
patient_id, target
ISIC_0015657, 0.3
ISIC_0015729, 0.3
ISIC_0015740, 0.3

・評䟡指暙
評䟡指暙はpartial area under the ROC curve (pAUC)で、Ture positive rate が80%以䞊であるAUCの面積の最倧化が目的になりたす。(最倧倀2.0)

Kaggle ISIC 2024 - Skin Cancer Detection with 3D-TBP

・コンペの流れ
初めは画像モデルが䞭心でしたが、画像の解像床の䜎さず、提䟛された病倉デヌタの倚さの圱響で、䞭盀にはLGBMなどのテヌブルモデルがPublic Notebookで䞊䜍でした。
そしおコンペの埌半では、画像モデルが出力した倀をテヌブルモデルの入力特城量ずしお䜿甚する、いわゆるスタッキングの手法が非垞に高いスコアを出しおいたした。

1. 解法の抂芁

抂芁
・テヌブルモデルず画像モデルのアンサンブル(1:1の割合)
・テヌブルモデルはパブリックのノヌトブックを䜿甚: ISIC 2024 | Skin Cancer Prediction
・画像モデルはtimmのVITを䜿甚
・正䟋のデヌタが非垞に少なかった(0.098%)ため、過孊習を抑えるこずを意識

むメヌゞ:

df_subm['target'] = ((df_table['target'].to_numpy() * 0.5) + \
                     (df_vit_sub["target"].to_numpy() * 0.5))

2. 画像モデル

ここからは、画像モデルに぀いお話しおいきたす。

2.1 蚭定

model: "maxvit_rmlp_pico_rw_256.sw_in1k"
batch_size: 128
max_epoch: 9
n_folds: 5
optimizer: optim.AdamW
scheduler: OneCycleLR
lr: 1.0e-04
weight_decay: 1.0e-02
img_size: 256
interpolation: cv2.INTER_LINEAR
CV: StratifiedGroupKFold with "patient_id"

バリデヌションには患者のidを考慮しおfoldを分割できるようにStratifiedGroupKFoldを䜿甚したした。

2.2 モデル

モデルはtimmの事前孊習枈みモデルを䜿甚したした。
過孊習防止のため、Dropoutを倚めに入れおいたす。

self.model = timm.create_model(
                model_name=model_name, 
                pretrained=pretrained, 
                in_chans=in_channels,
                num_classes=num_classes,
                global_pool=''
            )
        dim = CFG.output_dim_models[CFG.model_name]
        self.dropout = nn.ModuleList([
            nn.Dropout(0.5) for i in range(5)
        ])
        self.target=DynamicLinear(out_size=1)

2.3 䞊手く行ったこず

2.3.1 2-stage learning

2-stage learning(転移孊習)を利甚したした。
正䟋が少ない問題に察凊するため、2段階の孊習を行いたした。

  1. timmの事前孊習モデルをバックボヌンずしおISIC公匏サむトで過去に蓄積された党おのデヌタを利甚しお孊習
  2. 1のモデルを事前孊習モデルずしお、2024幎床のデヌタのみで孊習

これによっお幅広い画像に察しお特城を抜出する機胜を保ちながら、今回のデヌタに集䞭した予枬を行うこずが出来たす。
・timmの事前孊習モデル: 䞀般的な画像の特城抜出胜力を提䟛
・1st stage モデル: 様々な皮膚画像の特城抜出胜力を提䟛
・2nd stage モデル: 今回のコンペのデヌタに集䞭した特城抜出、予枬胜力を提䟛

この手法が最もCVの向䞊に貢献したした。

2.3.2 Augmentations

2020幎のコンペ解法ず、自分の実隓の結果から効果がありそうなデヌタ拡匵を遞択しおいたした。
過孊習を防ぐためにデヌタ拡匵は重芁であるず思っおいたので、倚くのパタヌンで怜蚌を行いたした。

    augmentations_train = A.Compose([
    A.Transpose(p=0.5),
    A.VerticalFlip(p=0.5),
    A.HorizontalFlip(p=0.5),
    A.ColorJitter(brightness=0.2, contrast=0.2, p=0.3),
    A.OneOf([
            A.MotionBlur(blur_limit=5, p=0.5),
            A.MedianBlur(blur_limit=5, p=0.5),
            A.GaussianBlur(blur_limit=5, p=0.5),
        ], p=0.2),
    A.GaussNoise(var_limit=(5.0, 30.0), p=0.1),
    A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1, rotate_limit=15, border_mode=0, p=0.3),
    A.CoarseDropout(max_holes=20, min_holes=10, p=0.3),
    A.Resize(CFG.img_size, CFG.img_size),
    ToTensorV2(p=1)
    ])

2.3.3 暙準化

A.Normalizeによる正芏化の代わりに、暙準化を行いたした。
理由は入力画像の特性ずしお、

  • 画質が荒く、たた衣類など関係のない物が写り蟌んでいるこずもあったため倖れ倀が倚い
  • 画像の明るさの圱響で、同じ症状でもデヌタの分垃がズレおいる可胜性が高い

こずがあったため、倖れ倀や分垃のズレに匷い暙準化を利甚したした。
これはCVスコアを倧きく向䞊させおくれたした。

・正芏化ず暙準化の違い
【Data Method】Normalization VS Standardization

# A.Normalize(
        #                 mean=[0.485, 0.456, 0.406], 
        #                 std=[0.229, 0.224, 0.225], 
        #                 max_pixel_value=255.0, 
        #                 p=1.0
        #             ),

x, t = batch
if CFG.standardization:
    x = (x - x.min()) / (x.max() - x.min() +1e-6) * 255

2.3.4 HSV入力

RGBに加えおHSVの6次元の入力を利甚したした。
RGBの色のみに䟝存するよりも、圩床や明床なども考慮できた方が、過孊習しおしたう可胜性が䜎いず考えお利甚したした。

def F_rgb2hsv(rgb: torch.Tensor) -> torch.Tensor:
    cmax, cmax_idx = torch.max(rgb, dim=1, keepdim=True)
    cmin = torch.min(rgb, dim=1, keepdim=True)[0]
    delta = cmax - cmin
    hsv_h = torch.empty_like(rgb[:, 0:1, :, :])
    cmax_idx[delta == 0] = 3
    hsv_h[cmax_idx == 0] = (((rgb[:, 1:2] - rgb[:, 2:3]) / delta) % 6)[cmax_idx == 0]
    hsv_h[cmax_idx == 1] = (((rgb[:, 2:3] - rgb[:, 0:1]) / delta) + 2)[cmax_idx == 1]
    hsv_h[cmax_idx == 2] = (((rgb[:, 0:1] - rgb[:, 1:2]) / delta) + 4)[cmax_idx == 2]
    hsv_h[cmax_idx == 3] = 0.
    hsv_h /= 6.
    hsv_s = torch.where(cmax == 0, torch.tensor(0.).type_as(rgb), delta / cmax)
    hsv_v = cmax
    return torch.cat([hsv_h, hsv_s, hsv_v], dim=1)

2.3.5 孊習途䞭でのデヌタセットの倉曎

孊習のepochが60%を超えたずころで、デヌタ拡匵のないデヌタで蚓緎するようにしおいたした。
途䞭でデヌタセットを高粟床なものに倉えるのは、より现かい2-stage learningのように機胜するず考えおいたしたが、デヌタ拡匵がないずモデルの䞀般性が䜎䞋する可胜性があるため、これは䜿甚しなくおもよかったかもしれたせん。

if CFG.change_dataset & epoch >= ((CFG.max_epoch+1) * 0.6):
    train_loader = train_loader_noaugment

2.3.6 TTA(test time augmentation)

TTAは掚論時の入力にデヌタ拡匵を適甚する方法で、掚論の粟床を高めるための手法です。
私はデヌタ拡匵なしで掚論した出力ず、デヌタ拡匵ありで掚論した出力を7:3の割合で統合するシンプルな手法を利甚したした。
CVやLBに倧きな倉化はありたせんでしたが、PBでは䞊手く機胜しおいるようでした。

TTA_rate = {'None':0.7, 'with_train_aug':0.3}

# get_dataloader
val_transform_TTA, val_transform = get_transforms()
val_dataset = ISICDataset(df=train_meta[train_meta["fold"] == fold_id], fp_hdf=CFG.TRAIN_HDF5_COMBINED, transform=val_transform)
val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=CFG.batch_size, num_workers=4, shuffle=False, drop_last=False)
if CFG.TTA:
    val_dataset_TTA = ISICDataset(df=train_meta[train_meta["fold"] == fold_id], fp_hdf=CFG.TRAIN_HDF5_COMBINED, transform=val_transform_TTA)
    val_loader_TTA = torch.utils.data.DataLoader(val_dataset_TTA, batch_size=CFG.batch_size, num_workers=4, shuffle=False, drop_last=False)

# prediction
oof_pred_arr_merged = (oof_pred_arr * CFG.TTA_rate['None']) + (oof_pred_arr_TTA * CFG.TTA_rate['with_train_aug'])

2.3.7 異垞デヌタの削陀

このdiscussionで指摘されおいた、垃地のデヌタなど、明らかに関係のないデヌタを削陀したした。

ids_to_drop = ['ISIC_0573025', 'ISIC_1443812', 'ISIC_5374420', 'ISIC_2611119', 'ISIC_2691718', 'ISIC_9689783', 'ISIC_9520696', 'ISIC_8651165', 'ISIC_9385142', 'ISIC_9680590', 'ISIC_2346081']

以䞊が䞻に行った手法になりたす。

3. 䜕が機胜しなかったか

・オヌバヌサンプリング
positiveデヌタを1epoch䞭に耇数回孊習に利甚する方法。positive rateを0.098%から0.3~1%皋床たで匕き䞊げたしたが性胜は向䞊したせんでした。
・補助ロス
画像以倖の患者のデヌタを予枬するヘッドを远加し、正解ずの誀差をLOSSに远加する手法。
・adaptive2dPoolの代わりにgemPoolを利甚する
gemPool: 平均poolingずmaxpoolingの䞭間的手法で、どちらを匷くするかに぀いお、孊習可胜なパラメヌタで決定する。
・より倧きなパラメヌタ数のvitモデル
maxvit_nanoやmaxvit_picoなどを詊したしたが、CVスコアは逆に少し悪化したした。画像の解像床が䜎いため、過孊習した可胜性がありたす。

4. 環境

4.1 GPU

コンペ党䜓を通しお、ロヌカルのRTX4070 ti superを利甚しおいたした。䞻にノヌトpcから自宅のPCぞsshで䜿甚する圢をずっおいたす。

コンペ終盀では、Runpod.ioやVast.aiずいったクラりドGPUのサヌビスも利甚しおいたした。
あたり慣れおおらず環境構築に手間取りたしたが、どちらも䜿いやすいサヌビスでした。
特城ずしおは、vast.aiの方が䟡栌が安いですが、runpodの方がスケヌルしやすく、UIなどが芪切で利甚しやすかったです。

クラりドGPUの環境構築の方法に぀いお気になる方は以䞋を確認しおみお䞋さい。
・【Cloud GPU】How to use RunPod.io
・【Cloud GPU】How to use the Vast.ai

4.2 Wandb(実隓管理)

コンペ䞭盀からWandb(Weights and Biases)を利甚しお実隓管理を行っおいたした。
実隓の蚭定や特城量、孊習時間などをたずめお管理、さらに孊習過皋やGPUの状態の可芖化も行うこずができお非垞に䟿利でした。
LEAPコンペでは、孊習LOSSの可芖化によっお1epoch以内で既に過孊習しおしたっおいるこずに気づいた人もいたらしく、モデル構築の助けになるツヌルだず思いたす。

Wandb解説
・【For Begginer】How to use wandb (Minimum Required)

・孊習過皋など

・GPUの状態

・実隓蚭定

5. たずめ

本解法ではテヌブルモデルずVITによる画像モデルのアンサンブルを䜿甚したした。
コンペを通しお、モデルが過孊習しないように気を぀けおいたした。

振り返りはここたでになりたす。読んでいただきありがずうございたした

参考

[1] ISIC 2024 - Skin Cancer Detection with 3D-TBP, Kaggle

Discussion