給与推定

給与推定により人事の赤池くんの窮地を救おう

賞金: 100,000 参加ユーザー数: 569 5年弱前に終了

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)} $$

所感

データがきれいだったため、新しいことにチャレンジできるコンペでした。
いろいろ試したのに、何の工夫もないモデルが最も良い結果となってしまい、少し寂しいです。

Favicon
new user
コメントするには 新規登録 もしくは ログイン が必要です。