読者です 読者をやめる 読者になる 読者になる

Avazuが終わった

Avazu Click-Through Rate Predictionが終わりました。長かった。期間が長すぎる!! と言っても、僕は始めて1週間目くらいにプランクトン分類が始まったため、そっちばかりでほとんどやっていなかったのですが、ついに終わりました。
結果は72th/1604。一応、上位5%には入っているけど、僕は絶対値を気にするので、72というのはあまり良くないな、と思う。

ソリューション

以下自分の投稿内容です。

概要

0.6 * 統計量を特徴量に使ったGBDTモデル + 0.4 * 交互作用項を追加した fast_solution_v3.py
です。

統計量を特徴量に使ったGBDTモデル

まずデータを2つに分割します。

  • 統計量計算用 (14/10/30のデータ)
  • モデル学習用 (14/10/30より前のデータ)

各カテゴリ変数について、

  • 出現回数
  • クリック回数 (出現したうちクリックされたレコード数)

を統計量計算用データから計算します。

カテゴリ変数 出現回数 クリック回数
site_id=123 10 3
site_id=456 100 10
app_id=123 40 8
app_id=456 50 10
... ... ...

という感じのデータ。これらを使って、モデル学習用データの各変数を置換します。
例えば、

site_id=123のとき site_id=(10, 3)
site_id=456のとき site_id=(100, 10)
app_id=123のとき app_id=(40, 8)
app_id=456のとき app_id=(50, 10)

のように。これでたくさんあるダミー変数の次元が全て2に次元削減されます。今回の場合、22変数だったので、44次元のデータになります。

この特徴量をxgboostを使って学習しました。
単体のLBスコアは、0.3933798でした。

今回とにかく、GBDTを使ってみたいという思いが初めにあったのですが、変数が多すぎてうまく学習できないように思えたのでとにかく減らしてみました。
データの分割方法はいろんなパターンが考えられて、たとえば、統計量計算に使うデータを1日ずつズラしながら7モデル学習して平均するようなこともできるのですが、やってみたところ特に良くなりませんでした。全体をランダムに分割するというのも試しましたが、これは統計量の情報が正確すぎるせいか、めちゃくちゃオーバーフィッティングしました。また古いデータから統計量を計算すると、新しく出て来た変数の情報が無くて変換できない変数がテストデータに増えるため、一番新しいデータである30日を統計量計算用に使うのがよさそうでした。

交互作用項を追加したfast_solution_v3.py

fast_solution_v3.pyはフォーラムで共有されているコードです。これ
中身は、FTRL-Proximal online learning algorithmとかいうのを使った線形分類器です。

変数の削除は、L1正則化に期待することにして、あとはいい感じのinteractionを追加できれば精度が良くなるだろうと思ったので、変数を追加する方向で改造しました。
方法としては、ただのブルートフォースアタックです。

  • 22変数から2つ選んだ全ての組み合わせについて、交互作用項として入れたときと入れなかった時の検証スコアを計算
  • 効果があった変数順にソート
  • 効果がありそうな変数を上から5個単位で追加していってスコアが悪くなったところで追加を辞める

というコードを書いて、一晩ほど計算した結果を少し手で調節して、以下のパラメーターで学習するとよい、ということになりました。

# これは事前に改造なしで探索したパラメーター
params = { "epoch": 2, "alpha": 0.05, "beta": 1.0, "L1": 3.0, "L2": 5.0 } 
# 追加する変数
params["interaction"] = (
    ('site_id', 'C14'),
    ('site_domain', 'C14'),
    ('app_domain', 'C14'),
    ('site_domain', 'C17'),
    ('site_id', 'C17'),
    ('app_domain', 'C17'),
    ('site_category', 'C14'),
    ('site_domain', 'C19'),
    ('app_id', 'C14'),
    ('site_domain', 'C20'),          
    ('app_id', 'C20'),
    ('site_id', 'C20'),
    ('site_id', 'C19'),
    ('app_category', 'C14'),
    ('app_domain', 'C18'),
    ('site_category', 'C17'),
    ('site_domain', 'app_id'),
    ('app_id', 'C17'),
    ('site_category', 'C20'),
    ('site_id', 'app_id'),
    ('site_domain', 'app_category'),
    ('app_id', 'C21'),
    ('site_domain', 'site_category'),
    ('app_id', 'C19'),
    ('site_category', 'app_id'),
    ('site_id', 'app_category'),
    ('site_domain', 'C21'),
    ('C14', 'C19'),
    ('site_id', 'site_domain')
    )

interactionは元のコードでは全て入れるか全て入れないかの2値ですが、指定した変数の組み合わせだけ使うように改造しています。
これで、単体のLBスコアは 0.3930048 でした。

アンサンブル

0.6 * 統計量を特徴量に使ったGBDTモデル + 0.4 * 交互作用項を追加したfast_solution_v3.py
です。重みは適当に試して決めました。

これで、LBスコアは 0.3905856 でした。各モデル単体のスコアに比べてかなり良くなっています。(このコンペではスコアが0.001変わると順位が150位くらい変わるので"かなり"良くなっています)

Privateスコアは 0.3887624。

感想

あまり熱心に取り組んでいないというのもあるのですが、フォーラムを見ると、learning rate decayが効いただの書いてあって、何でオレはそんなことも試していなんだ...と思う内容が多かったです。
データが大きくてあまり手の込んだことはできないという思いが強くて、試すパターンを制限しすぎてしまっていた気もします。