6th Place Solution by maruyama
GitHub に コード全体 を置きました。
ここでは、コード全体から抜粋して要点のみ説明します。
特徴抽出
何もしませんでした。
全部ニューラルネットワークに委ねました。
モデル
ニューラルネットワークを使いました。
カテゴリ変数をそのまま入れて、ニューラルネットワークに embedding させました。
それ以外の工夫は特になく、全結合を 3 段重ねる形に落ち着きました。
def build(self):
x_num_input = tf.keras.Input(shape = (self.num_dim,), name = "x_num")
x_cat_input = tf.keras.Input(shape = (self.cat_dim,), name = "x_cat")
x_cat = tf.keras.layers.Embedding(self.vocab_size, self.emb_dim, name = "embedding")(x_cat_input)
x_cat = tf.keras.layers.Flatten(name = "flatten")(x_cat)
x = tf.keras.layers.concatenate([x_num_input, x_cat], name = "concat")
x = tf.keras.layers.BatchNormalization(name = "hidden_norm1")(x)
x = tf.keras.layers.Dense(self.hidden_units, activation = tf.keras.layers.ReLU(), name = "hidden_dense1")(x)
x = tf.keras.layers.BatchNormalization(name = "hidden_norm2")(x)
x = tf.keras.layers.Dense(self.hidden_units, activation = tf.keras.layers.ReLU(), name = "hidden_dense2")(x)
x = tf.keras.layers.BatchNormalization(name = "output_norm")(x)
x = tf.keras.layers.Dense(1, name = "output_dense")(x)
self.model = tf.keras.Model(inputs = [x_num_input, x_cat_input], outputs = x)
損失関数とオプティマイザー
評価指標が MAE だったため、損失関数には MAE っぽい形だが微分可能な Huber loss を使いました。
オプティマイザーには Adam を使いました。
オプティマイザーのパラメータは特にチューニングしませんでした。
def compile(self):
self.loss_object = tf.keras.losses.Huber()
self.optimizer = tf.keras.optimizers.Adam()
やったけどダメだったこと
- 後段を Transformer にする。
- カテゴリ変数の多い問題には NLP の手法が転用できるのではないかと思い、試しました。
- ダメでした。
- 学習自体が進まなくなりました。もっと逆伝搬させる工夫を入れればよかったです。
- Embedding layer の出力を使って LightGBM でモデルを作り直す。
- "特徴抽出が得意なニューラルネットワーク" と "(ニューラルネットワークと比べると) 安定した学習結果が得られる LightGBM" のいいとこどりをしたら精度が上がるのではないかと思い、試しました。
- ダメでした。
- LightGBM の学習が強烈に遅くなりました。学習率 0.1 で 10,000 個重ねても early stopping しませんでした。LightGBM は密な特徴表現を扱うことがあまり得意でないみたいです。
- テストデータっぽさで重み付けした損失関数を最適化する。
- 働き方改革かな? に書いた通り、残業時間の分布が学習データとテストデータでかなり違っていました。こういった共変量シフトがある問題を解く方法として学習データとテストデータの生成確率の比で重み付けすることが提案されており (杉山先生の資料のp.4 で知りました)、それに近いことをやってみようと思い、試しました。
- 密度比を推定するのは大変そうだったため、代わりに LightGBM で作成した学習データ/テストデータの判別モデルを作り、テストデータの確信度を使いました。
- ダメでした。
- ちゃんと密度比を使って再チャレンジしたいです。
今思えば学習データとテストデータの確信度の比を重みにすればよかったです。確信度の比 (※下記の式) でやってもダメでした。うーん……
$$
\frac{p(x \mid y=\mathrm{test})}{p(x \mid y=\mathrm{train})}
=\frac{\frac{p(x,y=\mathrm{test})}{p(y=\mathrm{test})}}{\frac{p(x,y=\mathrm{train})}{p(y=\mathrm{train})}}
=\frac{p(x,y=\mathrm{test})}{p(x,y=\mathrm{train})}\bullet\frac{p(y=\mathrm{train})}{p(y=\mathrm{test})}
\propto\frac{p(x,y=\mathrm{test})}{p(x,y=\mathrm{train})}
$$
$$
\frac{p(x,y=\mathrm{test})}{p(x,y=\mathrm{train})}
=\frac{\frac{p(x,y=\mathrm{test})}{p(x)}}{\frac{p(x,y=\mathrm{train})}{p(x)}}
=\frac{p(y=\mathrm{test}\mid x)}{p(y=\mathrm{train}\mid x)}
$$
$$
\therefore
\frac{p(x \mid y=\mathrm{test})}{p(x \mid y=\mathrm{train})}
\propto\frac{p(y=\mathrm{test}\mid x)}{p(y=\mathrm{train}\mid x)}
$$
所感
データがきれいだったため、新しいことにチャレンジできるコンペでした。
いろいろ試したのに、何の工夫もないモデルが最も良い結果となってしまい、少し寂しいです。