FUJIFILMコンペで入賞してとても良いカメラをもらった話

FUJIFILM AI Academy Brain(s)さんにより開催されていたデータサイエンスチャレンジコンテストに参加していました.最終的になんとか3位で入賞することができ,つい先日,入賞者が集まる表彰式に参加しその景品として非常に良いカメラをいただきました.

今回は自分の解法や表彰式で感じたことなどを簡単に述べようと思います.

ちなみに普段あまりこういったコンペには参加していないため,「ここおかしくない?」という点がいくつかあると思いますので,是非ご指摘していただけるとありがたいです.

コンペ概要

同じくコンペに参加されていた,いのいちさんの記事が非常によくまとまっていて読みやすいので,詳しくはそちらを参照していただければ良いかと思います.

ざっくりと説明すると,

  • 写真の撮影年を推定する問題
  • 入力される画像のサイズは256×256で,1枚の写真の一部を切り出したようなもの

f:id:hrhr08hrhr:20200204225149p:plain
入力画像例

  • 年は1979年~2018年の40クラスで,これはtrain / test共通(多分)
  • trainデータが6686枚,testデータが1671枚
  • 正解ラベルの前後1年以内であれば正解とみなされる
  • testデータにpublic / privateがなく,その代わり締め切り前1週間はleaderboardが非公開になる
  • submitが無制限

といった感じでした.

やったこと

今回は,素直にクラス分類で解きました.中には回帰問題として取り組んでいた方もいたようですが,あまり結果は良くなかったとのことです.

工夫点としては,実際の正解年の前後1年にもラベルを与えてやりました.例えば正解年が2000年であれば,2000年は1,1999年と2001年は0.9として,categorical crossentropyを取りました.これは今回の正解判定の方法に基づいたもので,単純に正解年のみを1として学習させた場合に比べて,それなりに精度が向上していたと思います.ちなみに,端の年となる1979年に関しては,1980年のみに0.9を,2018年に関しても同様に,2017年のみに0.9を与えて学習させていました*1

もう一つの工夫点として,これはただの後処理になってしまうのですが,最終的に端の年である1979年と予測したものは1980年に変換してやりました.ここでも同様に2018年と予測したものも2017年に変換しました.今回の問題では,仮に実際の正解が1979年の場合,1979年と予測しても1980年と予測しても正解とみなされるので,このような後処理を行なっています.大きな効果は見込めませんが,精度が下がることはなく,実際に0.003~0.005くらいのスコアの上昇は見られました.

Data augmentationは次のような感じです.値はかなり適当です.これに加えてMixupも使いました.

transforms_train = albu.Compose([
    albu.Resize(img_height, img_width),
    albu.CLAHE(clip_limit=5.0, tile_grid_size=(8,8), p=0.2),
    albu.RandomBrightnessContrast(p=0.2),
    albu.RandomGamma(gamma_limit=(85, 115), p=0.2),
    albu.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.20, rotate_limit=0, p=0.2),
    albu.Normalize(),
    albu.CoarseDropout(max_holes=1, max_height=img_height//2, max_width=img_width//2,
                       min_height=img_height//8, min_width=img_width//8, p=0.3),
    AT.ToTensor()
])

その他パラメータは以下の通りです.

  • CV: StratifiedKFold(n_splits=10)
  • Batch size: 32
  • Epochs: 80
  • LR: 1e-3からMultiStepLRで適当に下げていった
  • Optimizer: Adam

ここまでの処理を用いて,EfficientNet-b5を学習させた時点で,スコアが0.7019でした.
ここからどうしたものか,と試したことはあったのですが,それはうまくいかなかったのでまた後ほど.


結局,0.7019を出した手法をベースとして,画像サイズを変えて学習させたモデルをアンサンブルしたのと,seed averagingを用いました.
画像サイズをそれぞれ256, 320, 400で学習させた3つのモデルの出力の平均を取るだけでもスコアはかなり向上し0.7516になりました.
最終的には画像サイズ200, 256, 320, 400, 450で学習させたモデルそれぞれに対しseed averagingを行い,それぞれの出力結果に対して適当な重みで加重平均を取ることで0.7612まで伸ばすことができました.

やったけどうまくいかなかったこと

そもそも人の目で見ても撮影年の判定なんて難しいのに,いきなり40クラス分類なんてできるのか?とずっと思っていました.そこで,古いor新しいくらいならなんとか識別できるのではないかと考え,まず2クラス(1979年~1998年,1999年~2018年)に大きく分類し,その後それぞれに対して20クラス分類を行うモデルを試しました.
が,結論から言うとうまくいかず,大別したそれぞれのクラスにおいて端となる年(1998年とか1999年とか)を出力しにくくなってしまっていました.
それなら,大きく3クラス,4クラスに分類した後にさらに細かく分類するモデルを用意してそれらをアンサンブルしてやればいいのでは,とも考えましたが,どうしてもナイーブに40クラス分類をした時のスコア(0.7019)を超えることができず,この方法は諦めました.この方法だと細かい分類の際に学習に使えるデータがどうしても少なくなってしまうからやっぱり難しかったのかな,という気がしています.

反省点や表彰式・懇親会を通して感じたこと

まず,今回はEfficientNet以外のモデルをあまり試せていなかったので,そこは反省点です.ましてやアンサンブルするのであれば別のモデルを試してみるべきだろうと.
他には,lossの可視化を怠っていたために,まだlossが落ちそうなのにepoch数を小さく設定してしまっていたりなど….

今回3位入賞ということで表彰式でLTもさせていただきましたが,正直1位,2位の方々と比べてコツコツとした作業が足りなかったのかなと思っています.2位の方はdata augmentationのパラメータの調整をかなり慎重にかつ色々試しておられましたし,1位の方もかなり学習に時間を費やしておられました*2.色々な手法を試す,という意味も含めて,手を動かす量が全体的に不足していたんだろうなと感じました.

あとは今回のコンペにおいて,入賞者のほとんどが使うベースラインを提供し,かつ,訳あって非公式記録ながらもleaderboardのトップに君臨し続けていたSh1r0さんとも色々お話をさせていただきました.解法も公開してくださっています.
sh1r09.hatenablog.com
最終的にはシンプルな解き方とはなっていますが,お話しさせていただいた限り様々な手法でチャレンジされていたように感じました.実はSh1r0さんが使われている後処理のプロセス自体は僕も思いついてはいたのですが,実際に試していなかったため,かなり悔いが残ります.

まとめ

このFUJIFILMさんのコンテスト,今回が第3回目の開催だったのですが,僕は第2回の時にも参加していて,その時は奨励賞としてチェキをいただきました.ただ,上位の方が良いカメラをもらっていたのを見て,「次こそはあのカメラを…」と密かに思っていたので,今回とても良いカメラをいただけてすごく嬉しかったです.また,実はしっかりと時間をかけて取り組んだ初めてのコンペだったので,結果という形を出せて本当に良かったです.その分反省する点も多かったので,今回の経験を別のコンペ等にも生かしていきたいと思います.
最後になりましたが,このような場を用意してくださったFUJIFILMの皆様,コンテスト運営事務局の皆様,また,表彰式でお話しさせていただいた皆様,本当にありがとうございました!
次回大会もあるらしいので,興味のある学生の方々は是非参加してみてください*3.本当に良いカメラなので.
 

*1:いま考えてみるとなかなかおかしなことをやっている気もする…

*2:ついでに言うと,お二人ともにLTの資料の作り込みもしっかりしていて,こちらが恥ずかしくなるくらいだった.

*3:僕も何かしらの形でコンテストに貢献できればいいなあと思っています.