NFL 1st and Future - Player Contact Detection 振り返り
はじめましての人ははじめまして。普段sqrt4kaidoという名前でkaggleのコンペなどに参加しています。本記事では、先日まで行われていたNFLで5位に入ることができましたので、その振り返りを行いたいと思います。
概要
本コンペは、The National Football League (ナショナル・フットボール・リーグ、以下:NFL)の試合中におけるプレーヤーの外部接触を検出するタスクでした。タスクの実施にあたっては、試合の動画データとトラッキングデータなどのテーブルデータが与えられました。提出はコードコンペティションの形式で行われました。
データ
動画は、全体が映ったものと、ゴール側(EndZone)、サイド側(SideLine)から撮られた3種類が提供されました。EndZoneとSideLineの動画は同期が取られており、後述するテーブルデータとも簡単にマージすることができます。それぞれの動画は、1つのplayが始まってから終わるまでの約12~15秒間で区切られていました。
テーブルデータは、以下の4種類が与えられました。
- trackingデータ
- helmetデータ
- labelデータ
- 動画のメタデータ
trackingデータは、各選手につけられたセンサーから取得されたデータで、ポジションや速度、加速度といった情報が含まれています。helmetデータは、昨年のソリューションから出力された各動画のヘルメットの座標です。このデータがあるおかげで、私のようにあまりコンピュータビジョンに造詣が深くない人も参入しやすい動画コンペだったと思います。labelデータは対象の動画の秒数においてplayer1とplayer2、もしくはplayer1とG(地面)にcontact(接触)があったかを表すデータです。
trainには149のgameにおける233のplay動画が提供されました。ラベルデータは4721618行存在します。 test時は61のplay動画に対して推論を行います。
ベースライン
多くの人が参照したであろうベースラインについて紹介します。
NFL 2.5D CNN Baseline [Inference] | Kaggle
このベースラインでは、各ラベルに対して
- 前後数frameの画像
- trackingデータ
をそれぞれCNNのバックボーンとmlpに通した後concatして全結合層に通すモデルです。各frameの画像は、helmetのbboxを用いて切り出されます。 ラベルを全て使うと途方も無い時間がかかってしまうため、事前に計算された各ラベルのplayer同士の距離が2ヤード以上のラベルは最初から衝突無しと判断し、学習/予測対象に含まれないようになっています。
しかしそれでもこのベースラインは学習に1epochあたり1時間程度かかりました。したがって、精度向上を行うとともに効率的な学習方法を模索するのもこのコンペの肝の一つでした。
弊チームソリューション
チームのprivate5位ソリューションのポイントを紹介します。詳細な解法はkaggleのdiscussionに上がっています。
1st and Future - Player Contact Detection | Kaggle
私たちのチームのソリューションは、2stageで構成されています。
- stage1 : 画像を中心としたCNN(+LSTM)モデル
- stage2: stage1の予測値を利用した決定木モデル
それぞれについて説明します。
stage1
stage1は、動画から抽出した画像と、一部のtrackingデータを利用します。以下にモデルの全体像を示します。
一番重要な部分は、outputの形状部分です。ベースラインではラベルの一行あたりに注目するため一回の学習の出力が(B, 1)ですが、 私たちのモデルでは(B, N)で出力します。Nとはシーケンスの長さです。次にシーケンスについて説明します。ラベルを衝突対象playerの組み合わせごとに時系列で並び替えると、各組み合わせごとに以下のような一連の時系列として現れます。シーケンスはこの一連のラベルから32などの固定長で切り出します。
このモデルでは、入力で作成したシーケンス長を一気に学習することができるため学習/推論にかかる時間を大幅に短縮できます。モデルやシーケンス長次第ですが、1epoch約10~15分で完了していました。また、このシーケンスごとに学習する仕組みのせいか、TSMやLSTMといった時系列特徴を学習できる仕組みが精度向上に寄与していました。
モデルへのinputは((B x N) x 3 x 128 x 128)の形状となっており、チャンネル数の3は以下の画像のように対象フレームの前後と、対象フレームのヘルメットbboxから作成されます。
stage2
stage2はstage1のCNN予測値を中心に特徴量を抽出します。上記シーケンスや、playごとなどさまざまな集約特徴量を作成しました。しかしCNNの予測値のモデルへの寄与がとても大きかったこともあり、作った特徴量は効かないものがほとんどでした。
他の工夫点として、 stage2のモデルではCNNと同じtrain/validの切り方を行います。したがって、stage1が5foldの場合stage2も5つモデルができます。 また、player1と2は無向なので、player1と2を逆にした特徴量を作成しデータ量を二倍にしました。最終的な予測は、平均を取ることで算出されます。 モデルはlgbmとxgboostを用いました。 特徴量抽出は全てpolarsで行っています。
推論
推論時には、さまざまなシーケンス長さで学習したモデルを使用することで、サブの多様性が増すように工夫しました。また、シーケンスによってはストライド幅を短くし、重複して推論させることで精度が大幅に向上しました。最終的には、oofで最適化された以下のstage2モデルを用いました。
予測値を0/1に直す閾値は最適化の際に求められたパーセンタイルによって決定されます。
その他高速化のtipsを以下に示します。
- PyTurboJPEGを利用することで、1.5倍ほど画像読み込み時間を短縮しました。opencvと様々なライブラリを比較しましたが、PyTurboJPEGが唯一画像読み込みを高速化できました。numpyに変換する方法も試しましたが、容量を多く使うので不採用となりました。
- 推論時は画像読み込み関数にlru_cacheを用いることでキャッシュを効率的に利用できました。
- ffmpegの画像抽出の画質は、多少下げても精度にはほとんど影響しませんでした。(具体的には、2から10まで下げました。)これにより画像抽出時間と、画像読み込み時間を高速化することができました。
上位解法紹介
利用できるデータの種類が豊富だったため、さまざまなソリューションが上がっていました。その中から2つ簡単に紹介します。
1st
1st and Future - Player Contact Detection | Kaggle
1位は、
- xgboostで簡単なネガティブサンプルを除去
- CNN
- xgboost
というパイプラインでした。1でネガティブサンプルを除去することで2以降のデータ量を減らし、実験を効率化しています。この流れで解いているチームは結構多かったように思います。 また特徴的な点として、trackingデータを画像化してCNNへinputしています。
3rd
1st and Future - Player Contact Detection | Kaggle
3位の解法は打って変わって1段階のアプローチになります。モデルが非常に複雑ですが、outputに着目します。 具体的には、対象のプレーヤーから近い人7人において接触があったかどうかを一気に出力するモデルになっています。 ベースラインがある特定の時間、空間における学習/推論だとすれば、私たちのソリューションは時間的に幅を広げたモデルになっていて、3位の方はさらに空間的にも幅を広げているような印象を受けます。 目を見張るようなモデルの全体図がありますので、ぜひリンク先をご覧ください。
また他には、提供されたヘルメットbboxの修正を行う工夫もありました。
感想、進め方など
今回動画データを扱うのが初めてでしたが、ヘルメットのassignmentデータが提供されていたおかげで戦うことができました。逆に言うと、この精度の高いヘルメットのassignmentデータを自ら作り出す力がまだ無いため、computer visionに関してはまだまだ素人だと感じています…
shakeについて
本コンペではテーブルデータを中心にソリューションを組み立てたチームが少しprivateで順位を落とし、逆にCNNを中心としたチームがshake upしました。
これについては上述したシーケンス(固定長に切り出す前)が関係しているのではないかと考えています。trainデータの最短シーケンス長は37でしたが、どうやらtestでは32より短いものがあったようです。
テーブルデータではこの長さが集約特徴量などへ影響しているのではと推察します。
進め方について
このコンペでは開始早々にCNNの予測値をlgbmに入れる2stage制にしようと決めて進めていました。
コンペ中盤でチームマージした際、チームメンバーはCNN単体で高いスコアを出していたので、そこに私の2stage制のアイデアを取り入れました。
しかし終わってみればチームのCNNがとても強く、ほとんどのゲインがstage1にあったと思います。stage2のゲインはモデルやシーケンスによりますが、スコアにして0~0.005程度です。キャリーしていただいたと言っても過言ではないと思うので、引き続きまた金メダルを取れるよう頑張ろうと思います。