Kaggle NFLコンペ紹介（優勝ポエムを添えて）

Kaggleの*[NFL Health & Safety - Helmet Assignment](https://www.kaggle.com/c/nfl-health-and-safety-helmet-assignment/overview)*というコンペで**優勝**:trophy:したので調子に乗って、コンペの紹介とポエムを書かせていただくことにしました。

多種多様なアプローチがとれる楽しいコンペでしたので、そのお気持ちを少しでもお伝えできれば甚幸です:relaxed:

#自己紹介
- 機械学習/プログラム歴はもうすぐ3年
- 巷に溢れるKaggle (Notebook) Expert
- 画像系コンペが好み。pandas力はゴミ。
- パパ Kaggler。とてもかわいい息子と娘がいる。

[2年前にポエムを書いて以来](https://qiita.com/Kmat67916008/items/3fbead37b95e4c9506c2)、久々のkaggleコンペになりました。この2年「メダルは取れないんじゃなくて取っていないだけww」とほざき続けてきた中でのコンペ参戦です。


# コンペ紹介 [NFL Health & Safety - Helmet Assignment]

## コンペの趣旨
NFL(National Football League)は非常に激しいスポーツで怪我も多く、中でも**頭部の衝突による後遺症**が残ることがわかっているそうです。特にHelmet-to-Helmetの衝突に関しては『頭部を下げたままタックルしない』といったルール改正がおこなわれるほどに重要視されており、NFLでは**頭部衝突を監視・軽減する取り組み**を進めているそうです。

そのため、昨年Kaggleにて、『[NFLプレイ動画の中から衝突したヘルメットを検出するコンペ](https://www.kaggle.com/c/nfl-impact-detection)』が開催されました。今年開催されたコンペはこれに続くもので、**『プレイ動画から検出されたヘルメットがどのプレイヤのものなのか』を判定することが目的**となります。
<img src="https://www.si.com/.image/t_share/MTY4MDI4Njc2OTI4NjQ0NDgw/new-nfl-helmet-lowering-rule-explainerjpg.jpg" width="600">

## 扱うデータ
扱うデータは以下がメインです。

- **NFLのプレイ動画**: 
1回のプレイが約6~12秒(60fps)で学習用データは120本分、テストデータは30本分。

- **プレイヤの位置情報**: 
各プレイヤのショルダーパッドに位置情報をつかむセンサが仕込まれており、プレイヤIDとフィールド上の座標位置の関係が与えられる[^ref1]。動画の時間(フレーム)と連携できる。
[^ref1]: ショルダーパッドの位置であり、ヘルメットの位置ではないことも、本コンペ攻略の上での悩みの種の1つだった。

- **正解ラベル(学習データのみ)**: 
動画中の各フレーム毎にヘルメットを囲う矩形と対応するプレイヤIDが付与されている。

以上を用いて、**動画の各フレームのヘルメットを検出～検出したヘルメットにプレイヤIDを付与する**ことが本コンペの目的で、その精度を競うことになります。なお、精度算出の際には頭部衝突しているヘルメットのウェイトは衝突していないものの1000倍にされます。言い換えると、衝突ヘルメットを見逃すと、1000個分見落としたことと等価になってしまうという辛さがあります。

実際にはややリコールよりな評価指標ですが、詳細はコンペ概要をご参照ください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/b6abfa7b-26d6-e882-f294-6e8e15c653f4.png" width="768">

最終的にこういった出力ができるようになります。検出枠とプレイヤIDが紐づけられていることがわかります。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">必要になったので今更ながら…<br>最初はこれ難しかったけど終盤は稼ぎ頭になったやつ。重みによらず0.995はかなり優秀。 <a href="https://t.co/rS7zGqUOMd">pic.twitter.com/rS7zGqUOMd</a></p>&mdash; K_mat (@Kmat67916008) <a href="https://twitter.com/Kmat67916008/status/1459084481390792707?ref_src=twsrc%5Etfw">November 12, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## このコンペ、何が難しいのか？
これだけ聞くと結構簡単そうに見えますが、苦戦した参加者も多かったように思います。

単純に最終スコアを見てみても、全参加者825team中

- 銅圏:hatching_chick:(100位): 0.488
- 銀圏:hatched_chick:(50位) : 0.593
- 金圏:rooster:(11位) : 0.858

と、強烈に差は開いており、悪く言えば**解けなかった人が多いコンペ**となりました。
では何が難しかったのでしょうか？

参加されていない方は、是非『自分ならどのようにアプローチするか』を考えてみると楽しいかと思います。


**以下、(私が)難しいと思ったポイントをいくつか挙げていきます。**

####難しさ①: 衝突ヘルメットが案外見えにくい
検出タスクとしては比較的簡単な部類だと思いますが、見えにくいシーンがチラホラあります。
● 遠いヘルメットは**10ピクセル前後の小物**でかなり見えにくい。
● **衝突中のヘルメットはオーバーラップが増える(高IoU)**ので見落としがち。地味に死活問題。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/34950283-d55d-b715-47c5-11b2eb95942a.png" width="700">

####難しさ②: 関係ない人が割り込む
**プレイヤIDを付与するのは試合に参加しているプレイヤのみ**で、それ以外は無視しないといけません。ですが困ったことに、まわりには関係のない人たちがたくさんいます。特に**サイドラインプレイヤは普通にヘルメットをかぶっていることもあるので見分けがつきません**。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/5dee7e13-b2d7-f72f-1de8-9f6cbcf5ce95.png" width="400">

####難しさ③: 大混雑
東京メトロ並みに混雑するときがあるので、こうなると誰のヘルメットかわかったもんじゃありません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/a6131323-003f-3c18-89f7-f0fc0b3e5215.png" width="700">

####難しさ④: カメラ情報が無い
撮影条件として『サイドライン側』もしくは『エンドゾーン側』は与えられているが、それ以外は不明。**高所から見下ろしているシーンもあれば、ローアングルなことも**ありますし、ズームインすることもズームアウトすることもあります。以下の2つの画像は同じ試合の全くの同じシーンなのですが、かなり雰囲気が違います。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/e6638ac0-5144-675b-dad7-226e1ad503c9.png" width="700">

####難しさ⑤: 遠近感がわからない
マップ情報からプレイヤを割り当てたいところですが、遠近感に苦しめられます。**画像上で近接しているからといってプレイヤ同士が実際に近くにいるかというとそうでもありません**[^ref2]。
以下の画像、Aの方がBよりも近く見えませんか？
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/ac7dd4bf-3e71-d675-263d-ff6c9acdd696.png" width="400">
[^ref2]: しゃがんだり寝そべったりするプレイヤもいるので、遠近感が余計にカオスになる。

####難しさ⑥: ユニフォームやヘルメットの色で分類できない
出力すべきラベルはHome_No.32やAway_No.76といった形であり、チーム名ではありません。もちろん特定のチームがホームになることもあればアウェイになることもあります。したがって**単純なプレイヤー分類は難しく、ホーム/アウェイの判別も簡単ではありません**。赤いからホーム、青いからアウェイ、なんてシンプルなことはできません。
試合の組み合わせによってはヘルメットの色はほぼ同じなんていうケースもあります。[^ref3]
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/d49c9fcf-846a-c692-fc35-657ccf3ce504.png" width="700">
[^ref3]: アノテーションは『ヘルメット』だけで『人』ではないこともこのコンペを面白くした要因。普通に考えるとヘルメットだけではなくユニフォームの情報も欲しい。

#### 難しさ⑦: debugで死ぬ
という問題を解く上で比較的複雑なパイプラインを組むことになるので、うっかりサブミットエラーを踏んでしまうことがあるのですが、コードコンペなので全くエラー内容が返ってきません。私自身は初のコードコンペなので非常につらかったです。大量にサブする人を見ても温かい目で見守ってあげてください。

## ソリューション紹介
ではこれらの問題にどのように対処したのか？少しだけ紹介します。
本格的に気になる方はDiscussionを是非ご参考ください。
https://www.kaggle.com/c/nfl-health-and-safety-helmet-assignment/discussion


### 一般的なアプローチ
Notebookがあがったこともあり、多くの人が以下の3つを組み合わせていました。
**[1] ヘルメット検出器**
**[2] 画像上の検出結果とマップ上の位置関係の紐づけ(homography変換など)**
**[3] プレイヤの追跡(トラッキング)**

下図のようなイメージです。

ヘルメットを検出したのち、画像上のヘルメットとマップ上のプレイヤ配置を見比べて対応関係(homography変換)を解くことでプレイヤを割り当てます。さらに、トラッキングによってプレイヤを追跡することで割り当てが困難な条件(例えば、人で混みあった条件)でもプレイヤの割り当てを成功させることができます。

各フレームでマッチングさせてから統合する人もいれば、序盤のフレームでプレイヤを確定させてからトラッキングに注力する人もいました。トラッキングは、シンプルなIoU系から, SORT, DeepSORT, ByteTrackerなどが用いられていました。
なお、検出器は相変わらずYOLO5系が強かったようです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/23e6d492-1f30-07d0-c2c4-f289f8a38b36.png" width="600">


### 上位陣のアプローチ
ほとんどの人が上述のような検出器とトラッカーを用いていましたが、これに加えてさらにいくつか尖った工夫がみられました。

**[A] チーム判定のためのクラスタリングや分類**
**[B] ユニフォームの番号検出によるプレイヤ特定**
**[C] 画像上の座標からリアルの座標へと変換する工夫**
**[D] 点間マッチングの精度向上のための工夫**

前述[2]のプレイヤ割り当てはそのままでは中々精度が出ませんが、[A]や[B]を用いるとプレイヤ同士が近接しているときでも割り当てしやすくなります。また、画像上の距離と実際の距離の違いについても、画像情報を元にカメラの撮影条件を割り出したりすることで修正する手法が見られました[C]。

下図はほぼ私の解法になりますが、イメージとしてはこんな感じです。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/127e9e97-ee18-b360-e4ee-c4d55ce613ad.png" width="700">
皆さん色んな解法をシェアしているので、是非ディスカッションもご参照ください。

## 最終結果
ありがたく頂戴いたしました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557686/856ece76-190b-72c8-53c1-8b48b3e1d1c6.jpeg" width="700">
なぜか日本勢が強かったですね！titoさんやfantastic_hirarinさんと1~3位をいただきました。
中盤は**Osaka, Japan怪しい説をディスカッションでとなえられる**という盛り上がるシーンもありました。

## 振り返り会
その後、ふぁむたろうさん主催で振り返り会もおこないました。本コンペのhostであるGM Robさんもいらっしゃり、非常にレアな体験をさせてもらいました。

まとめ記事：
https://fam-taro.hatenablog.com/entry/2021/11/08/230247


----
さて、以下はポエムです。ごりごりの主観ですので我慢できる人だけどうぞ。

----
#なぜ勝ってしまったのか？？
敬虔なkagglerの中には私のようなexpertがぽっと出で優勝したことに不快感を持つ方もいるかもしれません。そんな皆さんに、**~~まぐれ~~ホームランの打ち方**を少しだけ記そうと思いました。



##kaggleホームランチケットの買い方 :tickets:
* 変化球を狙う
* kaggleをしないという選択肢
* 全打席ホームランねらい

###変化球を狙う (変わり種コンペを狙う) :baseball:
残念ながら、先駆者は圧倒的な知識量と分析能力を持っているのでそう易々と勝てません。常勝プレイヤは経験が豊富なため初手から他者を圧倒しますが、そこからさらに緻密な実験をおこない、コンペの核となるポイントをつかみ取ります。そんな人がマシンリソースも持っているとなると鬼に金棒です。

画像系ですと分類・検出・セグメンテーションあたりは非常に一般的ですね。ゴールドやプライズ取る人はかなり詳しい人も多いと思います。「何かしらの発見」次第で上位に入り込めるコンペならいいですが、そうでない場合は純粋な戦闘力勝負になるので、私なら死にます。主観ですが、お題がシンプルな程辛い戦いが待っています。

したがって、**アプローチが勝負を分けるコンペが理想**です。ワンチャンあります。
**他のkagglerもやったことが無いような類のコンペ**なら、スタートラインで圧倒的な差をつけられることもありません。良いアプローチさえ見えれば一発あてることも不可能ではありません。

###kaggleをしないという選択肢 :lifter:
**「変わり種」を狙うからにはkaggle以外からも知識や実装を学ぶ必要があります。**

たとえば今回のコンペで私は、NNで座標をワープさせる発想と実装に即座に取り組めましたが、これは[過去に実装した単眼デプス推定](https://qiita.com/Kmat67916008/items/267a3d3a6ac7c3512382)の筋トレがかなり効果的でした。また、モデル内に非線形の二乗誤差最小化問題(正規方程式のイタレーション)を仕込ませるというのも初めてのトライでしたが、なんなく実装することができました。これも日頃の筋トレの成果だと思います。

とはいえもちろんkaggleでの学びも最低限は要求されると思います。今回のコンペではWBFアンサンブルを応用して精度を向上しましたが、こういったテクニックはkaggleでしか学ぶ機会はありません。

ということで、**kaggleをやればやるほどkaggleに強くなりますが、kaggle以外もやることで想像力やアプローチに幅が出る**と思います。今回は動画データでしたが、動画でできることって(教師データが無くとも)ビビるほどたくさんあるんですよね。知っているか知らないかで大きな差がつくと思います。

近年のkaggleはシンプルな分類・回帰問題よりも、複合的でマルチモーダルなタスクが多いように思います。新しい分野にぜひ一緒に一歩踏み出していきましょう。

### 全打席ホームランねらい:cricket:
賛否両論あると思いますが、私はコンペに参加するときは比較的**あそびながらホームランをねらいます**。

NFL繋がりですと、以前、動画からヘルメット衝突の瞬間を検出・判定するコンペがありました。その際、上位勢はマルチフレームの入力を用いたり、オプティカルフローでフレーム間のズレを解消したりしていました。
実はこのコンペ、私もデータはダウンロードしていましたが、コンペ終了までサブミットにいたりませんでした。最後まで動画を用いた自己教師あり単眼デプス＆モーション推定を構築していたからです。『デプスとモーション情報が追加であれば絶対有利だし最高に面白い』と思っていましたが、打席にすら立てませんでした…。

ということで、当たり前ですが初っ端からホームランを狙えばあたればデカいです。三振覚悟ですが。

# 最後に
最後までお読みいただきありがとうございました。NFLコンペ楽しかったので興味あればぜひソリューションやデータをのぞいてみてください！

調子こいたことを書きましたが、私はコンスタントに上位に入れるレベルではありませんので、皆さん一緒にがんばってお勉強しましょう！


追伸：
Solo 1stはいいぞ？:relaxed: