※四則演算やベクトル,行列について基本的な事項を理解していることが前提です.
Abstract
LSAとは文章の潜在共起性を発見するための次元削減手法です. 自然言語処理 NLPや情報検索 IRの分野で使われ,昨今は確率を導入したりしたLDAやそれを拡張した潜在トピックが主に 研究・応用されています. LSAは行列の特異値分解というものがメイン. しかし,私は固有値は聞いたことあるけど,特異値って?という状態だったので, 行列演算の復習と他サイトを参考に特異値分解についてまとめてみました. 線形変換とか,そういった線形代数は正直チンプンカンプンなので,間違いがあるかもしれませんが,ご了承ください.
- Abstract
- 行列基本演算
- 固有値分解 Eigendecomposition(Eigen Value Decomposition :EVD)
- 特異値分解 Singular Value Decomposition :SVD
- 参考
- 行列・ベクトルの表記
行列基本演算
実行列と複素行列
実行列は行列の要素が実数であるもの,複素行列は行列の要素が複素数であるものです.
実数と複素数の違いは虚数があるかないかといえるでしょう.
実数はのような数であり,複素数はのような数です.
が虚数で,二乗してになる数です.
虚数をと表記するのは工学系の数学であり,
数学科の数学(高校数学から大学の工学でない数理系学科の数学)では虚数はで複素数はと表記すると思われます.
どちらも内容は同じです.ただ,表記の仕方が違うだけで,
工学系はは電流を意味するため,虚数にを用いているものと思われます.
以下で示しているPythonではが虚数でのように書きます.
Rではと書くようです.
複素数\(A=a+jb\)があるとします. これに対し,符号を反転させた複素数\(\bar{A}=a-jb\)を(\(a+jb\)に対する)共役複素数と言います. つまり,共役関係は\(A*\bar{A}=(a+jb)*(a-jb)=a^2+b^2=実数値\)となる複素数間の関係のことを指しています.
>>> a=1+2j
>>> a
(1+2j)
>>> type(a)
>>> a.real
1.0
>>> a.imag
2.0
# 共役複素数
>>> a.conjugate()
(1-2j)
>>> a*a.conjugate()
(5+0j)
Pythonでは複素数はcomplex
型と定義されており,共役複素数はconjugate()
メソッドによって求めることができます.
次に,複素行列に対して, 以上のような共役複素数を要素とする(共役複素数で置き換えた)行列を と書きます. さらに,の転置行列を随伴行列(共役転置行列)といい,
と書きます.
以上で,随伴行列の表記をいくつかを示していますが, プログラミングであれば\(*\)は積演算のイメージがあるのでを用いているのかな~?と 思います. また,\(*\)を共役であることを示すものと捉えるか,転置まで行ったものと捉えるか,各分野で少し違うようです. 本記事ではを随伴行列とすることとします. 転置と共役のステップは順不同で,逆であっても随伴行列となります.
ちなみに,はダガ―と読む2項演算子です.
の特殊記号で\dag
や\textdagger
があり,それでもと同じような記号が出力されるはずですが,
はてなブログではこちらは認識しないようですね...
では,Pythonで複素行列を扱ってみましょう.
>>> import numpy as np
>>> b=2+2j
# 複素行列
>>> A=np.array([[a,0],[0,b]])
>>> A
array([[1.+2.j, 0.+0.j],
[0.+0.j, 2.+2.j]])
>>> A.real
array([[1., 0.],
[0., 2.]])
>>> A.imag
array([[2., 0.],
[0., 2.]])
>>> A.conj # A.conjugateでも同じ
# 共役複素行列
>>> np.conj(A)
array([[1.-2.j, 0.-0.j],
[0.-0.j, 2.-2.j]])
>>> -A
array([[-1.-2.j, -0.-0.j],
[-0.-0.j, -2.-2.j]])
>>> A*np.conj(A)
array([[5.+0.j, 0.+0.j],
[0.+0.j, 8.+0.j]])
# 随伴行列(共役転置行列)
>>> np.conj(A).T
array([[1.-2.j, 0.-0.j],
[0.-0.j, 2.-2.j]])
>>> np.conj(A.T)
array([[1.-2.j, 0.-0.j],
[0.-0.j, 2.-2.j]])
以下から固有値分解ならびに特異値分解についてみていくわけですが, 分解対象の行列は実行列でも複素行列でもよいです. ただ,一般的に実行列を想定している場合が多いかと思います. そもそもNLPやIRでは複素数を使わないでしょうし,本記事では固有値分解に関しては実行列を対象に考えます. ただし,特異値分解に関しては複素行列を想定したサイトや本の説明が多いように感じましたので,例題等では複素行列で 行ってみたいと思います.
固有値分解か特異値分解かは,実数か複素数かではなく,正方か長方かの違いによるものだと考えるとよいかと思います.
行列を行列やベクトルに分割する方法
例えば,
という行列を点線で分割すると,
という行列やベクトル(ベクトル)で分割できます.
分割の仕方は任意です.
ここで,分割して得られた行列やベクトルは元の行列に対して小行列といいます.
また,は縦ベクトル,は横ベクトルでもあります.
行列積
行列の積は左の行列の各行ベクトルと右の行列の各列ベクトルの内積です. そのため,左行列の列数と右行列の行数が一致している必要があります. 反対に左行列の行数と右行列の列数が異なっていても計算できます.
例えば,行列積は
となります.
左行列のサイズが,右行列のサイズが
のとき,計算して得られる行列のサイズはとなります.
転置行列とベクトルの内積
転置行列は行と列を入れ替えた行列であり,
という行列に対して,その転置行列は
と表せます.とも表記します.
転置行列について
という定理が成り立ちます.
また,ベクトルの内積は転置行列を用いることで行列の積として考えることができます. 例えば,
の内積は
というように一方のベクトルの転置をとることで行列の積となります.
逆行列
次の正方行列に対して,
その逆行列は
と表します.は一乗と読まずにインバースと読むはず...
それはおいといて,/は余因子行列,/は行列式といいます.
それぞれ,二次の場合については,
となります.
ここで,小行列というものについて述べておきます.
行列の小行列は行列から行と列を取り除いた行列です.
ちなみにです.
3次の正方行列に対する小行列をいくつか示すと,
となります.
2次の行列式と余因子行列について以上で示しました. 以下より,小行列を用いて,3次以降についての行列式と余因子行列は
となります.
逆行列に関して,
という定理が成り立ちます.
対角行列
- 対角行列長方対角行列と区別するのに,本記事では正方対角行列とする :行数と列数が等しく,左上から右下にかける対角成分のみ値を持っている行列
- 長方対角行列 :正方対角行列に任意の行もしくは列に任意の数だけ零ベクトル(要素が全て零のベクトル)を連結した行列
- ブロック対角行列 :正方対角行列の対角成分が値ではなく,正方行列となっている行列.
正方対角行列において,その乗は
となります. つまり,正方対角行列はその対角成分の各要素を乗すればよいということです. かなり計算がラクです. 2次正方対角行列とかで逆行列を考えると,以上のようになることが分かると思います.
固有値分解 Eigendecomposition(Eigen Value Decomposition :EVD)
対象:正方行列
(次)正方行列はつまり行列というような,行数と列数が一致している行列のことです. は任意の整数値です.
正則行列は逆行列をもつ行列,または行列式がでない行列のことです. 反対に逆行列をもたない行列,または行列式がである行列は特異行列と呼ばれます.
固有値,固有ベクトル
正方行列が
を満たすとき,
といいます. 固有値との数は行列のサイズによって異なります. 固有値には各固有値に対応した固有ベクトルがあり,固有ベクトルも固有値の数だけ存在します. は固有値と固有ベクトルの数です.
固有方程式(+単位行列)
\(\lambda_i\boldsymbol{P}_i=\lambda_i\boldsymbol{E}\boldsymbol{P}_i\)であることより,
が得られます.以上の式が自明な解以外を解にもつための必要十分条件より,
以上において,行列は単位行列であり,
という,対角成分がの行列です. 単位行列に関して,
という性質が成り立ちます.
一般化固有ベクトル
以上に対し,
を満たすを一般化固有ベクトルというそうです. LSAを使うにはここまで理解してなくてもいいと思いますが...
固有ベクトルと例題
以上から
となり, ある固有値を代入して計算することである固有値に対する固有ベクトルが得られます. はの列数です.
例として,
の固有値と固有ベクトルを求めてみましょう. をの固有値として,固有方程式を解くと,
より,の固有値はと分かりました.
ここで,
に\(\lambda = 5,1\)を代入することで,\(\boldsymbol{A}\)の固有ベクトル\(\boldsymbol{P}\)が求まります.
まず,固有値\(\lambda_1=5\)の固有ベクトルを求めましょう.
ここで,\(P_{11},P_{12}\)は任意の定数とします.
任意の定数なので,固有ベクトルが整数値の方が手計算したりするのにラクなので,
固有ベクトルが整数値になるように\(P_{11},P_{12}\)は任意で決めていいです.
他の固有ベクトルについても同様です.
固有値を上式に代入すると,
となり,
という連立方程式が得られ,上式より,
が得られます.以上から,
となります.
\(P_{11},P_{12}\)は任意の定数としたので,固有ベクトルが小さい整数値となるように, \(P_{11}=P_{12}=1\)とすると,固有値の固有ベクトルは
ここで,\(P_{21},P_{22}\)を任意の定数とします.
固有値と同様に式に代入すると,
となり,以上より,
となります.
ここで,\(P_{22}=1\)とすると,固有値の固有ベクトルは
となります.
ここで,以上をPythonでやってみましょう.
>>> import numpy as np
>>> A = np.array([[2,3],[1,4]])
>>> eig_values,eig_vetors = np.linalg.eig(A)
>>> print(eig_values)
[1. 5.]
>>> print(eig_vetors)
[[-0.9486833 -0.70710678]
[ 0.31622777 -0.70710678]]
>>> p2,p1 = np.hsplit(eig_vetors,[i for i in range(1,len(eig_values),1)])
>>> p2
array([[-0.9486833 ],
[ 0.31622777]])
>>> p1
array([[-0.70710678],
[-0.70710678]])
>>> (1/p2[1,0])*p2
array([[-3.],
[ 1.]])
>>> (1/p1[0,0])*p1
array([[1.],
[1.]])
対角化
後々のために,固有値を降順にしておいたほうがいいですね.
ここで,
とすると,
固有値分解
以上の対角化の式の両辺に,右からを,左からをかけると,
となり,を行列の積で分解することができました. これが固有値分解というわけです.
以上を
という先の例題と同じ行列を使って,固有値分解して得られる各行列を求め, さらに,検算として行列を掛け合わせたら元の行列に戻るかも Pythonを使ってやってみましょう.
>>> import numpy as np
>>> A = np.array([[2,3],[1,4]])
>>> eig_values,eig_vetors = np.linalg.eig(A)
>>> index=np.argsort(eig_values)[::-1]
>>> P = np.c_[eig_vetors[:,index[0]]]
>>> if len(index)>1:
for i in range(1,len(index),1):
P = np.hstack([P,np.c_[eig_vetors[:,index[i]]]])
>>> P
array([[-0.70710678, -0.9486833 ],
[-0.70710678, 0.31622777]])
>>> Pinv = np.linalg.inv(P)
>>> Pinv
array([[-0.35355339, -1.06066017],
[-0.79056942, 0.79056942]])
>>> np.dot(np.dot(P,np.diag(eig_values[index])),Pinv)
array([[2., 3.],
[1., 4.]])
固有値分解後の行列で行列演算を行うと,ちゃんと元通りの行列になっていることが分かります. また,以上では,固有値ならびに固有ベクトルを降順に並び替える手順も行っています.
おまけ:の求め方
対角化の式の両辺を乗して変形していくと,
以上より,の算出式が求められました.
特異値分解 Singular Value Decomposition :SVD
対象:長方行列
固有値分解は正方行列に対して適用できました. 対して,特異値分解は非正方行列のための行列分解の理論です.
非正方つまり長方行列は行列の行列で,通常です. な長方行列が正方行列,つまり,長方行列の特殊バージョンが正方行列であるといえますね.
直交行列(実数)とユニタリ行列(複素数)
実行列において,
を満たす行列を直交行列と言います.
以上の式について,何か見覚えがありませんか?
単位行列の性質として示した
と同じ形ですね. つまり,が正則ならば,
もまた,が直交行列である条件式であるわけですね.
ユニタリ行列は直交行列の転置行列の部分を随伴行列に置き換えた式を満たす複素行列のことです.つまり,
を満たす複素行列がユニタリ行列です. ある複素行列とその複素行列の随伴行列の行列積が実行列である単位行列となるとき, その複素行列はユニタリ行列というわけです. ユニタリ行列は実行列である直交行列を複素数に拡張したもの,もしくはユニタリ行列の実数値版が直交行列である といえます.
また,
を満たす複素行列を正規行列と言います. ユニタリ行列は正規行列の一種というわけです.
特異値,特異ベクトル
より定義されました.
特異値と特異ベクトルはサイズが,つまり行列の行列を用いて,
を満たすものとして定義されます. 以上において,
- :非負実数値
- :左特異ベクトル次元
- :右特異ベクトル次元
となっています.
また,が実行列ならばは,つまり,ただの転置行列です.
ここで,\(\boldsymbol{X}\boldsymbol{X}^\dagger\)と\(\boldsymbol{X}^\dagger\boldsymbol{X}\)の\(0\)でない固有値は一致します. そして,特異値はその\(0\)でない正の固有値の非負平方根であるとされています. ただし,\(\boldsymbol{X}\boldsymbol{X}^\dagger\)と\(\boldsymbol{X}^\dagger\boldsymbol{X}\)はどちらも正方行列で \(0\)でない固有値は一致していますが, サイズが異なるために固有ベクトルは異なります. \(\boldsymbol{X}\boldsymbol{X}^\dagger\)と\(\boldsymbol{X}^\dagger\boldsymbol{X}\)のそれぞれの固有ベクトルが \(\boldsymbol{X}\)の左特異ベクトル,右特異ベクトルというわけです. 一旦整理しましょう.
固有値分解と特異値分解
また,以上の式において,
- が(左を満たす行列をエルミート行列という)で正方行列
- 左特異ベクトルと右特異ベクトルが等価
のとき,固有値の式\(\boldsymbol{A}\boldsymbol{P}_i=\lambda_i\boldsymbol{P}_i\)になりますよね. つまり,ここで,特異値分解は固有値分解の拡張理論である,もしくは,固有値分解は特異値分解の特殊パターンであると 考えられますよね.
対角化
正方正規行列に対し,
とすると,
となります. このとき,はユニタリ行列です. また,固有値を求めるので,は正方でなければなりませんね.
以上において,行列の行列は(正方な)対角化はできないわけですが, とは対角化できるわけです. ここで,
であるので,
と対角化できます.
特異値分解
いくつかサイトを見たんですが,特異値分解の式の導出(証明)がイマイチ私には理解できなかったので, Pythonで代数演算もしくは数値を代入した演算を行い,特異値分解の式が正しいことを感覚的に納得しようかな~と思います. Pythonでは代数演算を”Sympy”というライブラリで行うことができ, 行列演算もこれを用いてできます.
行列の行列に対し,
固有値と特異値
の固有値との固有値は等しく, その固有値の平方根がの特異値ということでした. このことを代数演算で感覚的に確からしいことを確認したいと思います.
まずは,固有値分解できる正方行列でこのことを試してみます. そこで,
として,計算しましょう.
>>> import sympy as sy
>>> a,b,c,d = sy.symbols("a b c d")
>>> X=sy.Matrix([[a,b],[c,d]])
>>> l1,l2=X.eigenvals()
>>> u1,u2=(X.T*X).eigenvals()
>>> v1,v2=(X*X.T).eigenvals()
>>> u1==v1
True
>>> u2==v2
True
>>> u1,u2=(X*X.conjugate().T).eigenvals()
>>> v1,v2=(X.conjugate().T*X).eigenvals()
# conjugate()部分の代数演算をしてくれないので,等価にならない(計算すればなるはず)
>>> u1==v1
False
>>> u2==v2
False
>>> t2,t1=X.singular_values()
>>> t1==sy.sqrt(u1)
False
>>> t2==sy.sqrt(u2)
False
>>> t1==sy.sqrt(v1)
True
>>> t2==sy.sqrt(v2)
True
# 代数演算が複雑になると,Pythonで計算してくれないので,数値を入れて確認する
# 固有値
>>> xe=sy.lambdify((a,b,c,d),X.eigenvals(),"numpy")
>>> xe(1,2,3,4)
{-0.3722813232690143: 1, 5.372281323269014: 1}
# 特異値(メソッドを使って)
>>> xs=sy.lambdify((a,b,c,d),X.singular_values(),"numpy")
>>> xs(1,2,3,4)
[5.464985704219043, 0.3659661906262571]
# 特異値(左特異ベクトルの固有値の平方根)
>>> xs_=sy.lambdify((a,b,c,d),[sy.sqrt(x) for x in (X*X.conjugate().T).eigenvals().keys()],"numpy")
>>> xs_(1,2,3,4)
[0.3659661906262571, 5.464985704219043]
# 特異値(右特異ベクトルの固有値の平方根)
>>> xs_=sy.lambdify((a,b,c,d),[sy.sqrt(x) for x in (X.conjugate().T*X).eigenvals().keys()],"numpy")
>>> xs_(1,2,3,4)
[0.3659661906262571, 5.464985704219043]
# 他の数値では
>>> xe(4,3,1,2)
{1.0: 1, 5.0: 1}
>>> xs(4,3,1,2)
[5.398345637668169, 0.9262096826685894]
>>> xs_(4,3,1,2)
[0.9262096826685894, 5.398345637668169]
以上を長方行列でも試してみましょう. 固有値に関しては求められないため,そこのステップは省きます.
>>> a,b,c,d,e,f=sy.symbols("a b c d e f")
>>> X = sy.Matrix([[a,b,c],[d,e,f]])
>>> s2,s1,s3=X.singular_values()
>>> u1,u2=(X*X.conjugate().T).eigenvals()
>>> v1,v2,v3=(X.conjugate().T*X).eigenvals()
>>> s1==sy.sqrt(u1)
True
>>> s2==sy.sqrt(u2)
True
>>> s1==sy.sqrt(v1)
False
>>> s2==sy.sqrt(v2)
False
>>> s3==sy.sqrt(v3)
>>> s3
0
# 数値を入れて,特異値を確かめる
# 特異値(メソッドを使って)
>>> xs=sy.lambdify((a,b,c,d,e,f),X.singular_values(),"numpy")
>>> xs(1,2,3,4,5,6)
[9.508032000695724, 0.7728696356734833, 0]
# 特異値(左特異ベクトルの固有値の平方根)
>>> xs_u=sy.lambdify((a,b,c,d,e,f),[sy.sqrt(x) for x in (X*X.conjugate().T).eigenvals().keys()],"numpy")
>>> xs_u(1,2,3,4,5,6)
[0.7728696356734833, 9.508032000695724]
# 特異値(右特異ベクトルの固有値の平方根)
>>> xs_v=sy.lambdify((a,b,c,d,e,f),[sy.sqrt(x) for x in (X.conjugate().T*X).eigenvals().keys()],"numpy")
>>> xs_v(1,2,3,4,5,6)
[0.7728696356734833, 9.508032000695724, 0]
>>> a,b,c,d,e,f=sy.symbols("a b c d e f")
>>> X = sy.Matrix([[a,b],[c,d],[e,f]])
>>> s2,s1=X.singular_values()
>>> u1,u2,u3=(X*X.conjugate().T).eigenvals()
>>> v1,v2=(X.conjugate().T*X).eigenvals()
>>> s1==sy.sqrt(u1)
False
>>> s2==sy.sqrt(u2)
False
>>> s1==sy.sqrt(v1)
True
>>> s2==sy.sqrt(v2)
True
>>> u3
0
# 数値を入れて,特異値を確かめる
# 特異値(メソッドを使って)
>>> xs=sy.lambdify((a,b,c,d,e,f),X.singular_values(),"numpy")
>>> xs(1,2,3,4,5,6)
[9.525518091565107, 0.5143005806586431]
# 特異値(左特異ベクトルの固有値の平方根)
>>> xs_u=sy.lambdify((a,b,c,d,e,f),[sy.sqrt(x) for x in (X*X.conjugate().T).eigenvals().keys()],"numpy")
>>> xs_u(1,2,3,4,5,6)
[0.5143005806586431, 9.525518091565107, 0]
# 特異値(右特異ベクトルの固有値の平方根)
>>> xs_v=sy.lambdify((a,b,c,d,e,f),[sy.sqrt(x) for x in (X.conjugate().T*X).eigenvals().keys()],"numpy")
>>> xs_v(1,2,3,4,5,6)
[0.5143005806586431, 9.525518091565107]
\(\boldsymbol{X}\boldsymbol{X}^\dagger\)の固有値と\(\boldsymbol{X}^\dagger\boldsymbol{X}\)の固有値は等しいことが分かります. そして,それらの平方根も等しく,\(\boldsymbol{X}\boldsymbol{X}^\dagger\)と\(\boldsymbol{X}^\dagger\boldsymbol{X}\)の固有値の平方根が特異値であることも分かります. さらに,以上から,\(\boldsymbol{X}\)に関して,\(m\)を行数,\(n\)を列数とすると,
- m>n:の特異値との固有値の数はどちらも, の固有値にが含まれ,その数は
- m=n:の特異値との固有値と の固有値の数は全て(もしくは)
- m<n:の特異値との固有値にはが含まれ, その数はどちらも, の固有値の数は
と分かります.
また,正方行列において,固有値と特異値は非等価ですが,なんとなく似た数値になっているかと思います. 特異値は固有値と似たような役割を持つものではないのかなぁと考えられます. このことから,固有値が求められない場合に,代わりに特異値を用いる,それが特異値分解ということなのかなと.
Pythonで特異値分解をしてみよう
>>> import numpy as np
>>> from numpy import linalg as LA
もしくは
>>> from scipy import linalg as LA
>>> X = np.matrix([[2,1,1],[-1,2,1]])
>>> m,n = X.shape
# Hで随伴,Iで逆行列(matrixのみ.ndarrayにはない.)
# 右特異行列を求める
>>> X_eigenvalues,V = LA.eigh(np.dot(X.H,X))
# 降順にソート
>>> X_eigenvalues_index = sorted(np.argsort(X_eigenvalues),reverse=True)
>>> X_eigenvalues = [X_eigenvalues[i] for i in X_eigenvalues_index]
>>> V=V[:,X_eigenvalues_index]
# 特異値を求める
>>> s=np.sqrt(X_eigenvalues)
>>> s=[i for i in s if np.isnan(i)!=True]
>>> s
[2.6457513110645907, 2.23606797749979]
# 特異値行列を求める
>>> S=np.asmatrix(np.diag(s))
>>> S
matrix([[2.64575131, 0. ],
[0. , 2.23606798]])
# 左特異行列と特異値行列から右特異行列を求める
>>> U=np.dot(np.dot(X,V[:,:len(s)]),S.I)
>>> U
matrix([[-0.70710678, 0.70710678],
[-0.70710678, -0.70710678]])
# 特異値行列を長方化
>>> S= np.hstack([S,np.zeros((len(s),n-len(s)))])
# 確認
>>> np.dot(np.dot(U,S),V.H)
matrix([[ 2., 1., 1.],
[-1., 2., 1.]])
# 右特異行列を求める(固有値方程式から)
>>> XXt_eigenvalues,U = LA.eigh(np.dot(X,X.H))
>>> XXt_eigenvalues_index = sorted(np.argsort(XXt_eigenvalues),reverse=True)
>>> XXt_eigenvalues = [XXt_eigenvalues[i] for i in XXt_eigenvalues_index]
>>> U=U[:,XXt_eigenvalues_index]
>>> U
array([[ 0.70710678, -0.70710678],
[ 0.70710678, 0.70710678]])
# この右特異行列から元の行列を計算すると...
>>> U=np.asmatrix(U)
>>> V=np.asmatrix(V)
>>> np.dot(np.dot(U,S),V.H)
matrix([[-2., -1., -1.],
[ 1., -2., -1.]])
上記スクリプトにおいて,各特異行列を固有値方程式から求めたものを使って計算すると,元の行列の符号反転したものが出力されています. このことから,特異行列はどちらかを先に求めてから,求めた特異行列を使ってもう一方の特異行列を求めるやり方でないと, 符号がうまく揃わず元の行列が得られない,と考えられるので, 固有値方程式を解くのは片方だけにしたほうが良いでしょうね.
>>> import numpy as np
>>> from numpy import linalg as LA
もしくは
>>> from scipy import linalg as LA
>>> X = np.matrix([[2,1,1],[-1,2,1]])
>>> m,n = X.shape
# メソッドで特異値分解
>>> U,s,V_dagger=LA.svd(X)
# 特異値行列を求める
>>> S= np.hstack([np.diag(s),np.zeros((len(s),n-len(s)))])
# 確認
>>> np.dot(np.dot(U,S),V_dagger)
matrix([[ 2., 1., 1.],
[-1., 2., 1.]])
上記以外のサイズや形の行列に対しても,特異値分解をPythonでやってみましょう.
>>> import numpy as np
>>> from numpy import linalg as LA
もしくは
>>> from scipy import linalg as LA
>>> X = X = np.matrix([[2,1],[1,-1],[2,1]])
>>> X
matrix([[ 2, 1],
[ 1, -1],
[ 2, 1]])
>>> m,n = X.shape
>>> U,s,V_dagger=LA.svd(X)
>>> S= np.vstack([np.diag(s),np.zeros((m-len(s),len(s)))])
>>> np.dot(np.dot(U,S),V_dagger)
array([[ 2., 1.],
[ 1., -1.],
[ 2., 1.]])
>>> import numpy as np
>>> from numpy import linalg as LA
もしくは
>>> from scipy import linalg as LA
>>> X = np.matrix([[2,1],[1,-1]])
>>> X
matrix([[ 2, 1],
[ 1, -1]])
>>> U,s,V_dagger=LA.svd(X)
>>> S=np.diag(s)
>>> np.dot(np.dot(U,S),V_dagger)
array([[ 2., 1.],
[ 1., -1.]])
特異値分解の式
行列の行列に対し,
以上において,左特異行列,右特異行列,は対角成分が特異値の対角行列です. また,
- 左特異行列:次ユニタリ行列
- 右特異行列:次ユニタリ行列
- 特異値行列:対角成分が特異値
- m > n:行列の長方対角行列=
- m = n: or 次正方対角行列
- m < n:行列の長方対角行列=
となっています.
参考
- 作者:柴田 正和
- 出版社/メーカー: 森北出版
- 発売日: 2013/12/20
- メディア: 単行本(ソフトカバー)
- 固有値分解 - Wikipedia
- 特異値分解 - Wikipedia
- 特異値 - Wikipedia
- Pythonで特異値分解(SVD)を理解する - け日記
- 特異値分解の定義,性質,具体例 | 高校数学の美しい物語
- NumPyで行列の固有値、固有ベクトルを求めるlinalg.eig関数の使い方 - DeepAge
- NumPyで逆行列を求めるlinalg.invの使い方 - DeepAge
- NumPy配列ndarrayの対角成分の抽出、対角行列の作成(diag, diagonal) | note.nkmk.me
- http://www.cfme.chiba-u.jp/~haneishi/class/iyogazokougaku/SVD.pdf
- NumPyの配列ndarrayの欠損値np.nanを他の値に置換 | note.nkmk.me
- [Pythonによる科学・技術計算] numpy・scipyを用いた(一般化)固有値問題の解法,ライブラリ利用 - Qiita
- scipy.linalg.eig — SciPy v1.3.3 Reference Guide
- SymPyで代数演算してみる - Qiita
- sympyで線型代数学の問題を解く - My Notes
- Pythonを使って一瞬で連立方程式を解く - Qiita
- 複素数型 (実部・虚部・偏角)
- 共役転置と随伴行列│Python 数値計算入門
- https://algorithm.joho.info/programming/python/numpy-sort/
- numpy.ndarrayの基礎 (zeros / ones / empty) | Python-izm
- NumPyで行列式と逆行列を計算する - MathPython
- 大学1年生もバッチリ分かる線形代数入門
レイアウト等について
- Mathjaxの使い方を覚えて美しい数式表示をしてみよう! - はるなぴログ
- はてなブログでベクトルをbold表記したい[markdown] - 逆さまにした
- はてなブログのMarkdown記法等 - tak0kadaの何でもノート
- Markdown記法でコメントアウトする - Qiita
- SVD (singular value decomposition) に入門する - Qiita
- 【CSS】オーバーフローしたコンテンツの表示 - Qiita
- スタイルシート[CSS]/ボックス/ボックスからはみ出た部分の表示方法を指定する - TAG index
- https://hoshimi12.com/?p=12552
Pythonについて(他に記事でも参照)
Pythonデータサイエンスハンドブック ―Jupyter、NumPy、pandas、Matplotlib、scikit-learnを使ったデータ分析、機械学習
- 作者:Jake VanderPlas
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/05/26
- メディア: 単行本(ソフトカバー)
行列・ベクトルの表記
以上は\mathcal{x}
との数式入力した結果で,筆記体でxを書いたものです.
しかし,これではxがベクトルであることが伝わらない...
ということで,ググってトップに出てきたブログ記事にやり方が書いてありました.
以上のサイトのやり方を本文でも使用させていただきました.