💆‍♀️

【Kaggle】 Feedback3 コンペでボロ負けしたので教訓を全部吐きます【懺悔】

2022/12/08に公開

はじめに

この記事は、 Aidemy Advent Calendar 2022 の8日目の投稿です。

みなさま、いかがお過ごしでしょうか。株式会社アイデミーでデータサイエンティストとして勤務しております、ざっしーと申します。

先日11/29に閉幕したKaggleコンペティション Feedback Prize - English Language Learning (通称Feedback3)に参加してきました。
そこで見事にボロ負けしてきたので、この記事で教訓を1つ残らず落とします。🫠

打ち手の考え方などの概念的な話だったり、具体的な手法の話になったりと、話題のレイヤーが多少は雑多になるかもしれません。そのぶん大量の情報をお伝えできればと思っています。

対象読者の方々

こんな方に向けて書いています。

  • Feedback3コンペに参加しており、振り返りをしたい方
    • 良いモデルが組めた方には、あまり参考にならないかもしれませんが...。
  • Kaggleコンペに参加しようと思っており、イメージを掴みたい方
  • 最近のNLP手法に興味がある方

結果

お恥ずかしながら、まずは結果から。

  • 順位 : 1044位 ( / 2655 Teams)
  • スコア : 0.439590 (1位 0.433356)

LBではこちら。あまりイケてないチーム名で参加しているようです。

感想としては、めちゃくちゃ悔しいです。結果として最後1週間に詰め込む形にはなってしまいましたが、なんだかんだ5日間くらい半徹で打ち込みました。そのため私の中では、過去のコンペの中でも1,2番目に時間を費やしたコンペだと思います。にも関わらず、これまでのコンペで最悪レベルの結果に終わってしまいました。

ちなみにチームは5名で参加しましたが、(暇な僕とは違い)多忙なメンバーばかり集めてしまって、手を動かせたのはほぼ僕だけでした。とはいえリサーチや、議論の壁打ちに付き合ってくれたので、本当はみんなにメダルをプレゼントしたかったです...。

まぁ、泣き言は反省のタイミングで追々話すとして、コンペの内容からおさらいしていきましょう。

コンペの内容

端的に言うと、英語学習者(8年生から12年生、8年生で13-14歳くらいらしい)の小論文の自動採点を行うコンペです。
採点項目は全部で6つあり、人間による採点結果を正解ラベルとして、なるべく人間の採点を模倣できるモデルの構築を目指します。

採点基準は以下の6つです。各小論文に、以下6つのスコアが1〜5点で、0.5点刻みで付けられています。

  • cohesion : 文章内の主張が一貫しているか
  • syntax : 文章として、態に則った語順で文を構成できているか
  • vocabulary : 語彙力の豊富さ、語彙の使用の適切さ
  • phraseology : 言い回しの豊富さ、適切さ
  • grammar : 文法規則による単語の活用に誤りがないか
  • conventions : 慣習? 大文字アルファベットの使い方や、句読点の位置が適切か
    • ちょっとこれだけあまり自信がないです...。

最終的なスコアは、採点基準ごとに全ての小論文の正解データと予測値のRMSEを計算し、計算された6つのRMSEの値の平均値で計算されます。「MCRMSE」なんて名前が付いてるみたいですね。

難しい点としては、vocabulary, phraseologyのような、単語やフレーズ単位で抽出される情報で採点される項目と、cohesionやconventionsのような、複数の文を見渡した上で採点される項目が存在することです。というか、端的に言うと前者は割と予測が簡単で、後者が割と難しいです。

弊チームのソリューションの概要

以下の通り、ソリューションは比較的シンプルです。

  • 学習済み DeBERTaV3 モデルの、 BaseLarge を Fine Tuning
  • Pseudo Labeling
  • 2つのモデルの重み付け和

そもそもこのコンペティションでは、Y.Nakamaさんの神ベースラインを始めとする、DeBERTaV3のベースラインモデルたちが最初から高い精度で君臨していたため、スコアも解法も差がつきにくいのが実情でした。

ちなみに弊チームでは↑のモデルに追加で、RAPIDS SVR で各目的変数を回帰するスタッキングサブミッションも検討していました。しかし、最終日に実装にバグが有ったことが見つかり、地獄のようなスコアになったため断念しています。そこでは以下のメタ特徴量を追加する想定でした。

  • Readability Score (各文章について、1文の長さや単語の平均文字数などから、「読みやすさレベル」を数値的に出したもの)
  • 5つの DeBERTa Family 学習済みモデルにより抽出された特徴量
  • BM25 + Truncated SVD + LightGBM の予測値

コンペ全体を通して、DeBERTaV3が文章からの特徴抽出機としてはもうあまりに優秀でした。以下の2つのmemeを見ていただけると、今回のコンペ開催中のLBの様子がおわかりいただけるかと思います。

