芋の独り言

当ブログへのアクセスは当ブログのプライバシーポリシーに同意したものとみなします.

LSI/LSAのための行列演算の復習

※四則演算やベクトル,行列について基本的な事項を理解していることが前提です.

Abstract

LSAとは文章の潜在共起性を発見するための次元削減手法です. 自然言語処理 NLPや情報検索 IRの分野で使われ,昨今は確率を導入したりしたLDAやそれを拡張した潜在トピックが主に 研究・応用されています. LSAは行列の特異値分解というものがメイン. しかし,私は固有値は聞いたことあるけど,特異値って?という状態だったので, 行列演算の復習と他サイトを参考に特異値分解についてまとめてみました. 線形変換とか,そういった線形代数は正直チンプンカンプンなので,間違いがあるかもしれませんが,ご了承ください.

行列基本演算

実行列と複素行列

実行列は行列の要素が実数であるもの,複素行列は行列の要素が複素数であるものです. 実数と複素数の違いは虚数があるかないかといえるでしょう.
実数は1,00.5,\frac{1}{3},-3のような数であり,複素数1+j0.5,1-j0.5のような数です. j虚数で,二乗して-1になる数です.
虚数jと表記するのは工学系の数学であり, 数学科の数学(高校数学から大学の工学でない数理系学科の数学)では虚数i複素数1+0.5iと表記すると思われます.
どちらも内容は同じです.ただ,表記の仕方が違うだけで, 工学系はi,Iは電流を意味するため,虚数jを用いているものと思われます.
以下で示しているPythonではj虚数1+0.5jのように書きます. Rでは1+0.5iと書くようです.

複素数\(A=a+jb\)があるとします. これに対し,符号を反転させた複素数\(\bar{A}=a-jb\)を(\(a+jb\)に対する)共役複素数と言います. つまり,共役関係は\(A*\bar{A}=(a+jb)*(a-jb)=a^2+b^2=実数値\)となる複素数間の関係のことを指しています.

ここで,Python複素数を扱ってみましょう.


>>> 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()メソッドによって求めることができます.

次に,複素行列\boldsymbol{B}=(B_ij)に対して, 以上のような共役複素数を要素とする(共役複素数で置き換えた)行列を\bar{\boldsymbol{B}}=(\bar{Bij}) と書きます. さらに,\bar{\boldsymbol{B}}の転置行列を随伴行列(共役転置行列)といい,

\boldsymbol{B}^\dagger=\boldsymbol{B}^{*t}=\boldsymbol{B}^*=\bar{\boldsymbol{B}}^T=\bar{\boldsymbol{B}^T}

と書きます.

以上で,随伴行列の表記をいくつかを示していますが, プログラミングであれば\(*\)は積演算のイメージがあるので\boldsymbol{B}^\daggerを用いているのかな~?と 思います. また,\(*\)を共役であることを示すものと捉えるか,転置まで行ったものと捉えるか,各分野で少し違うようです. 本記事では\boldsymbol{B}^\daggerを随伴行列とすることとします. 転置と共役のステップは順不同で,逆であっても随伴行列となります.


ちなみに,\daggerはダガ―と読む2項演算子です. \LaTeXの特殊記号で\dag\textdaggerがあり,それでも\daggerと同じような記号が出力されるはずですが, はてなブログではこちらは認識しないようですね...
では,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では複素数を使わないでしょうし,本記事では固有値分解に関しては実行列を対象に考えます. ただし,特異値分解に関しては複素行列を想定したサイトや本の説明が多いように感じましたので,例題等では複素行列で 行ってみたいと思います.

固有値分解か特異値分解かは,実数か複素数かではなく,正方か長方かの違いによるものだと考えるとよいかと思います.

行列を行列やベクトルに分割する方法

例えば,


\boldsymbol{A}=
\begin{pmatrix}
a_{11} & a_{12} |& a_{13}\\ 
a_{21} & a_{22} |& a_{23}\\ \hline
a_{31} & a_{32} & a_{33}
\end{pmatrix}

という行列を点線で分割すると,


\boldsymbol{A}=
\begin{pmatrix}
\boldsymbol{A_1} & \boldsymbol{a_1}\\
\boldsymbol{a_2}
\end{pmatrix}

\begin{cases}
\boldsymbol{A_1}=\begin{pmatrix}a_{11} & a_{12}\\a_{21} & a_{22}\end{pmatrix},
\boldsymbol{a_1}=\begin{pmatrix}a_{13} \\ a_{23}\end{pmatrix},
\boldsymbol{a_2}=\begin{pmatrix}a_{31} & a_{32} & a_{33}\end{pmatrix}
\end{cases}

という行列やベクトル(ベクトル)で分割できます. 分割の仕方は任意です.
ここで,分割して得られた行列やベクトルは元の行列に対して小行列といいます.
また,\boldsymbol{a_1}は縦ベクトル,\boldsymbol{a_2}は横ベクトルでもあります.

行列積

行列の積は左の行列の各行ベクトルと右の行列の各列ベクトルの内積です. そのため,左行列の列数と右行列の行数が一致している必要があります. 反対に左行列の行数と右行列の列数が異なっていても計算できます.

例えば,行列積は


\begin{pmatrix}a & b\\\\ c & d \\\\ e & f \end{pmatrix}
\cdot
\begin{pmatrix}g & h \\\\ i & j \end{pmatrix}
=
\begin{pmatrix}\boldsymbol{a1} \\\\ \boldsymbol{a2} \\\\ \boldsymbol{a3} \end{pmatrix}
\cdot
\begin{pmatrix}\boldsymbol{b1} & \boldsymbol{b2} \end{pmatrix}
=
\begin{pmatrix}
ag+bi & ah+bj\\\\
cg+di & ch+dj\\\\
eg+fi & eh+fj
\end{pmatrix}
=
\begin{pmatrix}
\boldsymbol{a1} \cdot \boldsymbol{b1} & \boldsymbol{a1} \cdot \boldsymbol{b2}\\\\
\boldsymbol{a2} \cdot \boldsymbol{b1} & \boldsymbol{a2} \cdot \boldsymbol{b2}\\\\
\boldsymbol{a3} \cdot \boldsymbol{b1} & \boldsymbol{a3} \cdot \boldsymbol{b2}
\end{pmatrix}

となります.
左行列のサイズがa \times b,右行列のサイズがc \times d のとき,計算して得られる行列のサイズはa \times dとなります.

転置行列とベクトルの内積

転置行列は行と列を入れ替えた行列であり,


\boldsymbol{A}=
\begin{pmatrix}
a_{11} \quad \cdots  \quad a_{1n}\\\\
\vdots \quad \ddots  \quad \vdots\\\\
a_{m1} \quad \cdots  \quad a_{mn}
\end{pmatrix}
=\begin{pmatrix}\boldsymbol{a}_1 & \boldsymbol{a}_2 & \cdots & \boldsymbol{a}_n \end{pmatrix}

という行列に対して,その転置行列は


\boldsymbol{A}^T=
\begin{pmatrix}
a_{11} & \cdots & a_{m1}\\\\
\vdots & \ddots & \vdots\\\\
a_{1n} & \cdots &  a_{mn}
\end{pmatrix}
=\begin{pmatrix}\boldsymbol{a}_1 \\\\ \boldsymbol{a}_2 \\\\ \vdots \\\\ \boldsymbol{a}_n \end{pmatrix}

と表せます.^t\boldsymbol{A}とも表記します.
転置行列について


\begin{cases}
{(\boldsymbol{A}^T)}^T=\boldsymbol{A}\\
{(\boldsymbol{A}\boldsymbol{B})}^T=\boldsymbol{B}^T\boldsymbol{A}^T\\
{(\boldsymbol{A}+\boldsymbol{B})}^T=\boldsymbol{A}^T+\boldsymbol{B}^T\\
{(k\boldsymbol{A})}^T=k\boldsymbol{A}^T\\
{(\boldsymbol{A}^T)}^{-1}={(\boldsymbol{A}^{-1})}^T\\
|\boldsymbol{A}^T|=|\boldsymbol{A}|
\end{cases}

という定理が成り立ちます.

また,ベクトルの内積は転置行列を用いることで行列の積として考えることができます. 例えば,


縦ベクトル\boldsymbol{a}=\begin{pmatrix}a_1 \\\\ a_2 \\\\ \vdots \\\\ a_n \end{pmatrix},
縦ベクトル\boldsymbol{b}=\begin{pmatrix}b_1 \\\\ b_2 \\\\ \vdots \\\\ b_n \end{pmatrix}

内積


\boldsymbol{a} \cdot \boldsymbol{b} = \;^t\boldsymbol{a} \cdot \boldsymbol{b}

というように一方のベクトルの転置をとることで行列の積となります.

逆行列

n次の正方行列に対して,


\boldsymbol{A}=
\begin{pmatrix}
a_{11} & \cdots & a_{1n}\\\\
\vdots & \ddots & \vdots\\\\
a_{m1} & \cdots & a_{mn}
\end{pmatrix}

その逆行列


\boldsymbol{A}^{-1}=\frac{adj\;\boldsymbol{A}}{det\;\boldsymbol{A}}=\frac{\tilde{\boldsymbol{A}}}{|\boldsymbol{A}|}

