特異値分解のアルゴリズムと次元圧縮
ー MATLAB 版
  
早稲田大学教育・総合科学学術院 新井仁之
  
今回は特異値分解を,MATLABを使いながら学んでいく.このノートではエルミート行列の対角化は既習として,特異値標準化・特異値分解の証明(= アルゴリズム)の一つをプログラミングしながら学んでいく.特異値分解の応用として画像データの次元圧縮についても述べる.
  
1.準備
基本的な記法を設定しておく.
ℝ を実数全体のなす集合,ℂ を複素数全体からなる集合とし,𝕂 は ℝ または ℂ を表す.𝕂 はスカラー体,𝕂 の要素はスカラーと呼ばれる.   
N次元縦ベクトル  
  (ただし
 (ただし  )全体からなる集合を
)全体からなる集合を  で表す.これにはスカラー積と和が定義され,N次元線形空間をなしている(説明は既知として省くが,たとえば [1] 参照).
 で表す.これにはスカラー積と和が定義され,N次元線形空間をなしている(説明は既知として省くが,たとえば [1] 参照).   には次のエルミート内積(あるいはユークリッド内積)が定義される.
 には次のエルミート内積(あるいはユークリッド内積)が定義される. また  を
 を  のユークリッド・ノルムあるいは長さという.以下,特に混乱のない場合は
 のユークリッド・ノルムあるいは長さという.以下,特に混乱のない場合は  ,
,
と略記する.
M行N列の行列とは
 である.このような行列全体のなす集合を本ノートでは 
で表すことにする.各成分の複素共役をとり転置したものを  で表し.A の共役行列という.
 で表し.A の共役行列という. である. は N行M列の行列になっている.
 は N行M列の行列になっている. MATLABでは A' で実行できる.たとえば
A = randi([1 10],2,3)+1i*randi([1 10],2,3);
disp(char('A =', num2str(A)));
A =                    
 9+3i    2+10i    7+2i 