altテキスト
altテキスト

Meme Threadなんて、ナイスなDiscussionも盛り上がってました。

なぜ負けたのか

というわけで、この記事で1番の本質部分と言える、「なぜ負けたのか」の考察です。

個人的には、以下の3点が直接的な原因と考えています。

  1. 3つのサブミッションにバリエーションを持たせておらず、「運試し戦法」になっていたから
  2. Pseudo Labeling の実装にリークがあった
  3. そもそもシングルモデルの精度向上に有効な手法を見つけた数が少ない

大事な部分だと思うので、1つずつ見ていきましょう。

1. 3つのサブミッションにバリエーションを持たせておらず、「運試し戦法」になっていた

これは要するに、「サブミッションは3つまで選択できるのに、全てのサブミッションで、同じようなモデルの出力を使っていた。」という状態です。これの何が問題かと言うと、Private LBの評価用データとモデルの相性が良いかどうかの、「運試し戦法」となってしまっている点です。宝くじです。恋みくじです。

今回は、Public, Private 共にLB評価用のデータがそう多くありませんでした。そのためコンペ終了前から、Private LBの評価用データに偏りがあるだろうとは、事前にある程度予期されていました。
そのような状況下で常に上位を取り続けているKagglerは、選択する3つのサブミッションの解法に多くの差分をもたせます。これにより、Private LB用のデータの偏りに、最大限対応できるようにしているわけです。

対して僕はと言うと、後述の通りPseudo Labelingという手法を試したらCVスコアがグンと上がりまして、
「おお!上がった上がった!時間もないし、Pseudo Labelingでモデルいくつか訓練して、スコア良かった奴らのアンサンブルで出したるか。😚」
みたいな、無分別も甚だしいお気持ちでサブミッションを重ね、あろうことかそれを最終サブミットに選択してしまったわけです。

とくに、 Pseudo Labeling のような一般的に「リスクが高い」と言われる手法は、使うver, 使わないverでソリューションを分けるのが一般的らしいです。この辺りも少し考えればわかった話ではありますが、エイヤで選んでしまった思慮の浅さが出ていますね...。

結果として、後述のリークを踏んでしまった点と、そもそもPrivate LBの評価用データにPseudo Labelingがあまり良く効かなかった点の、ダブルパンチを喰らっています。 訂正 : Private LBの評価用データに、Pseudo Labelingはバッチバチに効いているらしいです。効いてない場合、やはり実装に穴があった可能性が高いです。

2. Pseudo Labeling の実装にリークがあった

こちらはシンプルに実装上のヘマです。 Pseudo Labeling という手法を使う際、その実装にミスがあり、リークが発生していたようです。

そもそも Pseudo Labeling とは、「コンペティションのデータとドメインは近いが、ラベリングが行われていないデータ」に対し、学習済みモデルを使ってラベルを付けてしまって(疑似ラベル)、訓練用データに混ぜて一緒に学習を行ってしまう手法です。
「そんなことしてええんか...?」とパッと見は思いますよね、僕も思います。しかし、それなりの確度でCV・LB共に良い結果が出る手法なのです。ラベルの値はそこまで信用ならずとも、「同じドメイン(今回は小論文)のデータを大量にモデルに食わせられる!」という点が重要といえます。

しかしながら、疑似ラベルを付ける際には、 「そのデータと同じ検証セットに割り振られるデータは省いて訓練したモデルによって、ラベリングを行う必要がある」 という落とし穴があります。CVを行うときにデータを複数の訓練・検証データのセットに分けるのですが、それぞれのセットごとに疑似ラベルのデータを作成する必要があるのです。でないと、本来見えてはいけない情報を使ってラベリングされたデータを訓練に使用してしまうことになります。結果として、CV値が本来のモデルの性能よりも高く出てしまうわけですね。

これは完全に盲点で、私の実装ではキレイに踏み抜いています。3つのサブミッションとも、この罠にハマっています。
とはいえ、どれほど精度に悪影響が出ているかはまだよくわかっていないので、後ほど正しいパイプラインを作って Pseudo Labeling をもう一度実行してみて、精度がどう変わったかの結果を見てみたいと思います。

また、Public LBのスコアを軽視しすぎた点も、大きな過ちだったかもしれません。私の考えとしては、「Public LBは評価用のデータが少ないから当てにならない! Trust CV!」という感じでしたが、少々カルト的だったようです。何事もバランスが大事ですね。
最終スコアを見るに、今回のコンペの Private LB のデータは、手元の訓練データよりも、Public LB のデータと性質が近かった感じもします。

3. そもそもシングルモデルの精度向上に有効な手法を見つけた数が少ない

結局のところ、最終的にはこの点が最大の敗因だったとは思います。そもそも試そうとした手法にセンスが無かったので、宜なるかな、結果にも繋がりませんでした。

