2nd Place Solution by takedarts
はじめに
最初に、有意義で楽しい機会を提供してくださいましたProbSpaceの運営の方々に感謝申し上げます。また、参加者の皆さん、お疲れさまでした。
私はデータ分析や特徴量生成が苦手でして、今までのテーブルコンペではあまりよい結果を得られませんでした。しかし、先日「Kaggleで勝つデータ分析の技術」という本を読んでいたときに、NNを使って新しい特徴量を自動生成するアイデアを思いつきまして、これを試してみたところ予想以上によい結果となりました。最後はsenkin13さんとの接戦となりまして、私としては楽しいコンペとなりました。
詳しい解法は後述しますが、私の解法のコンセプトは次の通りです。
- データを分析しない
- 特徴量を手動で作成しない(「入社年齢」を作りましたが効果は不明)
- 面倒な調整はコンピュータにやらせる
一応EDAは実施しまして、hirokiさんが報告されているようなデータの特徴は把握していました。
この特徴を反映させたpipelineを作れれば、もう少し改善できたかもしれません。
解法
解法の大まかな流れは下図の通りです。
モデル作成
通常のテーブルコンペでは、複数の特徴量に対して加算・乗算などの計算処理を行うことで新しい特徴量を作ります。そこで、NNにこれらの計算を行わせることにより、特徴量生成を自動化しようと考えました。上図の「Encoder」が特徴量生成のためのNNにあたります。Encoderは3個のFull Connection Layerで構成されていて、3個の入力値から1個の出力値を計算します。ただし、入力値にカテゴリ変数が含まれている場合は、その対応のためにEmbedding Layerを使います。Encoderの出力はスカラー値となっていますが、これは次元数を減らすことでより有用な特徴量を抽出するためです(AutoEncoderの特徴量抽出と同じ考え方です)。
1個のモデルに16個のEncoderを配置しました。それぞれのEncoderは3個の入力値をとりますので、1個のモデルが入力値としてとりうる値の組み合わせは膨大な数になります($C^{220}_{16}$通りになります)。そこで、進化的アルゴリズムを使って良さそうな入力値の組み合わせを探しました。1920個のNNモデルを作成し、その中でもっともMAEが低かった96個を第1世代モデルとして採用しました。このモデル作成を5-foldのそれぞれで行いました(合計480個のNNモデルを作成)。
学習
第1世代モデルと同じ構造の第2世代モデルを作成し、このモデルの学習を実施しました。このとき、第1世代モデルの出力と学習データの平均値を教師データとすることで、蒸留と呼ばれることを行っています。以降同様に、第N世代モデルの出力値を使って第N+1世代モデルを作成していき、第6世代モデルまで作成しました。
アンサンブル
当初は480個のモデルの出力の平均値を使っていたのですが、これだけの数のアンサンブルなら統計的な考えも入れていいだろうと思い、外れ値を除外してから平均値を計算するようにしました。これが大きくLBスコアを改善することになりました。
LBスコア
- 第1世代モデル480個(外れ値除外なし) : 19.950
- 第1世代モデル480個(以降、外れ値除外あり) : 19.908
- 第2世代モデル480個 : 19.847
- 第3世代モデル480個 : 19.841
- 第4世代モデル480個 : 19.827
- 第5世代モデル480個 : 19.816
- 第6世代モデル480個 : 19.816
- 第6世代モデル480個+パラメータ調整 : 19.774
- 第3+6世代モデル960個+パラメータ調整 : 19.766
おわりに
今回のアイデアが上手く言った要因として、データが綺麗であることや、データの規模が大きくはないといった点があります。大量の整理されていない実データを対象とするには色々と問題があり、できればこの手法を改善していきたいと思っています。
それとは別に、私もしっかりデータ分析できるようになりたい。。。