10+6i   10+10i    1+10i
disp(char('A^* =', num2str(A')));
A^* =         
9-3i    10-6i 
2-10i   10-10i
7-2i     1-10i
   
2.エルミート行列の対角化
特異値分解で使うので,エルミート行列の対角化について概説する.
A が N行N列の行列(N次正方行列)で,特に
をみたすものをエルミート行列という.また N次正方行列で
 (ただし
 (ただし  は単位行列)
 は単位行列)をみたすものをユニタリー行列という.
N 次正方行列 A を考える.複素数 λ に対する方程式  が非自明解を持つとする.すなわち,
が非自明解を持つとする.すなわち, ある  で,
 で, をみたすものが存在するとする.このとき  λ を A の固有値,
 をみたすものが存在するとする.このとき  λ を A の固有値, を λ に属する固有ベクトルという.λ に属する k 個の線形独立な固有ベクトルが存在するとき,λ の重複度は k であるという.このとき,この固有値の重複度を込めた個数は k 個であるともいう.
 を λ に属する固有ベクトルという.λ に属する k 個の線形独立な固有ベクトルが存在するとき,λ の重複度は k であるという.このとき,この固有値の重複度を込めた個数は k 個であるともいう. の場合,代数学の基本定理から,一般のN 次正方行列 A は重複度も込めて N 個の固有値を持つことが知られている([1]参照).
 の場合,代数学の基本定理から,一般のN 次正方行列 A は重複度も込めて N 個の固有値を持つことが知られている([1]参照). また,エルミート行列の固有値はすべて実数であることも知られている([1]参照). 
特異値分解では次の定理を使っていく.
定理1 (エルミート行列の対角化) A を N 次正方行列で,エルミート行列であるとする.A の重複度も込めた N 個の固有値を  とする.このとき,ある N 次 ユニタリー行列 U を
 とする.このとき,ある N 次 ユニタリー行列 U を をみたすようにとれる.
本ノートではエルミート行列の対角化の証明はしない.これについては通常の大学1年の線形代数のコースで学ぶ.たとえば [1] に詳しい証明が記されているので,興味のある読者は参照してほしい.
また,MATLAB では次のように eig コマンドを使ってエルミート行列の対角化ができるので,このコマンドを使っていくことにする.
eig を使って具体例でエルミート行列の対角化を行う.
まずエルミート行列をランダムに作る.
B = randi([0 9],N,N)+1i*randi([0 9],N,N);
A = B+B'; %これは作り方から明らかにエルミート行列
disp(char('A =', num2str(A)));
A =                    
18+0i     5-2i    15-6i
 5+2i     8+0i    18-1i
15+6i    18+1i    12+0i
disp(char('ユニタリー行列 U =', num2str(U)));
ユニタリー行列 U =                                                         
 0.27553-0.1016i         -0.67776+0.3129i          0.55239-0.22662i 
 0.62557-0.041931i        0.61793-0.069162i        0.46887-0.021327i
-0.72158-0i               0.23687+0i               0.65055+0i       
disp(char('対角行列 D =', num2str(D)));
対角行列 D =                          
-10.2355            0            0
       0      8.40311            0
       0            0      39.8324
検算:
まず U がユニタリー行列であることの確認.
C1 = sum(abs(U*U'-eye(3)),'all');
disp(char('|UU*-I|=', num2str(C1)));
次に対角化の確認
C2 = sum(abs(U'*A*U-D),'all');
disp(char('|U*AU-D|=', num2str(C1)));
  
3.特異値標準化
 とする.これに対する特異値標準化について説明していく.
 とする.これに対する特異値標準化について説明していく.
特異値標準化のアルゴリズム
 は N 次正方行列になっている.さらに
 は N 次正方行列になっている.さらに  よりエルミート行列である.
 よりエルミート行列である. の固有値は実数であるが,さらにこのタイプの行列の固有値は非負であることが示せる.そこでその固有値を
 の固有値は実数であるが,さらにこのタイプの行列の固有値は非負であることが示せる.そこでその固有値をと大きい順に並べる.
 (
  ( )
)とおき,これを A の特異値という.
さて, は N 次のエルミート行列であるから,定理1より N 次ユニタリー行列 V で,
 は N 次のエルミート行列であるから,定理1より N 次ユニタリー行列 V で, を対角化できるものがとれる.
 を対角化できるものがとれる. とおき,
 とおき,  (
 ( )
)と定める.このとき  が
 が  の正規直交系であることが証明できる.
 の正規直交系であることが証明できる. であるが,特に
 であるが,特に  の場合には,適当に
 の場合には,適当に  を追加して,
 を追加して, が
 が   の正規直交基底になるようにできる.
 の正規直交基底になるようにできる. 具体的にどのようなベクトルを追加すればよいか?
そのプログラムは次のように書ける.
Lemma.  足りない正規直交系を補完して正規直交基底にするアルゴリズム
% Q は Nxp 配列,ただし N>=p (行数 >= 列数)である必要がある.
% Q(:,1),...,Q(:,p) は正規直交系になるようにしておく.
% A は NxN 配列で,A(:,1),...,A(:,N)は正規直交基底であり,かつ A(:,k)=Q(:,k), 1<=k<=p 
% Q は正規直交系でない場合,Q の部分も合わせて直交化される.
    disp('入力配列の列数が行数を超えてます.')
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Q = GramSchmidt(A)
        v = v - (Q(:, j)' * A(:, i)) * Q(:, j);
試用例
 で試してみる.まず二つの線形独立なベクトルを適当に作る
 で試してみる.まず二つの線形独立なベクトルを適当に作るv1 = [1 2 3 4]; v2 = [2 3 4 5];
グラム-シュミットでこれを正規直交系にする.
disp(char('与えた正規直交系', num2str(Q)));
与えた正規直交系           
0.18257      0.8165
0.36515     0.40825
0.54772   5.439e-16
 0.7303    -0.40825
Q の正規直交性を確認
Q'*Q
    1.0000    0.0000
    0.0000    1.0000
Q を補完して, の正規直交基底を作る.
 の正規直交基底を作る. disp(char('Xの最初の二つは与えられた正規直交系と一致してることの確認', num2str(X(:,1:2))));
Xの最初の二つは与えられた正規直交系と一致してることの確認
0.18257      0.8165          
0.36515     0.40825          
0.54772 -3.6825e-16          
 0.7303    -0.40825          
X の正規直交性を確認
X'*X
    1.0000         0   -0.0000   -0.0000
         0    1.0000    0.0000    0.0000
   -0.0000    0.0000    1.0000   -0.0000
   -0.0000    0.0000   -0.0000    1.0000
disp(char('よって求める残りのベクトルは', num2str(X(:,3:4))));
よって求める残りのベクトルは      
 0.54772  1.3597e-16
 -0.7303     0.40825
-0.18257     -0.8165
 0.36515     0.40825
特異値標準形を与えるアルゴリズム
以上の Lemma を使って特異値標準形を与えるアルゴリズムを記す.このアルゴリズム自体は線形代数でよく知られたものである.(たとえば [1] 参照.)
function [Sigma, U, W]=SingNormal(A)
% Version 1.1 (July 6, 2024) by Hitoshi Arai
% Sigma は A の特異値を並べた min(M,N)x1 配列
% U,W は U'*A*W が特異値標準形を与える MxM 及び NxN ユニタリー行列
% エルミート行列の対角化の部分でMATLABの eig は使用している.
[Sigma,p] = sort(Sigma,'descend');
    U1(:,k)=Sigma(k)^(-1)*A*VV(:,k);
特異値分解の検算
まず適当に 4x5行列を与える.ただし rank≤3.
A(:,5)=A(:,2)
     7     0     9     9     0
     0     8     0     0     8
     2     6     4     4     6
     0     3     3     3     3
[Sigma,U,V] = SingNormal(A)
  17.5956 + 0.0000i
  13.0340 + 0.0000i
   1.8733 + 0.0000i
   0.0000 + 0.0000i
    0.6632   -0.6625    0.2670    0.2235
    0.3803    0.6974    0.4115    0.4469
    0.5593    0.2556   -0.1008   -0.7821
    0.3203    0.0974   -0.8656    0.3724
    0.3274   -0.3166    0.8903   -0.0000    0.0000
    0.4183    0.5681    0.0482   -0.6967    0.1209
    0.5210   -0.3566   -0.3184    0.1209    0.6967
    0.5210   -0.3566   -0.3184   -0.1209   -0.6967
    0.4183    0.5681    0.0482    0.6967   -0.1209
Sigma %特異値
  17.5956 + 0.0000i
  13.0340 + 0.0000i
   1.8733 + 0.0000i
   0.0000 + 0.0000i
U'*A*V %特異値標準形
   17.5956   -0.0000    0.0000    0.0000   -0.0000
   -0.0000   13.0340   -0.0000   -0.0000   -0.0000
   -0.0000   -0.0000    1.8733   -0.0000    0.0000
   -0.0000    0.0000   -0.0000   -0.0000   -0.0000
5x4 行列の場合の特異値分解の検証,
B=A' % 5x4行列
     7     0     2     0
     0     8     6     3
     9     0     4     3
     9     0     4     3
     0     8     6     3
[Sigma2,U2,V2] = SingNormal(B)
  17.5956 + 0.0000i
  13.0340 + 0.0000i
   1.8733 + 0.0000i
   0.0000 + 0.0000i
    0.3274   -0.3166    0.8903   -0.0000    0.0000
    0.4183    0.5681    0.0482   -0.6967    0.1209
    0.5210   -0.3566   -0.3184    0.1209    0.6967
    0.5210   -0.3566   -0.3184   -0.1209   -0.6967
    0.4183    0.5681    0.0482    0.6967   -0.1209
    0.6632   -0.6625    0.2670    0.2235
    0.3803    0.6974    0.4115    0.4469
    0.5593    0.2556   -0.1008   -0.7821
    0.3203    0.0974   -0.8656    0.3724
U2'*B*V2
   17.5956    0.0000   -0.0000   -0.0000
   -0.0000   13.0340   -0.0000    0.0000
    0.0000   -0.0000    1.8733   -0.0000
    0.0000   -0.0000   -0.0000    0.0000
   -0.0000   -0.0000    0.0000    0.0000
   
MATLABの svd との比較
MATLABに装備されている svd との比較をしておく.なお,前述の SingNormal は特異値標準化定理の証明([1])を再現した(特異値標準化定理の証明を学ぶための)プログラムで,高速化は考慮していないことに注意.
[U_matlab,S_matlab,V_matlab]=svd(A)
   -0.6632    0.6625    0.2670    0.2235
   -0.3803   -0.6974    0.4115    0.4469
   -0.5593   -0.2556   -0.1008   -0.7821
   -0.3203   -0.0974   -0.8656    0.3724
   17.5956         0         0         0         0
         0   13.0340         0         0         0
         0         0    1.8733         0         0
         0         0         0    0.0000         0
   -0.3274    0.3166    0.8903   -0.0000    0.0000
   -0.4183   -0.5681    0.0482    0.6459   -0.2878
   -0.5210    0.3566   -0.3184    0.2878    0.6459
   -0.5210    0.3566   -0.3184   -0.2878   -0.6459
   -0.4183   -0.5681    0.0482   -0.6459    0.2878
sum(abs(U_matlab'*A*V_matlab - U'*A*V),'all')
計算時間の比較:1000x2000行列でテスト.
% TestMatrix = rand(1000,2000);
% save('TestMatrix','TestMatrix');
[S,U,V] = SingNormal(TestMatrix);
[U_matlab,S_matlab,V_matlab]=svd(TestMatrix);
コメント:SingNormal は特異値標準化定理の証明を理解するためのもので,高速化していないから,MATLABの svd の方が断然早い.実際に使う場合は MATLAB の svd がよい.
  
4.画像の特異値分解と次元圧縮
本節では,特異値分解を使った画像の次元圧縮について述べる.まず特異値分解から始める.
行列  の特異値標準化
 の特異値標準化  を考える.
 を考える. とおく.U は M次ユニタリー行列であるから,
 とおく.U は M次ユニタリー行列であるから, は  の正規直交基底,また
 の正規直交基底,また は  の正規直交基底になっている.また
 の正規直交基底になっている.また  とし,
 とし, を正の特異値とする.このとき
 を正の特異値とする.このとき が成り立つ.これを A の特異値分解という.
特異値分解を用いた次元圧縮は次のようなものである. に対して
 に対して このようにすると, である.これをプログラムする.
 である.これをプログラムする. function A_k = SingDecomp(A,k)
    A_k = A_k + S(j)*U(:,j)*conj(V(:,j)'); 
画像の読み込み
A = imread('RIMG0030_AI_Gray.tif');
検算  r=rank(A) のとき,A=A_r であることの確認
disp(['原画像 A の階数は',num2str(r),'です']);
特に  と原画像 A との比較をする.
 と原画像 A との比較をする. AK = zeros(s1,s2,length(a));
    AK(:,:,j) = SingDecomp(A,k);
    disp(['近似データ A_{',num2str(k),'} の階数は',num2str(k),' です。'])
end
近似データ A_{5} の階数は5 です。
近似データ A_{10} の階数は10 です。
近似データ A_{120} の階数は120 です。
    title(['A_{',num2str(a(j)),'}'])
%print('CompressDim', '-dtiff', '-r300'); % 画像ファイルを保存する場合
   
参考文献
[1] 新井仁之,線形代数 基礎と応用,日本評論社,2006.     Kindle版(2022年版)があります. 
履歴
Version 1.1  2024年7月8日 minor changes
Version 1.2 2025年1月2日 minor changes
Version 1.3  2025年5月9日 minor changes
-----------------------------------------------------------------
© Hitoshi Arai, July 2024.