辞書学習とは
LASSOとは、\(y\)と\(\Phi\)が与えられたとき、\(\frac{1}{2}\|y - \Phi x \|^2_2 + \lambda \|x\|_1\)を最小化するような\(x\)を見つける問題でした。これは、\(y = \Phi x\)に対して、観測値\(y\)は説明変数のうち少ないものの組み合わせで表現できるはずだという仮定に基づいています。
今回紹介する辞書学習は、観測値が与えられたとき、これらの観測値は辞書と呼ばれる典型的なパターンが少数組み合わさることで表現できるはずだという仮定に基づいています。
辞書学習では、観測値をいくつか集めてきて行列\(Y\)を作り、ここから、\(Y \approx DX\)となるように、典型的なパターンを意味する辞書の行列\(D\)と、辞書の少数の組み合わせ方を示すスパースコードの行列\(X\)に分解を行います。
このような行列の分解を行うアルゴリズムはいくつか知られており、スパースコードを\(L_1\)正則化を使って求めるscikit learnに実装されている方法や、\(L_0\)正則化を使って求めるMODアルゴリズムやK-SVDアルゴリズムなどがあります。ここでは、シンプルで強力なK-SVDアルゴリズムを使ってみましょう。
K-SVDアルゴリズム
K-SVDアルゴリズムは、scikit learnなどには実装がないため、筆者がコミッタをつとめるオープンソースのスパースモデリング用のライブラリであるspm-imageを使うことにします。
まずは、ランダムな100 x 200の行列\(Y\)を作って、それにK-SVDアルゴリズムを適用してみましょう。実装の都合上、行優先(Row-major)で、\(Y \approx XD\)と扱われることに注意してください。つまり、\(Y\)は200次元の観測値を縦に100個並べたものと解釈します。
そして、以下のコードを実行することで、100 x 50のスパースコードの行列\(X\)と50 x 200の辞書の行列\(D\)が得られます。
import numpy as np from spmimage.decomposition import KSVD Y = np.random.rand(100, 100) ksvd = KSVD(n_components = 50, transform_n_nonzero_coefs = 5) X = ksvd.fit_transform(Y) D = ksvd.components_
得られたスパースコードの行列\(X\)と辞書の行列\(D\)を可視化した様子です。\(D\)の各行を辞書といい、これが、観測値\(Y\)を構成するための少数のパターンを表しています。\(X\)はほとんどの値が0の疎な行列となっており、\(X\)の\(i\)行目は、\(Y\)の\(i\)行目の観測値を表現するための辞書の組み合わせ方を示しています。
![スパースコードと辞書](http://cz-cdn.shoeisha.jp/static/images/article/11823/11823_001.png)
しかし、残念ながら、今回の観測値\(Y\)はランダムな行列であるため有益な辞書は得られていないようです。これは、再構成誤差\(Y - XD\)を計算してみると、その誤差が大きいことからわかります。実際、\(Y\)は各要素の値が0~1のランダムな値ですが、再構成誤差\(Y - XD\)について、MAE(Mean Absolute Error、平均絶対誤差)を計算してみると、
np.mean(np.abs(Y - X.dot(D)))
0.12821788379791452
が得られ、これは無視できない大きさであることがわかります。
辞書学習では、観測値は辞書と呼ばれる典型的なパターンが少数組み合わさることで表現できるはずだという仮定に基づいていますが、そもそもこの仮定に当てはまっていないものに対しては意味のある結果を得ることができません。では、どういうときに使えばいいのでしょうか。その一つとして、画像データへの応用があります。