EnsekiTT Blog

EnsekiTTが書くブログです。

機械学習の前処理でカテゴリデータをワンホット表現にする話

こんにちは、えんせきです。
インフルエンザから復活して久々に会社に行ったら浦島太郎気分になりました。

つまりなにしたの?

カテゴリデータを説明変数に含むデータを機械学習に突っ込む時にはよくカテゴリデータをワンホット表現にすることが多い。
今回はそのワンホット表現を作る話。
f:id:ensekitt:20180206223704j:plain

なんでワンホットベクトルにするの?

極端な話、単純な線形分離がしやすくなるかどうかってところだと思います。
例えば、カテゴリが「にんじん、たまねぎ、じゃがいも」だとして、

これを「にんじん:1, たまねぎ:2、じゃがいも:3」とかってなっていると
 y = wx
のような線形分離を適用すると閾値の設定によって
0.5:「φ, にんじんたまねぎじゃがいも」
1.5:「にんじん, たまねぎじゃがいも」
2.5:「にんじんたまねぎ, じゃがいも」
3.5:「にんじんたまねぎがじゃいも, φ」
の4パターンになっちゃう。

これを「にんじん:100, たまねぎ:010, じゃがいも:001」とかってすると
 y = w_{にんじん} x_{にんじん} + w_{たまねぎ} x_{たまねぎ} + w_{じゃがいも} x_{じゃがいも}
って表現できるので、それぞれのwに0.5とかの閾値を入れれば好きな分類が表現できる。
うれしい。
ってことでニューラルネットワークとか決定木*1向けにこんな変換をしてみる。
f:id:ensekitt:20180206224031j:plain

じゃあどうやるの?

対象のデータセット

ID 美味いもの メイン食材
0 肉じゃが じゃがいも
1 きんぴら にんじん
2 オニオンリング たまねぎ
3 ポテトサラダ じゃがいも
4 もみじおろし にんじん
5 フライドポテト じゃがいも

一人暮らしのレパートリーがバレる。

データフレームに読み込む

import pandas as pd
import sklearn.preprocessing as sp
data = pd.read_csv("menu.csv", skipinitialspace=True)

f:id:ensekitt:20180206223447p:plain
こんなデータを読み込みこんだ。

ラベルエンコードを実施する

le = sp.LabelEncoder()
le.fit(data.MATERIAL.unique())
data.MATERIAL = le.fit_transform(data.MATERIAL)

#  参考: 元に戻す方法
data.MATERIAL = le.inverse_transform(data.MATERIAL)

f:id:ensekitt:20180206223456p:plain
ラベルがラベルの元ネタと対応ついた状態で変換できた。
Excelで置換とかしてCSVで読み出しちゃうと後で対応表を片手に変換しなおすハメになるのでこういった方法を使ったほうがいいと思う。)

ワンホット表現に変換する

ohe = sp.OneHotEncoder()
enced = ohe.fit_transform(data.MATERIAL.values.reshape(1, -1).transpose())
temp = pd.DataFrame(index=data.MATERIAL.index, columns="MATERIAL-" + le.classes_, data=enced.toarray())
enced_data = pd.concat([data, temp], axis=1)
del enced_data['MATERIAL']
enced_data

f:id:ensekitt:20180206223618p:plain
変換できた。Reshapeとかちょっとややこしいけど、これはもともとカテゴリがベクトルで表現されているものに適用するための仕組みだからっぽい。

実行したJupyter Notebookはこちら

github.com

*1:やらなくてもできるけど無駄に木が深くなるからバランス調整すると性能悪化の要因になりうる