浮世絵作者予測 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特徴を活用できないかと考えました.この手法を採用した理由としては前のコンペで使った再利用できそうなコードがあったこと,他の手法に比べて比較的高速で精度も高いことが挙げられます.
全体のモデル構造としては下の図のようになります.
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スコアを見ながら良い結果を採用しました.
うまくいかなかったもの