コンピュータサイエンスにおいて、データは様々な方法で表現することができ、当然ながら、その一つ一つに利点もあれば、ある分野では欠点もある。
コンピュータはカテゴリを持つデータを処理することができないので、この情報をコンピュータに処理させるためには準備する必要がある。
この作業を前処理という。
前処理の大部分はエンコーディングで、コンピュータが理解できる方法でデータの一つ一つを表現します(この名前は文字通り「コンピュータコードに変換する」ことを意味します)。
コンピュータサイエンスの多くの分野、特に機械学習とデジタル回路設計では、ワンホットエンコーディングが広く使われています。
この記事では、ワンホットエンコーディングとは何かを説明し、人気のあるいくつかの選択肢、PandasとScikit-Learnを使ってPythonでそれを実装します。
また、コンピュータにおける他の表現方法との有効性、長所と短所、そしてその応用例についても比較します。
ワンホットエンコーディングとは?
ワンホットエンコーディングとは、ベクトル表現の一種で、ベクトル内の1つの要素を除いてすべて0とし、その値を1として表現するものである。
また、似たような実装としてワンコールドエンコーディングと呼ばれるものがあり、この場合、ベクトル内の要素は1つを除いてすべて1であり、その値は0である。
例えば、[0, 0, 0, 1, 0]
や[1 ,0, 0, 0, 0]
はワンホットベクトルの一例である。
これと似たような手法で、同じくデータを表現するために使われるのが、統計学におけるダミー変数であろう。
これは、他の符号化方式とは大きく異なり、いずれも複数のビットに1を持たせるものです。
以下は、0から7までの数を2進数、グレイコード、ワンショットで表現した場合の比較表である。
| 10進数|2進数|グレイコード|ワンホット|の3種類
| 1 | 001 | 001 | 0000001 |
| 2 | 010 | 011 | 0000010 |
| 3 | 011 | 010 | 0000100 |
| 4 | 100 | 110 | 0001000 |
| 5 | 101 | 111 | 0010000 |
| 7 | 111 | 100 | 1000000 |
実際には、1つのホットベクトルに対して、n個の質問をし、nは持っているカテゴリーの数です。
これは1番ですか?これは2番ですか?これは7という数字ですか?
それぞれの「0」は「偽」であり、ベクトル中の「1」に当たれば、その答えは「真」である。
ワンホットエンコーディングは、カテゴリ特徴を分類や回帰アルゴリズムと相性の良い形式に変換するものです。
複数の種類のデータ表現が必要な手法に非常に有効である。
例えば、あるベクトルは回帰(以前の戻り値に基づいて関数を近似する)に最適で、あるベクトルは分類(固定セット/クラス、通常はバイナリへの分類)に最適かもしれない。
| ラベル|ID
| — | — |
| ストロベリー|1
| アップル|2
| スイカ|3
| レモン|4
| ピーチ| 5
| オレンジ|6
ここに6つのカテゴリデータの入力例がある。
ここで使われているエンコーディングの種類は「ラベルエンコーディング」と呼ばれるもので、非常にシンプルなものです。
コンピュータは数字の扱い方を知っているので、このカテゴリをどのように表現すればよいかがわかる。
しかし、このエンコーディングの方法はあまり効果的ではありません。
なぜなら、自然に高い数字に高い重みを与える傾向があるからです。
イチゴ」というカテゴリーが「リンゴ」よりも大きいとか小さいとか、「桃」に「レモン」というカテゴリーを加えると「オレンジ」というカテゴリーになるとか、そういうことは序数ではないので、意味がないのである。
これらのカテゴリーを一発符号化で表現する場合、実際には行を列に置き換えることになる。
これは、与えられたカテゴリーごとに1つの boolean
カラムを作成し、これらのカラムのうち1つだけが各サンプルに対して値1を取ることができるようにするものである。
| ストロベリー|アップル|スイカ|レモン|ピーチ|オレンジ|ID|…
| — | — | — | — | — | — | — |
上の表から、バイナリコードやグレイコードと比較して、ワンショット表現ではより多くの桁数が必要であることがわかります。
n桁の場合、ワンショット符号化ではn個の値しか表現できませんが、バイナリやグレイ符号化ではn桁で2n個の値を表現することができます。
実装
パンダ
ワンホットエンコーディングを使用して、データセットのカテゴリカラムの値を数値に変換する簡単な例を見てみましょう。
ここでは、国とその ID のリストという非常にシンプルなデータセットを作成します。
import pandas as pd
ids = [11, 22, 33, 44, 55, 66, 77]
countries = ['Spain', 'France', 'Spain', 'Germany', 'France']
df = pd.DataFrame(list(zip(ids, countries)),
columns=['Ids', 'Countries'])
上のスクリプトでは、df
という名前の Pandas データフレームを ids
と countries
という二つのリストで作成しています。
このデータフレームに対して head()
メソッドを呼び出すと、以下のような結果になります。
df.head()
国名のカラムには、カテゴリカルな値が含まれています。
get_dummies() 関数を使用して、 Countries
カラムの値を 1 ホットのエンコードされたベクトルに変換することができる。
y = pd.get_dummies(df.Countries, prefix='Country')
print(y.head())
get_dummies()メソッドの
prefix属性の値として
Countryを渡したので、出力にはワンホットエンコーディングされた各カラムのヘッダーの前に
Country` という文字列が見えます。
このコードを実行すると、次のような結果が得られます。
Country_France Country_Germany Country_Spain
0 0 0 1
1 1 0 0
2 0 0 1
3 0 1 0
4 1 0 0
Scikit-Learn
もう一つの方法は、もう一つの有名なライブラリであるScikit-Learnを使うことである。
このライブラリには OneHotEncoder
クラスと LabelBinarizer
クラスの両方があり、この目的のために使用できます。
まず、LabelBinarizer
をインポートしましょう。
from sklearn.preprocessing import LabelBinarizer
そして、先ほどと同じデータフレームを使用して、LabelBinarizer
をインスタンス化してフィットしてみましょう。
y = LabelBinarizer().fit_transform(df.Countries)
y`をプリントすると、次のようになります。
[[0 0 1]
[1 0 0]
[0 0 1]
[0 1 0]
[1 0 0]]
しかし、これはPandasのアプローチほどきれいではありません。
同様に、 OneHotEncoder
クラスを使うこともできます。
このクラスは先ほどのクラスとは異なり、マルチカラムデータをサポートしています。
from sklearn.preprocessing import OneHotEncoder
そして、リストを入力して、それをエンコーダにフィットさせてみましょう。
x = [[11, "Spain"], [22, "France"], [33, "Spain"], [44, "Germany"], [55, "France"]]
y = OneHotEncoder().fit_transform(x).toarray()
print(y)
これを実行すると、次のようになります。
[[1. 0. 0. 0. 0. 0. 0. 1.]
[0. 1. 0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0. 0. 1.]
[0. 0. 0. 1. 0. 0. 1. 0.]
[0. 0. 0. 0. 1. 1. 0. 0.]]
ワンホットエンコーディングの応用例
ワンホットエンコーディングは、機械学習やデジタル回路設計の分野で多く利用されています。
機械学習
前述したように、コンピュータはカテゴリカルデータをあまり得意としていない。
我々はカテゴリカルデータをうまく理解しているが、それはコンピュータが持っていない一種の前提知識によるものである。
機械学習の技術やモデルの多くは、非常に限定されたデータセット(通常はバイナリ)で動作する。
ニューラルネットワークはデータを消費して0..1
の範囲で結果を出し、その範囲を超えることはほとんどない。
つまり、機械学習アルゴリズムの大部分はサンプルデータ(「学習データ」)を受け取り、そこから特徴を抽出する。
この特徴を元に数学的モデルを作成し、それを使って予測や判断を行う。
その際、これらのタスクを実行するように明示的にプログラムされている必要はない。
入力は技術的に無制限であるが、出力は通常いくつかのクラスに限定されるような場合、分類が良い例となる。
2値分類の場合(例えば猫と犬を分類するようニューラルネットワークに教える)、猫なら「0」、犬なら「1」というマッピングになります。
しかし、ほとんどの場合、予測したい学習データは前述の果物の例のようにカテゴリ的なものです。
この場合も、私たちにとっては非常に意味のあることですが、アルゴリズムにとっては単語そのものが理解できないため、意味がありません。
このようなアルゴリズムでデータを表現するためにワンホットエンコーディングを使うことは技術的には必要ないが、効率的な実装をしたい場合にはかなり有効である。
デジタル回路設計
基本的なデジタル回路の多くは、その入出力値を表現するためにワンショット表記を採用しています。
例えば、有限状態マシンの状態を表すのに使われる。
グレーやバイナリなど他の表現方法を用いると、自然な互換性がないため、状態を判断するためのデコーダが必要になる。
一方、ワンショット有限状態機械は、nビット目が高ければ論理的にn番目の状態であるため、デコーダは不要である。
リングカウンターは、フリップフロップをシフトレジスタに接続したカウンターの一種であり、一方のフリップフロップの出力が他方のフリップフロップの入力に接続される。
このカウンターの1番目のフリップフロップは第1の状態を表し、2番目のフリップフロップは第2の状態を表し、以下同様である。
最初、マシンのすべてのフリップフロップは’0’に設定されていますが、最初のフリップフロップは’1’に設定されています。
次のクロックエッジがフリップフロップに到達すると、「ホット」ビットが1つ進み、2番目のフリップフロップに到達します。
このようにして「ホット」ビットは最後の状態まで進み、その後マシンは最初の状態に戻る。
デジタル回路設計におけるワンホットエンコーディングの他の使用例としては、アドレスデコーダがある。
アドレスデコーダはバイナリコードやグレーコードを入力し、ワンホットに変換して出力する。
優先度エンコーダは、その逆で、1ホットの入力を受けて、それをバイナリまたはグレイに変換して出力する。
ワンホットエンコーディングの長所と短所
他のエンコーディングと同様、ワンホットには良い点もあれば問題点も多くあります。
メリット
ワンホットエンコーディングの大きな利点は、1つのフリップフロップにアクセスするだけでよいので、マシンの状態を決定するコストが低く、一定であることです。
マシンの状態を変更する場合も、2つのフリップフロップにアクセスするだけなので、ほぼ同じ速度で変更できます。
また、ワンホットエンコーディングの優れた点は、実装が容易なことだ。
この記法で作られたデジタル回路は、設計も修正も非常に簡単だ。
また、有限状態マシンにおける不正な状態の検出も容易である。
ワンショット実装は最速であることが知られており、ステートマシンを他のどのエンコーディングよりも速いクロックレートで動作させることができる。
デメリット
ワンホットエンコーディングの主な欠点は、前述のように多くの値を表現できないことです(n個の状態を表現するには、n桁、つまりフリップフロップが必要です)。
このため、例えばワンショット15ステートリングカウンターを実装しようとすると、15個のフリップフロップが必要になりますが、バイナリ実装では3個のフリップフロップで済みます。
このため、PALデバイスでは特に非実用的で、コストも非常に高くなりますが、FPGAの豊富なフリップフロップを活用することができます。
この種のエンコーディングのもう1つの問題は、有限状態マシンの状態の多くが不正になることです。
良い点は、先に述べたように、これらの不正な状態を検出するのは非常に簡単であり(1つのXORゲートで十分)、その対策はそれほど難しくないということです。
結論
ワンホットエンコーディングは非常にシンプルなので、理解しやすく、実際に使いやすい。
コンピュータサイエンスの世界でこれだけ人気があるのは当然といえば当然です。
短所があまりないため、応用範囲が広い。
結局のところ、長所が短所を明らかに上回っているため、この種の実装は将来にわたって間違いなく定着することでしょう。