と表します.^{-1}は一乗と読まずにインバースと読むはず...
それはおいといて,adj\;\boldsymbol{A}/\tilde{A}は余因子行列,det\;\boldsymbol{A}/|\boldsymbol{A}|行列式といいます.

それぞれ,二次の場合については,


det\;\boldsymbol{A}=|\boldsymbol{A}|=
\begin{vmatrix}
a_{11} & a_{12}\\\\
a_{21} & a_{22}
\end{vmatrix}
=a_{11} \cdot a_{22} - a_{12} \cdot a_{21}

adj\;\boldsymbol{A}=adj\;\begin{pmatrix}a_{11} & a_{12} \\\\ a_{21} & a_{22}\end{pmatrix}
=\begin{pmatrix}a_{22} & -a_{12}\\\\ -a_{21} & a_{11}\end{pmatrix}

となります.
ここで,小行列というものについて述べておきます.
行列\boldsymbol{A}の小行列\boldsymbol{M}_{ij}は行列\boldsymbol{A}からi行とj列を取り除いた行列です. ちなみにi,j=1,2,\cdots,nです. 3次の正方行列に対する小行列をいくつか示すと,


\boldsymbol{M}_{11}=\begin{pmatrix}a_{22} & a_{23} \\\\ a_{32} & a_{33}\end{pmatrix}
\cdots
\boldsymbol{M}_{33}=\begin{pmatrix}a_{11} & a_{12} \\\\ a_{21} & a_{22}\end{pmatrix}

となります.

2次の行列式と余因子行列について以上で示しました. 以下より,小行列を用いて,3次以降についての行列式と余因子行列は


det\;\boldsymbol{A}=|\boldsymbol{A}|=\displaystyle\sum_{k=1}^{n} a_{ik} \cdot {(-1)}^{i+k}|\boldsymbol{M}_{ik}|=\displaystyle\sum_{k=1}^{n} a_{kj} \cdot {(-1)}^{k+j}|\boldsymbol{M}_{kj}|\\\\
adj\;\boldsymbol{A}\to要素\tilde{\boldsymbol{A}_{ij}}={(-1)}^{i+j}|\boldsymbol{M}_{ji}|

となります.

逆行列に関して,


\begin{cases}
{(\boldsymbol{A}^{-1})}^{-1}=\boldsymbol{A}\\
{(\boldsymbol{A}\boldsymbol{B})}^{-1}=\boldsymbol{B}^{-1}\boldsymbol{A}^{-1}
\end{cases}

という定理が成り立ちます.

対角行列

  • 対角行列\to長方対角行列と区別するのに,本記事では正方対角行列とする :行数と列数が等しく,左上から右下にかける対角成分のみ値を持っている行列
  • 長方対角行列 :正方対角行列に任意の行もしくは列に任意の数だけ零ベクトル(要素が全て零のベクトル)を連結した行列
  • ブロック対角行列 :正方対角行列の対角成分が値ではなく,正方行列となっている行列.

正方対角行列=
\begin{pmatrix}
a_{11} \quad \cdots  \quad \boldsymbol{0}\\\\
\vdots \quad \ddots  \quad \vdots\\\\
\boldsymbol{0} \quad \cdots  \quad a_{nn}
\end{pmatrix}

長方対角行列=
\begin{pmatrix}
a_{11} & \cdots  & \boldsymbol{0}\\\\
\vdots & \ddots  & \vdots\\\\
\boldsymbol{0} & \cdots  & a_{nn}\\\\
0 & 0 & 0
\end{pmatrix}
\quad or \quad
\begin{pmatrix}
a_{11} & \cdots  & \boldsymbol{0} & 0\\\\
\vdots & \ddots  & \vdots & 0\\\\
\boldsymbol{0} & \cdots  & a_{nn} & 0\\\\
\end{pmatrix}

ブロック対角行列=
\begin{pmatrix}
\boldsymbol{A_{11}} \quad \cdots  \quad \boldsymbol{0}\\\\
\vdots \quad \ddots  \quad \vdots\\\\
\boldsymbol{0} \quad \cdots  \quad \boldsymbol{A_{nn}}
\end{pmatrix}


正方対角行列において,そのn乗は


\boldsymbol{A}^{n}=
\begin{pmatrix}
a_{11}^n \quad \cdots  \quad \boldsymbol{0}\\\\
\vdots \quad \ddots  \quad \vdots\\\\
\boldsymbol{0} \quad \cdots  \quad a_{nn}^n
\end{pmatrix}
\quad
n:任意の整数

となります. つまり,正方対角行列はその対角成分の各要素をn乗すればよいということです. かなり計算がラクです. 2次正方対角行列とかで逆行列を考えると,以上のようになることが分かると思います.

