機械学習の勉強を進めて行く中でOne Hot encodingという単語に出くわしました。
One Hot encodingとは、カテゴリー変数を機械学習のアルゴリズムが学習しやすいように0と1で表現する処理のことです。
縦持ちのカテゴリー変数を、横持ちにすることで、機械学習のモデル生成がうまくいくので、ちょくちょくしなければならない前処理です。
デジタル回路において、one-hot(ワン・ホット)は1つだけHigh(1)であり、他はLow(0)であるようなビット列のことを指す 類似のものとして、0が1つだけで、他がすべて1であるようなビット列をone-cold(ワン・コールド)と呼ぶことがある
One-hot - Wikipedia
売上データを使った例
会社によくあるケースとして、売れた商品と売上金額が入っているDBを考えてみましょう。
One Hot encoding前
このように格納してあるDBは多いのではないでしょうか。普通に利用する分には良いのですが、機械学習では少し使いづらい形です。
id | item | sales |
1 | A | 1000 |
2 | B | 2000 |
3 | C | 3000 |
One Hot encoding後
One Hot encodingの処理を加えると、品名の列を、品名A、品目B、品名Cに分けて、ブーリアン型の0か1がセットされます。0と1をセットするので二値化(
id | sales | itemA | itemB | itemC |
1 | 1000 | 1 | 0 | 0 |
2 | 2000 | 0 | 1 | 0 |
3 | 3000 | 0 | 0 | 1 |
このカテゴリー変数を分割する処理をOne Hot encodingと呼びます。
実装
いつもお世話になっている前処理大全(P154.第7章 展開)に横持ちから縦持ちにする方法で、One Hot encodingが実現できます。
Pythonで計算
import pandas as pd
import numpy as np
ファイルの読み込み¶
df = pd.read_table('~/Desktop/analysis/blog/input/onehot.csv')
df.head(5)
id | item | sales | |
---|---|---|---|
0 | 1 | A | 1000 |
1 | 2 | B | 2000 |
2 | 3 | C | 3000 |
モデルの作成¶
# index='sale' : グルーピングしたい列(表示したい列)をセット
# columns='item' : one hot encodingしたいカテゴリ変数をセット
# values='id' : カテゴリ変数の値(0か1)として利用する、aggfuncで利用する引数なのでid、sales、item、どれでも良い
# aggfunc=lambda x: len(x), fill_value=0 : valuesの個数をカウント(0か1)、値がなかったときに0をセット
pd_onehot = pd.pivot_table(df,
index=['id', 'sales'],
columns='item' ,
values='id' ,
aggfunc=lambda x: len(x), fill_value=0)
pd_onehot
item | A | B | C | |
---|---|---|---|---|
id | sales | |||
1 | 1000 | 1 | 0 | 0 |
2 | 2000 | 0 | 1 | 0 |
3 | 3000 | 0 | 0 | 1 |
ファイルの出力¶
# forecastのデータをcsvで出力
pd_onehot.to_csv('~/Desktop/analysis/blog/output/onehot.py.csv')
Rで計算
library('tidyverse')
library('dplyr')
ファイルの読み込み¶
df = read.table(file = '~/Desktop/analysis/blog/input/onehot.csv', header = TRUE)
df
id | item | sales |
---|---|---|
1 | A | 1000 |
2 | B | 2000 |
3 | C | 3000 |
One Hot encoding¶
# カテゴリ型に変換
df$item <- as.factor(df$item )
# group_by(id, item, sales) : グルーピングしたい列(表示したい列)をセット
# summarise(cnt=n()) : グルーピングした列でのカウント、idがユニークであるので必ず1になる
# spread(item, cnt, fill=0) : 表示したい列からone hot encodingしたいカテゴリ変数をセット、cntの値(=1)、値がなかったときに0をセット
df.onehot <- df %>%
group_by(id, item, sales) %>%
summarise(cnt=n()) %>%
spread(item, cnt, fill=0)
df.onehot
id | sales | A | B | C |
---|---|---|---|---|
1 | 1000 | 1 | 0 | 0 |
2 | 2000 | 0 | 1 | 0 |
3 | 3000 | 0 | 0 | 1 |
ファイルに出力¶
# ファイルに書き出す
write.csv(df.onehot, file = '~/Desktop/analysis/blog/output/df.onehot.csv', row.names = T)
カテゴリー変数を数字として扱って機械学習するのは何故ダメなのか?
モデルが正しく生成できないからです。カテゴリー変数の品名A、B、Cに優劣、平均はありません。
例えば品名A、B、C、を1、2、3に変換してモデル生成をすると、1(A)よりも3(C)の方が良い製品、という間違ったモデルを生成してしまいます。
また、数字として扱うと品目の平均をとれることになるので、1(A)+2(B)+3(C)=6/3=2(B)となります。これは品名の平均は2(B)になる、という誤ったモデルが生成されてしまうことになります。
品名に優劣、平均という概念はありませんが、数字ラベルを用いるとこのようなおかしなモデルを作ってしまうのでカテゴリー変数を数字で扱うのがダメなのです。
コメント