たとえば、DeBERTaV3モデルの出力結果をどう集約して Regression Head に渡すかの「Pooling」が、今回のコンペでは1つ工夫のポイントでした。僕はそこでオリジナル手法として、各文の文法構造を集約したトークンを用意できないかと考えました。
具体的には、各文の末尾(ピリオド等の直後)に [SentencePool] みたいなトークンを挿入し、文ごとの grammar や syntax の特徴をそこに集約しようとしました。で、それを新しく定義した Regression Head に渡そうとしたわけですね。
BERTでは、文章全体の特徴は CLS トークンに集約され、各トークンレベルの特徴は対応するトークンの出力を見るのが定石です。「じゃあ文単位の特徴って集約されてなくない!? ウォウ ウォウ⤴ 」と思ったわけです。

ただ、新たに追加した [SentencePool] のトークンに、いかにして文単位の情報を集約するか、いろいろ試したり調べたりしましたが、全くわかりませんでした。
そもそも、誰でも思いつきそうなアイデアではあります。本当に有効なら既にコンペのディスカッションで上がりまくっていたり、論文の1つや2つは見たことがあってもおかしくないはずです。なので、仮にうまく実装できても効かなかった可能性も高いと思います。

結局この取り組みは、論文などの裏付けも見つけられないまま1日以上の試行錯誤にハマってしまい、これ以上時間を割くわけには行かないと判断して断念しました。

反省点としてはやはり、

  • BERTに対する理解が不足していて、自分のアイデアに希望があるかどうかの判断もつかなかった点
  • そもそもそんなアイデアに貴重な時間を割きすぎちゃった点

の2点に集約されるでしょう。
BERTから意味情報の集約はできないくせに、自分の反省点の集約は上手にできましたねガハハ

こんな感じで、このPooling問題以外にも複数箇所で詰まってしまい、効率よく進めることが叶いませんでした。
そもそも時間が無い中でメダルを取ろうと思ったら、こういう成果を生まない時間を極力減らす必要があるのは明白なんですけどね。一度スタックしたら徹底的に調べようとしちゃう性格上の問題が大きいと思います。もっとアヒルさんとお話し続けて、常にメタ視点を持ち続けないといけませんね。

次はどうやってメダルを取るか

以上の反省を踏まえて、「じゃあワイがメダルを取るにはどうしたらええんや...」を考えました。
思うところは色々あるものの、まずは絶対的な「時間と知識量」を補う必要があるのは明白です。そのため指針としては立てやすいです。

  1. 3ヶ月間参加する
  2. リサーチ量1位(当社比)を取るつもりで、Discussionや、関連する過去コンペソリューションなどを読み漁る

1. 3ヶ月間参加する

そもそも僕の実力的に、1週間の参加では銅メダルすらも取れるに見合った力は無いことを痛感しました。せめて3ヶ月間忍耐するところから始めようと思っています。
ここまでは、「参加期間は関係ねぇ!戦略だ!質だ!」という勢いで書いては来たものの、冷静に考えれば1週間徹夜で頑張ったとしても、相当の実力差がない限り、その12倍もの期間で試行を重ねられている参加者にはまず勝ちようがないでしょう。ましてや実力も劣っている状態です。3ヶ月ストイックに参加できて銅メダルが妥当なはずです。

これまでも、3日とか1週間のコンペではそれなりに良い結果を出したことがあったりはします。ですが、Kaggleのような長期間のコンペティションではそもそもメンタルが持たずに途中で投げ出してしまいがちで、3ヶ月間参加し続けられた試しがありません。
普通に今後生きていく上でも、この忍耐力のなさは改善していきたいところです。

というわけでまずは3ヶ月間、コンスタントにサブミッションを重ねてメダルを目指そうと思います。

2. リサーチ量1位(当社比)を取るつもりで、Discussionや、関連する過去コンペソリューションなどを読み漁る

この点に関しては、「試行のセンスを補うために先人に学びまくろう!」という至極当然の論理のもと、書いています。前述の通り、知識も経験も微妙な状態なので、いざコンペ特有のソリューションを考えようとデータを眺めても、実現可能性が薄かったり、実現してもインパクトがなかったりと、センスの悪いアイデアしか浮かびません。

もちろんそれだけで、コンペの経験が多い方々と互角に立ち回れるとは思ってはいません。ですが、もう少しマシな結果にはなるのかなと思います。長期的に見て、自身のスキルの糧にもなるとは思いますので。

余談ですが、既存解法を寄せ集めてアンサンブルするだけで、メダルが取れちゃうこともあるみたいですね。もちろん戦略としては違反でもなんでも無いのですが、データサイエンティストとしてのスキルアップを第一の目標に掲げている僕としては、時間がかかってもいいので納得できるモデル構築を行いたいと思っています。