固有値分解 Eigendecomposition(Eigen Value Decomposition :EVD)

対象:正方行列

n次)正方行列はn\times nつまりnn列というような,行数と列数が一致している行列のことです. nは任意の整数値です.

正則行列逆行列をもつ行列,または行列式0でない行列のことです. 反対に逆行列をもたない行列,または行列式0である行列は特異行列と呼ばれます.

固有値固有ベクトル

正方行列\boldsymbol{A}

$$ \boldsymbol{A}\boldsymbol{P}_i=\lambda_i\boldsymbol{P}_i $$

を満たすとき,

といいます. 固有値との数は行列のサイズによって異なります. 固有値には各固有値に対応した固有ベクトルがあり,固有ベクトル固有値の数だけ存在します. i固有値固有ベクトルの数です.

固有方程式(+単位行列

\(\lambda_i\boldsymbol{P}_i=\lambda_i\boldsymbol{E}\boldsymbol{P}_i\)であることより,

$$ (\lambda_i\boldsymbol{E}-\boldsymbol{A})\boldsymbol{P}_i=0 \quad (\boldsymbol{P}_i\neq\boldsymbol{0}) $$

が得られます.以上の式が自明な解以外を解にもつための必要十分条件より,

$$ det(\lambda_i\boldsymbol{E}-\boldsymbol{A})= \begin{vmatrix}\lambda_i\boldsymbol{E}-\boldsymbol{A}\end{vmatrix}=0 $$

という固有値を求める特性方程式(固有方程式)が得られます.

以上において,行列\boldsymbol{E}単位行列であり,

$$ \boldsymbol{E}= \begin{pmatrix} 1 \quad \cdots \quad \boldsymbol{0}\\\\ \vdots \quad \ddots \quad \vdots\\\\ \boldsymbol{0} \quad \cdots \quad1 \end{pmatrix} $$

という,対角成分が1の行列です. 単位行列に関して,

  • \boldsymbol{A}\boldsymbol{E}=\boldsymbol{A},\boldsymbol{E}\boldsymbol{A}=\boldsymbol{A}
  • \boldsymbol{A}\boldsymbol{A}^{-1}=\boldsymbol{A}^{-1}\boldsymbol{A}=\boldsymbol{E}

という性質が成り立ちます.

一般化固有ベクトル

以上に対し,

$$ (\lambda_i\boldsymbol{E}-\boldsymbol{A})^m \boldsymbol{P}_i=0 \quad (\boldsymbol{P}_i\neq\boldsymbol{0},m:正の整数) $$

を満たす\boldsymbol{P}_iを一般化固有ベクトルというそうです. LSAを使うにはここまで理解してなくてもいいと思いますが...

固有ベクトルと例題

以上から

$$ (\lambda_i\boldsymbol{E}-\boldsymbol{A})\boldsymbol{P_i}=(\lambda_i\boldsymbol{E}-\boldsymbol{A}) \begin{pmatrix} P_{i1}\\P{i2}\\ \vdots \\ P{ik} \end{pmatrix} =0 $$

となり, ある固有値を代入して計算することである固有値に対する固有ベクトルが得られます. k\boldsymbol{A}の列数です.

例として,

$$ \boldsymbol{A}= \begin{pmatrix} 2 & 3 \\ 1 & 4 \end{pmatrix} $$

固有値固有ベクトルを求めてみましょう. \lambda\boldsymbol{A}固有値として,固有方程式を解くと,

$$ det(\lambda\boldsymbol{E}-\boldsymbol{A})= \begin{vmatrix}\lambda\boldsymbol{E}-\boldsymbol{A}\end{vmatrix}=0\\ \begin{vmatrix} \lambda - 2 & -3\\ -1 & \lambda - 4 \end{vmatrix} =0\\ (\lambda - 2)(\lambda - 4)-3=0\\ \lambda^2 - 6\lambda +8-3=0\\ \lambda^2-6\lambda+5=0\\ (\lambda -5)(\lambda -1)=0 $$

より,\boldsymbol{A}固有値\lambda = 5,1と分かりました.
ここで,

$$ (\lambda\boldsymbol{E}-\boldsymbol{A})\boldsymbol{P}=0 $$

に\(\lambda = 5,1\)を代入することで,\(\boldsymbol{A}\)の固有ベクトル\(\boldsymbol{P}\)が求まります.
まず,固有値\(\lambda_1=5\)の固有ベクトルを求めましょう.
ここで,\(P_{11},P_{12}\)は任意の定数とします. 任意の定数なので,固有ベクトルが整数値の方が手計算したりするのにラクなので, 固有ベクトルが整数値になるように\(P_{11},P_{12}\)は任意で決めていいです. 他の固有ベクトルについても同様です.
固有値5を上式に代入すると,

$$ (\lambda\boldsymbol{E}-\boldsymbol{A})\boldsymbol{P}=0\\ \begin{pmatrix} 5-2 & -3\\ -1 & 5-4 \end{pmatrix} \begin{pmatrix} P_{11}\\ P_{12} \end{pmatrix} =0\\ \begin{pmatrix} 3 & -3\\ -1 & 1 \end{pmatrix} \begin{pmatrix} P_{11}\\ P_{12} \end{pmatrix} =0\\ \begin{pmatrix} 3P_{11}-3P_{12}\\ -P_{11}+P_{12} \end{pmatrix} =0 $$

となり,

$$ \begin{cases} P_{11}-P{12}=0\\ -P_{11}+P_{12}=0 \end{cases} $$

という連立方程式が得られ,上式より,

$$ P_{11}=P_{12} $$

が得られます.以上から,

$$ P_1= \begin{pmatrix} P_{11}\\ P_{12} \end{pmatrix} = \begin{pmatrix} P_{11}\\ P_{11} \end{pmatrix} =P_{11} \begin{pmatrix} 1\\ 1 \end{pmatrix} $$

となります.

\(P_{11},P_{12}\)は任意の定数としたので,固有ベクトルが小さい整数値となるように, \(P_{11}=P_{12}=1\)とすると,固有値5固有ベクトル

$$ P_1= \begin{pmatrix} 1\\ 1 \end{pmatrix} $$

次に,固有値\lambda_2=1固有ベクトルを求めましょう.

ここで,\(P_{21},P_{22}\)を任意の定数とします.

固有値5と同様に式に代入すると,

$$ (\lambda\boldsymbol{E}-\boldsymbol{A})\boldsymbol{P}=0\\ \begin{pmatrix} 1-2 & -3\\ -1 & 1-4 \end{pmatrix} \begin{pmatrix} P_{21}\\ P_{22} \end{pmatrix} =0\\ \begin{pmatrix} -1 & -3\\ -1 & -3 \end{pmatrix} \begin{pmatrix} P_{21}\\ P_{22} \end{pmatrix} =0\\ -P_{21}-3P_{22}=0\\ P_{21}=-3P_{22} $$

となり,以上より,

$$ P_2= \begin{pmatrix} P_{21}\\ P_{22} \end{pmatrix} = \begin{pmatrix} -3P_{22}\\ P_{22} \end{pmatrix} =P_{22} \begin{pmatrix} -3\\ 1 \end{pmatrix} $$

となります.

ここで,\(P_{22}=1\)とすると,固有値1固有ベクトル

$$ P_2= \begin{pmatrix} -3\\ 1 \end{pmatrix} $$

となります.

ここで,以上を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でも固有値固有ベクトルが求められました.

対角化

後々のために,固有値を降順にしておいたほうがいいですね.
ここで,


\boldsymbol{P}=(固有ベクトルを並べたもの)=
\begin{pmatrix}
\boldsymbol{P}_1,\boldsymbol{P}_2, \cdots, \boldsymbol{P}_i 
\end{pmatrix}

とすると,

$$ \boldsymbol{A}を対角化した行列 =\boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P}= \begin{pmatrix} \lambda_1 & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & \lambda_i \end{pmatrix} $$

