One-Hot Encodingをするには値の種類が多いときに便利なGroup-Hot Encoding


　ここで紹介するテクニックは、２クラス・他クラス分類において、「One-Hot Encodingをすると細かくなりすぎるが、連続値として扱うわけにもいかない」カラムをどう扱うかについてのアイデアをまとめたものです。
　このテクニックは、コンペに取り組んでいる間に自分で思いついたもので、過去に読んだ本には載っていません。ですが、さすがに世界初のアイデアということもないと思います。(Group-Hot Encodingは自分でつけた名前であり、公式なものではありません）
　このアイデアにより、Signateのコンペティション 
 
 https://signate.jp/competitions/1247

 
 において、瞬間値ながら1200人中5位(社会人1位)の暫定評価を得るところまで達成しました。（ですが、コンペ終盤の他参加者の追い上げ＋最終評価でのshake upにより結果的にはイマイチな成績:cold_sweat:）

## 前提

* この記事中に登場するデータは、説明をわかりやすくする目的で適当に改変してあります。コンペのデータそのものではありません。

# コンペティションの概要

　ニューヨーク市内の木（街路樹なのか、公園や建物の敷地内なのかは不明。一応元ネタのニューヨーク市提供のデータがあるらしい）について、さまざまなデータと、その「健康状態」が表形式（大きなCSVファイル）で与えられています。健康状態は３段階(good/fair/poor)に区分されています。

![161dac53b7fa6c386da03b9907cc3658.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59073/72f09e48-d6d4-8eba-42c0-0ca5470233de.png)


　訓練データとして、健康状態のわかっているデータが約２万レコードあります。また、テストデータとして、健康状態**以外**がわかっているデータが約２万レコードあります。
　課題としては、訓練データから健康状態を推定するロジックを作成し、テストデータの健康状態を推定して提出します。主催者側はテストデータ側の「正解」を持っているので、それと照合してどれくらい正確だったかによって提出者の成績が決まります。

　この記事で扱うのは、それぞれの木の所在地を示す大地区番号・小地区番号というカラムです。
　大地区は、東京でいえば「新宿区」「千代田区」のようなけっこう大きな領域だと思えばよいです。小地区はもっと細かい区分、例えば「秋葉原」「新橋」「恵比寿」くらいのイメージです。

　また説明の簡単さのため、以下の前提をおきます。

 * 健康状態がpoorであるかどうかを推定する話に限定する
 * 全訓練データで通算すると、健康状態がpoorである割合は6%とする。
 * 大地区は１～５の５種類が登場するものとする
 * 小地区は1～100の100種類が登場するものとする

# 基本のOne Hot Encoding

　まずこちらを復習しておきます。これはだいたいどんな本にも出ている基本の技です。
　この大地区ごとに訓練データをまとめて、総数とpoorである数を集計すると以下のようだったとしましょう。

![ca6bf03754b9dbda349d1e19c573c2b0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59073/9a3b3fb7-b85e-63f2-7adf-08370c022e34.png)


　平均では6%ですが、地区により明らかに6%より大きいものと小さいものが存在します。これは推定において有力情報といえそうです。より専門的な言い方をすると、「有意な特徴量である」となります。

　しかし, **地区番号の数値の大小関係に意味があるわけではない**ので、各レコードがどの大地区に所属するかは大地区ごとの特徴量にしなければなりません。「大地区Nに所属するかどうか(N:1~5)」という特徴量をつくり、各レコードはその中の１つだけが値１をとり、他は０をとるようにします。以下のような具合です。

![c47feebe40330f49cdf4964e4287a61d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59073/b79d7250-78a1-3aef-5abd-2e0c9b43564a.png)


　これがOne Hot Encodingです。こうやって元データを作成しておけば、特徴量をどう料理するかはその先のXGBoostなりlightgbmがうまくやってくれます。

　本コンペでも大地区に相当するカラムは存在しており、自分のモデルでもこのOne Hot Encodingは特徴量として採用しています。

# 小地区になるとどうか

　では小地区の場合を見てみます。木の総数でソートしたところこんなふうになっていたとしましょう。

![51f781f730a4171730d0d366070d158b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59073/b1e0c78b-6318-b850-9a70-9b0fdd51de82.png)


　例えば以下が分かります。

* 小地区は100種類あるので、当然総数の少ない地区が存在する。
* そういう場合は、poorなものが極端に期待値より多かったり少なかったりしても、有意な情報なのか偶然なのかが推定不可能である。(母集団が小さいほど、偶然に左右されやすいです）

　また、OneHotEncodingを使った場合、地区の種類と同数の特徴量ができるうえ、値が１になっている割合も極めて低くなります。大地区番号の場合と比較すると、

| |大地区番号|小地区番号|
|:-----------:|:------------:|:------------:|
|特徴量の種類数|5|100|
|値が1である割合|20%|1%|

となります。一般に、情報量の少ない特徴量が多くなるほどモデルの性能は悪化します。（価値の低い特徴量を我慢して使うくらいなら、最初から切り捨てていったほうがモデルの性能は高いこともよくあります）

　しかし小地区番号を捨てるのは直感的にもったいないので、何とかうまく活用する方法はないか...と考えて思いついたのが次に紹介するGroup-Hot Encodingです。

# 小地区番号ごとに「σ」を計算する

　σは、期待値との差が標準偏差の何倍か、の値です。学生にはおなじみの偏差値です。
　全データでpoorである率が6%なら、ある母集団内のpoorの数は二項分布に従います。これを強引に正規分布に近似して、小地区ごとの期待値と標準偏差を求め、実際に観測された数と比較するとσがわかる、という手順です。
　本当はこの程度のサイズの母集団だったり、6%という低い割合については正規分布に近似するのは強引すぎるのですが、他にお手軽な手段がないので目をつぶります。

　ただ、あまりに小さい小地区はさすがに使えないので、ある程度のラインで足切りをして、「小さすぎて利用不可」という区別はあらかじめつけておきます。

　ここでは、100の小地区のうち小さすぎる20地区を別にとりわけ、残りの80地区についてσを計算し、σの順でソートします。

　このようになったとしましょう。

![345a7f637ce13e5f5ff1db236588e30b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59073/cedb2e70-0056-f9a2-e050-53c539a35257.png)


　おおざっぱには、順位が真ん中の40位近辺のσは0になるでしょう。

　そうしておいて、例えば５グループに分けたいと思ったら、

* 1～16位　→　グループA
* 17～32位　→　グループB
* 33～48位　→　グループC
* 49～64位　→　グループD
* 65～80位　→　グループE

　と小地区をグループに分けます。例えるなら、生徒に偏差値をつけ、偏差値順でクラス分けをするようなものです。

# Group Hot Encoding

　そして、小地区番号のかわりに、どのグループに入っているか（もしくは利用不可グループか）によってOne Hot Encodingをします。

![cbe74741685e1d5590196ec8236b32f2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59073/aed500dd-37d4-b6e2-a16f-1070dd541af4.png)


　こんな具合です。これで、小地区情報を６個の特徴量で表現することができ、ベタなOneHotEncodingのときの特徴量数100に比べて大幅に削減できました。

　この名称は僕がつけたものであり、公式なものではありません。どこかに名前がついているかもしれないのでご存じの方いましたら教えてください。

　もちろん、細かいバリエーションはいろいろ考えられます。例えば、

* 順位だけでグループを決めず、各グループで木の総数がだいたい均等になることを目指してみる
* 「順位で真ん中のグループに所属していること」はどのみちpoorかどうかの判定材料にならないので、判定不可グループとマージしてしまう（これで特徴量が１つ減る）

　とかがあります。
　僕が本コンペで作成したモデルでは、こういうのを一通り試し、最も成績のよいものを採用しています。

# 最後に

　なんとなく始めたコンペ生活ですが、予想以上に面白い。僕ももう年齢は40代後半だし、仕事も半分引退してて時間はあるので、今後も楽しい趣味になりそうです。AtCoderのような瞬発力の必要なコンペだと、もはや若い人に勝てる見込みはありませんが、機械学習系ならまだまだいけます！　次の目標は、自分の子供にこの楽しみを教え、高校生のうちに金メダルを取れるくらいに鍛えることかな。