得たスキル

とはいえ、今回のコンペでは泣き言ばかりではなく、得たスキルもたくさんあります。参加期間は短かったですが、がんこちゃんなので、自分でパイプラインを組んで何度も実験を回す経験は積めました。

  • PyTorch Lightning の習得 & パイプライン構築
  • BERTモデルからの特徴抽出やFineTuningを、短いスパンで何度も試行できるようになった
  • スタッキングを初めてコンペで組めた
  • Pseudo Labeling を初めてコンペで使えた
    • 落とし穴も理解した
  • Nvidia RAPIDS SVR を初めて使った
    • ものすごく速いサポートベクター回帰
    • 画像でも文章でも、学習済みの特徴抽出器があれば解けちゃう印象で、すごく便利
  • GPU2枚刺しマシンで学習を回し続ける術を身に着けた
    • 会社に感謝 卍 電気代陳謝 😎
  • Kaggle APIに習熟した

試したことリスト

また雑多にはなりますが、試したことリストを以下に列挙します。今見ると、モデルの学習率とか構造とかいじってるばかりで、本質的なデータパイプラインに全然手をつけられていませんね。

  • Pooling Head の置き換え
    • MeanPooling ← 採用!
    • CLS Pooling
    • CLS + MeanPooling
    • ConcatnatePooling
    • WeightedLayerPooling
    • SentencePooling (上述のオリジナル手法
  • Regression Head の置き換え
    • 文章全体を見て判断する評価指標 coherence, convention を、BERTの一番はじめの input である [CLS] トークンの出力から推論するようにしてみた
      • 効かなかった
  • Regression Head の重み初期化戦略(全部チームメイトのbassyさんの成果!神!)
    • Xavier Uniform
    • Xavier Normal
    • Kaiming Uniform
    • Kaiming Normal
    • Orthogonal ←採用!bassyさんありがとう…
  • 層ごとの学習率に差分をもたせる Layerwise Learning Rate Decay ← 採用!
    • BERTの前半層は * 0.05
    • BERTの後半層は * 0.2
    • Pooling, Regression Head 層は * 1.0
  • 学習率スケジューリング
    • Cosine ← 採用!
    • Linear
  • 敵対的学習手法
    • AWP (Adversarial Weight Perturbation) で汎化能力を上げよう作戦
      • 効かなかった
  • 重みの平均化
    • SWA (Stochastic Weight Averaging) で汎化能力をあげよう作戦
      • 効かなかった
  • MultiNLIでFT済みのDeBERTaV2モデルの試行
    • 効かなかった
    • 文章全体の特徴抽出の質の改善を期待していました

小手先とはいえ、パイプライン構築から色々試しはしたので、コーディングに費やす時間はそれなりでした。
コンペ終了時点の体調はかなり残念でした。

「眠気の向こう側に行ってしまった男性が半笑いで朝を迎えたイラストです。」

次に参加するコンペと目標

次は、RSNAコンペに参加してこようと思います!かなり強いKagglerが初っ端からLBに集まってる印象で、この中で戦い抜けたらかなりの成長になるかなと。

概要としては、マンモグラフィのX線データから、乳がんに罹患しているか否かを判定するコンペです。ちなみにFinal Submission Deadlineは 2023/2/27 なので、今から出れば3ヶ月近く出れるという算段です。最高!

個人としては銅メダル以上で、今週末から毎日1サブミットを継続していきたいと思います。

別のアドベントカレンダーで、このFeedback3コンペのWinning Solutionsの詰め合わせモデルを作ると宣言してしまっているので、それが終わるまで本気サブミッションは出来なくなってしまうとは思うのですが、毎日情報にキャッチアップして、サブミットし続けるところから始めようと思っています。

Kaggleプロフィールのこれを真っ青にしたいです。

ついでにGitHubも真緑にしたいです。

終わりに

以上です。いかがだったでしょうか。半分ポエムみたいな書きぶりになってしまい申し訳ございませんでした。
個人的には、今回のコンペで大変悔しい経験ができたので、まずはこのKaggle熱を冷まさないように、コンスタントにコンペに参加していくとともに、一方でもう少し冷静に戦略を考える時間を大切にしていきたいと思います。

Feedbackコンペの詳細な解法について知りたい方は、Kaggleアドベントカレンダーで「Feedback3 の Winning Solution 全部入れモデル作ってみた」という記事を投稿する予定なので、そちらも楽しみにお待ちいただけますと幸いです。

RSNAコンペのLBでお会いしたときは、お手合わせどうぞよろしくお願いいたします。
あと、弊社はデータサイエンティストを絶賛採用中なので、ご興味あればお気軽にご連絡ください!

おわり。

Aidemy Tech Blog

Discussion