固有値分解

以上の対角化の式の両辺に,右から\boldsymbol{P}を,左から\boldsymbol{P}^{-1}をかけると,

$$ \boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P}= \begin{pmatrix} \lambda_1 & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & \lambda_i \end{pmatrix} \\ \boldsymbol{A}= \boldsymbol{P} \begin{pmatrix} \lambda_1 & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & \lambda_i \end{pmatrix} \boldsymbol{P}^{-1} $$

となり,\boldsymbol{A}を行列の積で分解することができました. これが固有値分解というわけです.

以上を

$$ \boldsymbol{A}= \begin{pmatrix} 2 & 3 \\ 1 & 4 \end{pmatrix} $$

という先の例題と同じ行列を使って,固有値分解して得られる各行列を求め, さらに,検算として行列を掛け合わせたら元の行列\boldsymbol{A}に戻るかも 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.]])

固有値分解後の行列で行列演算を行うと,ちゃんと元通りの行列になっていることが分かります. また,以上では,固有値ならびに固有ベクトルを降順に並び替える手順も行っています.

おまけ:\boldsymbol{A}^nの求め方

対角化の式の両辺をn乗して変形していくと,

$$ \boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P}= \begin{pmatrix} \lambda_1 & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & \lambda_i \end{pmatrix} $$
$$ (\boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P})^n= \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} $$
$$ (\boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P})(\boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P})(\boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P})\cdots(\boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{P})= \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} $$
\[ \boldsymbol{P}^{-1}\boldsymbol{A}(\boldsymbol{P}\boldsymbol{P}^{-1})\boldsymbol{A}(\boldsymbol{P}\boldsymbol{P}^{-1})\boldsymbol{A}(\boldsymbol{P}\cdots\boldsymbol{P}^{-1})\boldsymbol{A}\boldsymbol{P}= \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} \]
$$ \boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{E}\boldsymbol{A}\boldsymbol{E}\boldsymbol{A}\boldsymbol{E}\cdots\boldsymbol{E}\boldsymbol{A}\boldsymbol{P}= \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} $$
$$ \boldsymbol{P}^{-1}\boldsymbol{A}\boldsymbol{A}\boldsymbol{A}\cdots\boldsymbol{A}\boldsymbol{P}= \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} $$
$$ \boldsymbol{P}^{-1}\boldsymbol{A}^n\boldsymbol{P}= \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} $$
$$ \boldsymbol{A}^n=\boldsymbol{P} \begin{pmatrix} (\lambda_1)^n & \cdots & \boldsymbol{0}\\\\ \vdots & \ddots & \vdots\\\\ \boldsymbol{0} & \cdots & (\lambda_i)^n \end{pmatrix} \boldsymbol{P}^{-1} $$

