ロバストな損失関数でHouse Pricesをやる
※20210730追記
この記事はたぶんADMMに関して変なことをやっています。拡張ラグランジアンの係数は多分グリッドサーチとかでは調べないのと、notebookのElasticNetのsoft-thresholdingが間違ってます。
※追記ここまで
なんかコンペにでも参加しようか、と思うも程よい感じのが探せない。
でも何かしらはやりたいなと思うと、Titanicと同じくKaggle的にはチュートリアルだし、回帰から生まれた回帰太郎なのに回帰やってないしでHousePricesをやるかとなった。
……ものの、いくつかやってみた系の記事を眺めると、なかなか説明変数がだるい感じ。
いろいろと眺めて気になったのは目的変数のSalePriceの対数のヒストグラムを見て正規分布っぽいですねといってLassoなりを適用しているもの。
見た感じ(正規分布は日常的にとてもよく使うので)でも正規分布より裾が厚いし、実際
SalePriceの対数のQ-Qプロットを見ると裾の方はやはり直線からのずれがそれなりにある。たとえばこの方の記事など。
この方は外れ値に対してしっかり処理しているが、Lassoそのままぽい〜もそこそこ見かけたりした。
それで思ったのは、説明変数の前処理はもう適当に済ませてしまって、コスト関数をそれっぽくしたときに予測精度が上がるかを見ようか、ということ。
ロバストな損失関数といってもいろいろあるが、見た感じだと中央部分はガウスっぽいし裾だけそこそこ厚いからHuber lossがよさそうに見える。Tukeyとかε-感度損失とかだと指しすぎな感じがする。
それと正則化はがっつり入れたい。
こういうときにさくっと導出できて便利なのがADMMで
基本的にこれの6章と一緒。
本命をHuber+L1だと思うわけだけど
Least absolute deviations - Wikipedia
HuberがほとんどLADと一緒なので、LAD+L1の更新式を導出してちょっとずつ変えていった。
HuberないしLADの場合に正則化項をつける場合は上のpdfでの6.1に|x|_1の項がつくわけだけど、この場合は更にx - w = 0という条件をつけて拡張ラグランジアンをつくると解けるようになる。
というわけで実際に実装してやってみたのが下記リンクのnotebook。
https://github.com/tkimaizumi/HousePrice
pandasの処理が奇怪なことになっているのは手元の環境のせい。具体的にいうとnumpyのverのせいっぽい。対処方法を調べるとnumpyのverを下げろと出てきたもののそれも嫌だし別の環境つくるのも面倒だしということでそういうことになっている。
比較のためにElasticNet, LassoからはじめてLAD+L1,LAD+ElasticNet, Huber+ElasticNet, Huber+L1の順にやっていった。
マルチコあるからL2ノルム項を入れないと……と即思ってしまい、はじめはElasticNetでやったものの、よくよく考えると予測しかしないんだから関係ないじゃんと思いLassoでやったりなど結構場当たり的に進めた。
統計的に有意な差かはあやしいと思うものの、L1よりElasticNetのほうがよかった。理由を考えると、trainとtestを別々に標準化している分(trainのfitでtestをtransform)、trainとtestのマルチコの度合いが違ってElasticNetのほうがよくなるのかも知れない?
Kaggle的にはどうせtestの特徴量が与えられてるし実務とかじゃないのでマージして標準化するのが筋だとは思うもののそれは好きではないので仕方ない。
スコアそのものと説明変数に関しては今回まるで関心がないので数値型なら即標準化、それ以外はOne-Hot Encodingして標準化した。
でsubmitしてみたときのスコアが
ElasticNet: 0.13837
Lasso: 0.13864
LAD+L1: 0.13584
LAD+ElasticNet :0.13560
Huber + ElasticNet :0.13839
Huber + L1 :0.13857
HuberよりLADのほうがよかったですまる。
0.138台と0.135台は順位で見ると1000位くらい上がる感じの差っぽい。テストデータのサイズがトレーニングデータのサイズとほぼ一緒で1460くらいだから結構な差といってもいいのか……?
まとめ。
予想とはちがったけどもロバストな損失関数にすると一応スコアは改善されるっぽい。
雑感:
ADMM、導出と実装がとっても楽でよい。ただ収束判定とかが適当だったりするのでその辺はどうにか。
欲しい解があんまり動かなくなったらやめる方式にしていて、3パラメータのグリッドサーチなんだけども、LADだとCVが早めに終わったのにHuberだと結構時間かかった(つまりmax_iterまでやってる)ので多分収束してない。
ついでに色々と調べている過程でRanganがADMMはGAMP(Generalized)/VAMP(Vector Approximate Message Passing)でMAP推定する際の特殊な場合と言っていて信頼感が増した。
まあ非凸な場合はちょっとあやしいなと思うわけですが。
あと3-Blockに分割すると収束しないことがあるとか結構色々と知れてよかった。もうちょっとADMMに関して調べたい。
LADだとこのくらいのデータ1460×300くらいで25 msとか。MBPで10^3のグリッドサーチしても数分くらいという高速さよ。