クラウドファンディング資金調達の成否予測
SIGNATE主催の22卒インターン生を選考する機械学習コンペティションに参加し、銅メダルを獲得しました。
※コンペの情報公開ポリシーに則った範囲でのレビューになります
Notebook (github): https://github.com/Hayato009/Signate_CrowdFunding
背景
機械学習コンペティションプラットフォームを提供するSIGNATEがデータサイエンスグループの22卒メンバーを募集する目的で、インターン生を選考する機械学習コンペティションを開催しました。コンペ上位入賞者にインターン参加権利が与えられます。
課題内容は「クラウドファンディングでの資金調達の成否」の予測、つまり二値分類問題です。特徴量としてはそれぞれのクラファンプロジェクトの調達目標金額やアピール文章が成形済みテーブルデータとして与えられました。
今回私は1月18日~2月3日の約2週間、本コンペティションの予測課題に取り組みました。機械学習コンペティションの経験はKaggleのTitanicやSIGNATEの住宅価格予測などチュートリアル的課題のみだったので、本格的なコンペ参加は初となります。本コンペを通じ、機械学習の学びを得ることと入賞報酬のインターン参加を目指しました。
詳細
1/18 参加初日 (score 0.7142)
まず、ベースラインとなるモデル作成から始めました。当初テキストデータの取り扱い方に見当もつかなかったので、ひとまずカラムごと処理対象外にしてしまい、テキストデータ以外の特徴量のみ使用することにしました。
目的変数の分布に偏りがなく欠損値も存在しない非常に整ったデータだったので、前処理はカテゴリ変数のエンコーディングのみ行い、XGBoostモデルに学習させました。このモデルの暫定スコアはmacro-F1で0.7142でした。
当時1位の人のスコアが0.82ほどあり、超えられるビジョンが見えませんでした。そこで他の参加者の意見を参考にしようとフォーラムを覗くと、LightGBMを用いてスコア0.7994を出したソースコードが共有されていて、これを参考にさせて頂くことにしました。
1/19~1/27 共有ソースコード理解
上記のソースコードには以下の内容が含まれていました。
- 特徴量の表記揺れの統一
- 特徴量を意味ある形に変換
- 数値変数をビニング処理することにより、意味を持つカテゴリ変数に変換
- 特徴量同士の四則演算
- 特徴量集約
- テキストデータのBug-of-wordsによる基本特徴量の作成
- Text vectorの作成
- 特徴量重要度の活用
これらはすべて自分が試したことのない手法だったので尻込みしましたが、スコア0.8のために!と思い、一行一行のコード理解を試みました。一行ずつ入力しなおして実行し、分からない部分は別のデータを使って実験したり、検索してみるなどして進めました。コンペ開始から1週間ほど経った27日にはソースコード全体の理解が得られました。
1/28 予測結果提出、特徴量数実験 (score 0.7996, 0.8124)
集約特徴量や特徴量同士の四則演算による新たな特徴量を大量に作成したため、最終的な特徴量数は1600を超えました。この中には完全に無駄な特徴量も含まれるため、LightGBMの特徴量重要度の高い順に50個選んだものをモデルに学習させ、予測と結果提出まで行いました。その結果スコアが0.7996と、当初自分が作ったモデルから大幅な改善が見られました。
今回は特徴量重要度を扱うのが初めてだったので、どの程度の特徴量数が適切なのかが分かりませんでした。上位50個以外にも有用な特徴量が含まれている可能性もあったので、特徴量100個、200個、300個、etc. と特徴量数を増やしてモデルを学習すると交差検証によるスコアにどのような変化が起こるのかを観察しました。その結果、特徴量200~250個あたりで0.817程と交差検証スコアが最大になり、より増やすと徐々にスコアが下がることが分かりました。改めて特徴量200個で学習したモデルの予測結果を提出すると、F1-score 0.8124を獲得しました。
1/29~1/31 Stacked Generalization (score 0.8095)
以前、以下のサイトで学んだStacked Generalizationを試しました。
今回は特徴量50個、100個、200個、300個、500個で学習した5個のベースモデルをLogistic Regressionを用いたメタモデルによってスタッキングしました。交差検証スコアが0.810程度と以前より低いと分かった時点で不審に思っていて、実際に提出してみても0.8095と奮いませんでした。この原因を「相関が高い結果を出力するモデル同士を組み合わせたために上手くいかなかった」と考えました。
後になって分かったことですが、コンペ終了後に表示される最終スコアでは0.8198と自分の作ったモデルの中で最高スコアをたたき出していました。もし提出モデルとしてこのモデルを選んでいたら銀メダル圏内に潜り込んでいたはずだったので、非常に悔しい思いをしました。スタッキングしたモデルの汎化性能を信じることにするとともに、今まで行っていた交差検証より正確に汎化性能を評価できる方法を学ぼうと思いました。
1/31 テキスト関連特徴量の追加(score: 0.8174)
テキストから得られる特徴量の重要度が高いと分かっていたので、これをメインにした集約特徴量および他の特徴量との四則演算によって得る新しい特徴量を作りました。
結果として交差検証スコアで0.8195を出しその有用性を確認したのですが、特徴量重要度を見ると" (プロジェクト期間 × 到達目標金額) / テキスト内句読点数 " に類するもの(高い相関がありそう)が非常に多く、これらを重要と判断するあまり、もともと重要と判断されていた多くの特徴量が十分に学習できなくなってしまったのではないかと危惧しました。
そこで、新しく作った特徴量群のうち" (プロジェクト期間 × 到達目標金額) / テキスト内句読点数 " だけ残しました。結果として交差検証スコアは0.8190とほぼ変わらなかったのですが、扱うべき特徴量数が減り、特に問題も見受けられなかったため、これでよしとしました。
提出したもののスコアは0.8174だったので、実際に改善出来ているようでした。
2/1~2/2 Target Encoding
『Kaggleで勝つデータ分析の技術』に記載されているTarget Encodingを試してみることにしました。しかし全くうまくいかず、スコアは0.8を下回る始末。LeakしてしまっているのだろうとTarget Encodingをあきらめ、元の状態にコードを戻しました。
ところが元のコードのはずなのにスコアが0.8を切ったままでした。この原因解明にかなり時間を使ってしまったのですが、原因は訓練データとテストデータの処理方法を間違えていたことでした。以下、その詳細になります。
元々は訓練用の特徴量群とテスト用の特徴量群をひとまとめにしてから前処理を行い、後で訓練用とテスト用に分割しなおすという手順を取っていました。それを訓練用・テスト用別々に前処理を行うコードに書き直したのですが、前処理用関数のfit, transformまでを訓練用・テスト用別々に行ってしまっていました。このため、カテゴリ変数に使ったLabel Encodingにおいて訓練データ・テストデータでの対応がついていなかったのだと思います。
本来このようなミスがあればすぐ気づくはずだったのですが、Target Encodingと訓練データ・テストデータの処理変更を同時に行ってしまい、以前のデータも残していなかったために時間を食ってしまいました。これ以降は途中経過をGithubに上げ、一度に一部分のコードしか操作しないことを癖付けようと思いました。
2/3 最終日
残り時間で改善できる部分が思いつかなかったので、最終日はソースコードでおかしな部分がないかの確認だけ行いました。1/31のモデルによる予測を最終投稿用としました。
結論
コンペ結果
元々12位だったのがかなりのshake downを受け、最終順位は36位で銅メダルを獲得しました。投稿した予測のうち、予想外に1/28の特徴量重要度上位100個によるモデルや1/31のStacked Generalizationを用いたモデルが高精度を出すという、汎化誤差に対する理解不足を痛感させられる結果に終わりました。
学び
初めて本格的に機械学習コンペをやり通しましたが、機械学習の手法的な学びはもちろん、プログラミングを進めるうえで身に刻むべき反省点もたくさん見つかる経験でした。
今回の一番大きな反省点は時間の見積もりが甘かったことです。コンペが始まってから3週間ほど経過した時点で参加したこともあり、まだまだCNNモデル作成やスタッキングの再トライなど出来ることを残した状態で時間切れとなってしまいました。コンペ開催から一週間以内に参加することで、より自分の力を出し切れるのではないかと思います。
今後もコンペに参加し、機械学習の理解を深めつつ腕を磨いていこうと思います。