以上より,\boldsymbol{A}^nの算出式が求められました.

特異値分解 Singular Value Decomposition :SVD

対象:長方行列

固有値分解は正方行列に対して適用できました. 対して,特異値分解は非正方行列のための行列分解の理論です.

非正方つまり長方行列はmn列の行列で,通常m \neq nです. m=nな長方行列が正方行列,つまり,長方行列の特殊バージョンが正方行列であるといえますね.

直交行列(実数)とユニタリ行列(複素数

実行列において,

\boldsymbol{B}\boldsymbol{B}^T=\boldsymbol{B}^T\boldsymbol{B}=\boldsymbol{E}

を満たす行列\boldsymbol{B}を直交行列と言います.
以上の式について,何か見覚えがありませんか? 単位行列の性質として示した

\boldsymbol{B}\boldsymbol{B}^{-1}=\boldsymbol{B}^{-1}\boldsymbol{B}=\boldsymbol{E}

と同じ形ですね. つまり,\boldsymbol{B}が正則ならば,

\boldsymbol{B}^T=\boldsymbol{B}^{-1}

もまた,\boldsymbol{B}が直交行列である条件式であるわけですね.

ユニタリ行列は直交行列の転置行列の部分を随伴行列に置き換えた式を満たす複素行列のことです.つまり,


\begin{cases}
\boldsymbol{B}\boldsymbol{B}^\dagger=\boldsymbol{B}^\dagger\boldsymbol{B}=\boldsymbol{E}\\\\
\boldsymbol{B}^\dagger=\boldsymbol{B}^{-1}
\end{cases}

を満たす複素行列がユニタリ行列です. ある複素行列とその複素行列の随伴行列の行列積が実行列である単位行列となるとき, その複素行列はユニタリ行列というわけです. ユニタリ行列は実行列である直交行列を複素数に拡張したもの,もしくはユニタリ行列の実数値版が直交行列である といえます.

また,


\boldsymbol{B}\boldsymbol{B}^\dagger=\boldsymbol{B}^\dagger\boldsymbol{B}

を満たす複素行列\boldsymbol{B}を正規行列と言います. ユニタリ行列は正規行列の一種というわけです.

特異値,特異ベクトル

固有値固有ベクトルは正方行列に対して,

$$ \boldsymbol{A}\boldsymbol{P}_i=\lambda_i\boldsymbol{P}_i $$

より定義されました.
特異値\sigmaと特異ベクトルu,vはサイズがm \times n,つまりmn列の行列\boldsymbol{X}を用いて,


\begin{cases}
\boldsymbol{X} \boldsymbol{v}_i=\sigma_i\boldsymbol{u}_i\\\\
\boldsymbol{X}^\dagger \boldsymbol{u}_i=\sigma_i\boldsymbol{v}_i
\end{cases}

を満たすものとして定義されます. 以上において,

  • \sigma_i:非負実数値
  • u_i:左特異ベクトル\to m次元
  • v_i:右特異ベクトル\to n次元

となっています.
また,\boldsymbol{X}が実行列ならば\boldsymbol{X}^\dagger\boldsymbol{X}^T,つまり,ただの転置行列です.

ここで,\(\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{X} \boldsymbol{X} =\boldsymbol{X}^\dagger (左を満たす行列をエルミート行列という)で正方行列
  • 左特異ベクトルと右特異ベクトルが等価

のとき,固有値の式\(\boldsymbol{A}\boldsymbol{P}_i=\lambda_i\boldsymbol{P}_i\)になりますよね. つまり,ここで,特異値分解固有値分解の拡張理論である,もしくは,固有値分解は特異値分解の特殊パターンであると 考えられますよね.

対角化

正方正規行列\boldsymbol{C}に対し,


\boldsymbol{P}=(\boldsymbol{C}の固有ベクトルを並べたもの)=
\begin{pmatrix}
\boldsymbol{P}_1,\boldsymbol{P}_2, \cdots, \boldsymbol{P}_i 
\end{pmatrix}

とすると,


\boldsymbol{C}を対角化した行列
=\boldsymbol{P}^{\dagger}\boldsymbol{C}\boldsymbol{P}=
\begin{pmatrix}
\lambda_1 & \cdots  & \boldsymbol{0}\\\\
\vdots & \ddots  & \vdots\\\\
\boldsymbol{0} & \cdots  & \lambda_i
\end{pmatrix}

となります. このとき,\boldsymbol{P}はユニタリ行列です. また,固有値を求めるので,\boldsymbol{C}は正方でなければなりませんね.

以上において,mn列の行列\boldsymbol{X}は(正方な)対角化はできないわけですが, \boldsymbol{X}\boldsymbol{X}^\dagger\boldsymbol{X}^\dagger\boldsymbol{X}は対角化できるわけです. ここで,


\begin{cases}
左特異行列\boldsymbol{U}=(\boldsymbol{X}\boldsymbol{X}^\daggerの固有ベクトルを並べたもの)=
\begin{pmatrix}
\boldsymbol{u}_1,\boldsymbol{u}_2, \cdots, \boldsymbol{u}_ i
\end{pmatrix}\\\\
右特異行列\boldsymbol{V}=(\boldsymbol{X}^\dagger\boldsymbol{X}の固有ベクトルを並べたもの)=
\begin{pmatrix}
\boldsymbol{v}_1,\boldsymbol{v}_2, \cdots, \boldsymbol{v}_i 
\end{pmatrix}
\end{cases}

であるので,


\boldsymbol{X}\boldsymbol{X}^\daggerを対角化した行列\boldsymbol{D_U}
=\boldsymbol{U}^{\dagger} \boldsymbol{X}\boldsymbol{X}^\dagger \boldsymbol{U}=
\begin{pmatrix}
\lambda_1 & \cdots  & \boldsymbol{0}\\\\
\vdots & \ddots  & \vdots\\\\
\boldsymbol{0} & \cdots  & \lambda_i
\end{pmatrix}

\boldsymbol{X}^\dagger\boldsymbol{X}を対角化した行列\boldsymbol{D_V}
=\boldsymbol{V}^{\dagger} \boldsymbol{X}^\dagger\boldsymbol{X} \boldsymbol{V}=
\begin{pmatrix}
\lambda_1 & \cdots  & \boldsymbol{0}\\\\
\vdots & \ddots  & \vdots\\\\
\boldsymbol{0} & \cdots  & \lambda_i
\end{pmatrix}

と対角化できます.

特異値分解

いくつかサイトを見たんですが,特異値分解の式の導出(証明)がイマイチ私には理解できなかったので, Pythonで代数演算もしくは数値を代入した演算を行い,特異値分解の式が正しいことを感覚的に納得しようかな~と思います. Pythonでは代数演算を”Sympy”というライブラリで行うことができ, 行列演算もこれを用いてできます.

mn列の行列\boldsymbol{X}に対し,

\boldsymbol{X}=\boldsymbol{U}\boldsymbol{S}\boldsymbol{V}^\dagger

固有値と特異値

\boldsymbol{X}\boldsymbol{X}^\dagger固有値\boldsymbol{X}^\dagger\boldsymbol{X}固有値は等しく, その固有値平方根\boldsymbol{X}の特異値ということでした. このことを代数演算で感覚的に確からしいことを確認したいと思います.

まずは,固有値分解できる正方行列でこのことを試してみます. そこで,


\boldsymbol{X}=
\begin{pmatrix}
a & b\\\\
c & d
\end{pmatrix}

として,計算しましょう.


>>> 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:\boldsymbol{X}の特異値と\boldsymbol{X}^\dagger\boldsymbol{X}固有値の数はどちらもn\boldsymbol{X}\boldsymbol{X}^\dagger固有値0が含まれ,その数はm
  • m=n:\boldsymbol{X}の特異値と\boldsymbol{X}\boldsymbol{X}^\dagger固有値\boldsymbol{X}^\dagger\boldsymbol{X}固有値の数は全てm(もしくはn
  • m<n:\boldsymbol{X}の特異値と\boldsymbol{X}\boldsymbol{X}^\dagger固有値には0が含まれ, その数はどちらもn\boldsymbol{X}^\dagger\boldsymbol{X}固有値の数はm

と分かります.

また,正方行列において,固有値と特異値は非等価ですが,なんとなく似た数値になっているかと思います. 特異値は固有値と似たような役割を持つものではないのかなぁと考えられます. このことから,固有値が求められない場合に,代わりに特異値を用いる,それが特異値分解ということなのかなと.

Python特異値分解をしてみよう

参考[1]の特異値分解の例題を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.]])

