2018年11月5日月曜日

行列の特異値分解

特異値分解(singular value decomposition, SVD)から見ていきましょう。固有値を計算していたときには、その行列が正方行列であることを前提としていましたが、特意値分解は正方行列ではない行列にも拡張したものと言えます。Aの特異値分解とは、

A=U∑V∗
のことをいいます。ここで、VはA∗A(*は共役転置行列を表す)の固有ベクトルを列ベクトルとして並べた行列、UはAA∗の固有ベクトルを列ベクトルとして並べた行列、∑は特異値を対角に並べた行列とします。ただし、Aは(m,n)行列として、AA∗(ハミルトン行列)の固有値はせいぜいmin(m,n)であり、それらの正の固有値をσ2iとした時、固有値の平方根σiを特異値といいます。

[参考URL]

特異値分解:https://ja.wikipedia.org/wiki/%E7%89%B9%E7%95%B0%E5%80%A4%E5%88%86%E8%A7%A3

具体的に計算をすると以下になります。ちなみに、@は行列の積を簡素化するための演算子です。(PythonやNumpyのバージョンによっては使えませんが、ilect上では大丈夫です。)


# (2,5)行列
A = np.array([[1,2,3,4,5],[6,7,8,9,10]])

# 特異値分解の関数linalg.svd
U, s, Vs = sp.linalg.svd(A)
m, n = A.shape

S = sp.linalg.diagsvd(s,m,n)

print("U.S.V* = \n",U@S@Vs)
U.S.V* =
[[ 1. 2. 3. 4. 5.]
[ 6. 7. 8. 9. 10.]]
補足:ちなみに、この特異値分解は機械学習の章で学ぶリッジ回帰や主成分分析などと関係があります。さらに深層学習を学ぶ上でも行列分解は大事です。この講座では、細かい計算は追いませんが、頭の片隅において置いてください。

[参考URL]

Statistical Learning with Sparsity The Lasso and Generalizations: https://web.stanford.edu/~hastie/StatLearnSparsity_files/SLS.pdf

次は、LU分解です。Aを正方行列として、Ax = bを解く代わりに、PLUx = bを解くことで、効率よく解を求めることができます。置換行列P、対角成分がすべて1の下3角行列L、上3角行列UをA = PLU となるようにおきます。具体的な計算は次のようになります。


#データの準備
A = np.identity(5)
A[0,:] = 1
A[:,0] = 1
A[0,0] = 5
b = np.ones(5)

# 正方行列をLU分解する
(LU,piv) = sp.linalg.lu_factor(A)

L = np.identity(5) + np.tril(LU,-1)
U = np.triu(LU)
P = np.identity(5)[piv]

# 解を求める
x = sp.linalg.lu_solve((LU,piv),b)
x
array([-3., 4., 4., 4., 4.])
次は、コレスキー分解です。Aがエルミート行列で正定値の場合に、下3角行列L、共役転置L∗の積A=LL∗に分解し、方程式はLL∗x=bとなり、これを解きます。


A = np.array([[7, -1, 0, 1],
[-1, 9, -2, 2],
[0, -2, 8, -3],
[1, 2, -3, 10]])
b = np.array([5, 20, 0, 20])


L = sp.linalg.cholesky(A)

t = sp.linalg.solve(L.T.conj(), b)
x = sp.linalg.solve(L, t)

# 解答
print(x)
[ 0.758 2.168 1.241 1.863]

# 確認
np.dot(A,x)
array([ 5.000e+00, 2.000e+01, 2.665e-15, 2.000e+01])
他には、QR分解等も可能です。ここでは割愛しますが、以下のURLなどを参考にしてください。

[参考URL]

Scipyの行列計算:https://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

以上でScipyを使った線形代数・行列の分解については終わりになります。

なお、行列分解の計算だけを見ていると何の役に立つのイメージしにくいのですが、実務的には商品のリコメンデーションなどに応用されています(非負値行列因子分解(NMF: Non-negative Matrix Factorization)など)。購買データを扱う際、1つ1つの購買(バスケット、購買ユーザー)に対して、各購入商品のフラグ付けをして行列にすることが多いのですが、ほとんど疎(スパース)な状態で、そのまま集計・分析をするとあまり意味のある結果が出ないことが多いです。そのため、次元削減するのに行列の分解の結果が使われます。なお、これらに関連する参考書としては、以下が参考になります。

[参考文献]

『岩波データサイエンス Vol.5、特集「スパースモデリングと多変量データ解析」』(岩波データサイエンス刊行委員会 (編集)、岩波書店)

ちなみに、非負値行列因子分解は、ある行列XをX≒WHと近似した時に、その近似後の行列W、Hの要素が全部正になるようにしており、以下の例はsklearnのdecompositionを使っています。


# NMFを使います
from sklearn.decomposition import NMF

# 分解対象行列
X = np.array([[1,1,1], [2,2,2],[3,3,3], [4,4,4]])

model = NMF(n_components=2, init='random', random_state=0)

W = model.fit_transform(X)
H = model.components_

W
array([[ 0.425, 0.222],
[ 0.698, 0.537],
[ 0.039, 1.434],
[ 2.377, 0.463]])

H
array([[ 1.281, 1.281, 1.282],
[ 2.058, 2.058, 2.058]])

np.dot(W, H) #W@Hでもよい
array([[ 1., 1., 1.],
[ 2., 2., 2.],
[ 3., 3., 3.],
[ 4., 4., 4.]])

0 件のコメント:

コメントを投稿