浮世絵作者予測 LB3位(学生LB1位)解法

はじめに

浮世絵コンペを開催してくださったProbSpace運営の皆様,3ヶ月もの長い期間楽しいコンペティションを運営していただきありがとうございました. また,参加されていた方々もお疲れ様でした.
今回は総合LB3位・学生限定LB1位をとることができました. 最終的にどのような解法を使ったか,それまでにどのようなことを試したかを簡単にまとめていきたいと思います. ソースコードや再現手順はこちらのGitHubに掲載しています.

Final Solution

Model

今回のコンペティションでは普段気になっているアイディアをできるだけ試すことを目指していて,そのアイディアのうちの一つとしてSegmentationのタスクで用いられているMultiScaleな特徴を捉える機構をClassificationのタスクにも応用できないかと考えていました.
似たような思想のネットワークとしてKaggleのHuman Protein Atlas Image Classification Challengeで使われていたGAPNetはResNetなどの特徴抽出ネットワークの最終層だけを用いるのではなく複数の層の出力を用いることでMultiScale特徴を活用する構造になっています.
私はSegmentationタスクで使われている機構のうち,FastFCNに用いられているJPU(Joint Pyramid Upsampling)とASPP(Atrous Spatial Pyramid Pooling)を採用しMultiScale特徴を活用できないかと考えました.この手法を採用した理由としては前のコンペで使った再利用できそうなコードがあったこと,他の手法に比べて比較的高速で精度も高いことが挙げられます.
全体のモデル構造としては下の図のようになります.

ukiyoe.png

BackboneはDenseNet121, SE-ResNeXt50, InceptionResNetV2, ResNet50, CBAM-ResNeXt50を用いました. それぞれのモデルの中間層の活性化関数は元々ReLUだったものをMishに変更しました.

Augmentations

AugmentationはライブラリAlbumentationsを用い過去のコンペを参考にしながらチューニングしました. 最終的には以下のものを使いました.

import albumentations as albu
def get_training_augmentation(size=(256, 256)):
    train_transform = [
        albu.Resize(size[0], size[1]),
        albu.HorizontalFlip(p=0.5),
        albu.OneOf([
            albu.RandomBrightness(0.1, p=1),
            albu.RandomContrast(0.1, p=1),
            albu.RandomGamma(p=1)
        ], p=0.3),
        albu.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.0, rotate_limit=15, p=0.3),
        albu.Cutout(p=0.5)
    ]
    return albu.Compose(train_transform)

その他,学習時にCutMixをalpha=0.25で用いました.

Training

First stage training
  • Backbone: DenseNet121,SE-ResNeXt50,InceptionResNetV2, ResNet50, CBAM-ResNeXt50
  • CV: 5 Split
  • Epochs: 150
  • Optimizer: diffRGrad
  • LR: 1e-3
  • Batch size: 32
  • Scheduler: CosineAnnealingLR(T_max=Epochs, eta_min=1e-8)
  • Loss function: nn.CrossEntropyLoss with label_smoothing(0.1)
Stacking

CNN with Staking を用いて上で学習した5(x cv5)モデルをStackしました. この結果を使ってCV:0.90~91, LB:0.912が得られます.今回のデータでは単純に各モデルの出力を平均するだけでもスコアはそこまで変化しなかったです.

  • Model: input(B, num_class, num_model, 1)->Conv2d(1, 8, (3, 1))->BN->ReLU->Dropout(0.5)->Conv2D(8, 32, (3, 1))->BN->ReLU->Dropout(0.5)->Flatten->Dense(32*num_class, 8*num_class)->Dense(8*num_class, num_class)
  • CV: 5 Split(First Stageと共通)
  • Epochs: 50
  • Optimizer: RAdam
  • LR: 1e-3
  • Batch size: 32
  • Scheduler: CosineAnnealingLR(T_max=Epochs, eta_min=1e-5)
  • Loss function: nn.CrossEntropyLoss()
Second stage training

上記のStackingした結果を用いて出力値が0.8を超えているものに関してPseudo Labelを作成しました.作成したPseudo Labelを学習データに加えてランダムな重みから再学習させました.

  • Backbone: DenseNet121
  • CV: 5 Split
  • Epochs: 300
  • Optimizer: diffRGrad
  • LR: 1e-3
  • Batch size: 32
  • Scheduler: CosineAnnealingLR(T_max=Epochs, eta_min=1e-8)
  • Loss function: nn.CrossEntropyLoss with label_smoothing(0.1)

この結果を使うとCV:0.91~92, LB:0.927が得られます.

Inference

  • TTA: First stageあり/Stackingあり/Second stageなし

Test Time Augmentationはlr-flipとhigh-low-brightnessを用いました.テストデータが非常に少なかったため一概にTTAが効いたとも言い切れずLBスコアを見ながら良い結果を採用しました.

うまくいかなかったもの

  • AdaCos
  • Auto Focal Loss
  • Knowledge Distill/Mean Teacher
    学習に時間がかかり過ぎ......
  • Filter Response Normalization
  • Antialiased-CNNs
  • PixMix(オリジナル)
    CutMixの矩形を数ピクセル単位に分割してMixしたらMixUpとCutMixのいいところどりができるんじゃないかと思ったのですがそんなに甘くはなかったです

Aws4 request&x amz signedheaders=host&x amz signature=d272fbcbabc5ab16a282adeced83d1f082f3928c62885363cd261d9db0ab1417
tattaka

参考までに同条件で100epochほど学習させたときのJPU+ASPPありなしの比較です スクリーンショット_2020-01-16_13.27.00.png

Favicon
new user
コメントするには 新規登録 もしくは ログイン が必要です。