特異値分解の式

mn列の行列\boldsymbol{X}に対し,

\boldsymbol{X}=\boldsymbol{U}\boldsymbol{S}\boldsymbol{V}^\dagger

以上において,左特異行列\boldsymbol{U},右特異行列\boldsymbol{V}\boldsymbol{S}は対角成分が特異値の対角行列です. また,

  • 左特異行列\boldsymbol{U}m次ユニタリ行列
  • 右特異行列\boldsymbol{V}n次ユニタリ行列
  • 特異値行列\boldsymbol{S}:対角成分が特異値
    • m > n:mn列の長方対角行列=\begin{pmatrix} n次正方対角行列 \\ n次零ベクトル \end{pmatrix}
    • m = n:m or n次正方対角行列
    • m < n:nm列の長方対角行列=\begin{pmatrix} m次正方対角行列 \quad m次零ベクトル \end{pmatrix}

となっています.

参考

  1. 科学者・技術者のための 基礎線形代数と固有値問題

    科学者・技術者のための 基礎線形代数と固有値問題

    • 作者:柴田 正和
    • 出版社/メーカー: 森北出版
    • 発売日: 2013/12/20
    • メディア: 単行本(ソフトカバー)
  2. 線形代数の基礎

    線形代数の基礎

  3. 固有値分解 - Wikipedia
  4. 特異値分解 - Wikipedia
  5. 特異値 - Wikipedia
  6. Pythonで特異値分解(SVD)を理解する - け日記
  7. 特異値分解の定義,性質,具体例 | 高校数学の美しい物語
  8. NumPyで行列の固有値、固有ベクトルを求めるlinalg.eig関数の使い方 - DeepAge
  9. NumPyで逆行列を求めるlinalg.invの使い方 - DeepAge
  10. NumPy配列ndarrayの対角成分の抽出、対角行列の作成(diag, diagonal) | note.nkmk.me
  11. http://www.cfme.chiba-u.jp/~haneishi/class/iyogazokougaku/SVD.pdf
  12. NumPyの配列ndarrayの欠損値np.nanを他の値に置換 | note.nkmk.me
  13. [Pythonによる科学・技術計算] numpy・scipyを用いた(一般化)固有値問題の解法,ライブラリ利用 - Qiita
  14. scipy.linalg.eig — SciPy v1.3.3 Reference Guide
  15. SymPyで代数演算してみる - Qiita
  16. sympyで線型代数学の問題を解く - My Notes
  17. Pythonを使って一瞬で連立方程式を解く - Qiita
  18. 複素数型 (実部・虚部・偏角)
  19. 共役転置と随伴行列│Python 数値計算入門
  20. https://algorithm.joho.info/programming/python/numpy-sort/
  21. numpy.ndarrayの基礎 (zeros / ones / empty) | Python-izm
  22. NumPyで行列式と逆行列を計算する - MathPython
  23. 大学1年生もバッチリ分かる線形代数入門

レイアウト等について

  1. Mathjaxの使い方を覚えて美しい数式表示をしてみよう! - はるなぴログ
  2. はてなブログでベクトルをbold表記したい[markdown] - 逆さまにした
  3. はてなブログのMarkdown記法等 - tak0kadaの何でもノート
  4. Markdown記法でコメントアウトする - Qiita
  5. SVD (singular value decomposition) に入門する - Qiita
  6. 【CSS】オーバーフローしたコンテンツの表示 - Qiita
  7. スタイルシート[CSS]/ボックス/ボックスからはみ出た部分の表示方法を指定する - TAG index
  8. https://hoshimi12.com/?p=12552

Pythonについて(他に記事でも参照)

  1. 実践力を身につける Pythonの教科書

    実践力を身につける Pythonの教科書

行列・ベクトルの表記

\mathcal{x} 以上は\mathcal{x}\LaTeXの数式入力した結果で,筆記体でxを書いたものです. しかし,これではxがベクトルであることが伝わらない... ということで,ググってトップに出てきたブログ記事にやり方が書いてありました.

以上のサイトのやり方を本文でも使用させていただきました.