Feature Hashing

機械学習において、Feature Hashing（フィーチャーハッシング）は、高速かつ省メモリな特徴量をベクトルに変換する手法であり、任意の特徴をベクトルあるいは行列のインデックスに変換する。kernel trick(カーネルトリック)に似せてHashing Trick（ハッシュトリック）とも呼ばれる。連想配列を走査するのではなく、ハッシュ関数を特徴量に適用し、その値をインデックスとして直接使用する。 典型的な文書分類のタスクにおいて、機械学習アルゴリズムには(学習と分類の両方において)自由な形式のテキストが入力される。このテキストからBag of words（英語版）(BOW)表現が作られる。つまり、トークンが抽出・カウントされ、訓練データ中のそれぞれのトークンが、訓練データ・テストデータ両方におけるそれぞれの文書の特徴量(独立変数)として定義される。 ところが、ほとんどの場合機械学習アルゴリズムは数値型のベクトルを扱うように定義されている。それゆえ文書集合に対するBag of wordsはDocument-term matrix（英語版）と見なされる。ここでそれぞれの行は文書を表し、列は特徴量(単語)を表している。つまり、行列の(i, j)成分は文書iのj番目の単語の頻度(または重み)を表す(行列の行と列の役割を逆にする見方もあるが、この違いは重要ではない)。 このような行列は一般的に非常にスパースである。 訓練あるいはその前段階にいて、訓練データの単語集合に対して辞書表現を作り、単語をインデックスに射影するという方法がよく使われる。しばしばハッシュテーブルやトライ木を使って辞書が作られる。例えば、次のような3つの文書 John likes to watch movies. Mary likes movies too. John also likes football. は辞書を使って次のように変換される。 そして次のようなDocument-term行列ができる。 ( John likes to watch movies Mary too also football 1 1 1 1 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 1 0 0 0 0 0 1 1 ) {\displaystyle {\begin{pmatrix}{\textrm {John}}&{\textrm {likes}}&{\textrm {to}}&{\textrm {watch}}&{\textrm {movies}}&{\textrm {Mary}}&{\textrm {too}}&{\textrm {also}}&{\textrm {football}}\\1&1&1&1&1&0&0&0&0\\0&1&0&0&1&1&1&0&0\\1&1&0&0&0&0&0&1&1\end{pmatrix}}} (文書の分類やクラスタリングでよくされるように、時制は無視している) このプロセスでの問題なのが辞書を保存するために多くのスペースが必要で、訓練データのサイズが大きくなるにつれてその必要スペースが増加することである(Heapsの法則（英語版）)。 そのうえ、単語集合の大きさが一定数で固定されているときには、その単語集合に含まれない新しい単語や綴りの正しくない単語を使うことで、学習した分類フィルターをすり抜けることができてしまう。これはYahoo! Research（英語版）のスパムフィルタリングでFeature Hashingが使われる理由である。 もちろんHashing Trickの利用は文書分類やその他文書レベルの類似タスクに限られるわけではなく、多くの(あるいは上限が存在しない)数の特徴量を持つあらゆる問題に適用できる。 ハッシュ関数hを訓練・予測対象のアイテムの特徴集合(例えば単語集合)に適用して、そのハッシュ値を特徴量のインデックスとして使う。そしてこのインデックスで特徴ベクトルを更新する。このようにして、辞書を使うことなくあらかじめ定義した長さの特徴ベクトルを作ることができる: function hashing_vectorizer(features : array of string, N : integer): x := new vector[N] for f in features: h := hash(f) x[h mod N] += 1 return x ハッシュ値の衝突を避けるために、1ビット出力の関数ξを使って更新値の符号を決定する方法が提案されている。アルゴリズムは次のようになる: function hashing_vectorizer(features : array of string, N : integer): x := new vector[N] for f in features: h := hash(f) idx := h mod N if ξ(f) == 1: x[idx] += 1 else: x[idx] -= 1 return x 上の擬似コードは実際にサンプルをベクトルに変換する。処理の最適化としては、(h,ξ)のペアの列だけを生成し、その列を処理して学習や予測を行うというものが考えられる。線形モデル（英語版）は係数ベクトルを表す1つのハッシュテーブルで表現することができる。 2番目のハッシュ関数であるξを使って特徴値の符号を決定するとき、出力の配列のそれぞれの列の平均の期待値は0になる。なぜなら、ξはいくつかの衝突を回避するからある。例えば2つの符号特徴量f₁とf₂が互いに衝突し、それ以外の特徴量とは衝突していないとする。このときξに対して何も前提条件が無いとすると、右の表で示すような同じ確率を持つ4つの場合がある。 この例では、衝突が回避される確率は50%である。多値ハッシュ関数を使えばより衝突のリスクを回避することができる。 さらに、もしφがハッシュ関数 ξのHashing Trickによって実現された変換だとすると(つまり、φ(x)が標本xに対して作られた特徴ベクトルだとすると)、ハッシュ後の空間におけるベクトルの内積は不偏である: E [ ⟨ φ ( x ) , φ ( x ′ ) ⟩ ] = ⟨ x , x ′ ⟩ {\displaystyle \mathbb {E} [\langle \varphi (x),\varphi (x')\rangle ]=\langle x,x'\rangle } ここで期待値はハッシュ関数φについて計算されている。 ⟨ φ ( x ) , φ ( x ′ ) ⟩ {\displaystyle \langle \varphi (x),\varphi (x')\rangle } が半正定値のカーネルであることが確かめられる。 最近の研究ではHashing Trickは単語からインデックスへの教師ありの射影に拡張された。この方法では重要な単語の衝突を避けるよう明示的に学習が行われる。 GanchevとDredzeはランダムなハッシュ関数を使って特徴量をもともとの1000分の数10程度に落としてテキスト分類を行い、符号に関するハッシュ関数を使わない場合でさえ、Feature Hashingは分類精度に悪影響を及ぼさないことを示している。 Weinbergerらはアレンジしたハッシュ関数をスパムフィルタリングの問題に応用し、これをマルチタスク学習（英語版）の問題に定式化した。ここで入力特徴量はユーザーと特徴量のペアになっており、パラメータベクトルが数10万人のユーザーに対するグローバルなフィルターであると共にユーザーごとのフィルターとして機能する。これによってフィルターの精度が上がることを確かめられた。 Hashing Trickの実装は以下で提供されている: Apache Mahout Gensim scikit-learn sofia-ml Vowpal Wabbit（英語版）