特異値分解のアルゴリズムと次元圧縮
ー MATLAB 版
早稲田大学教育・総合科学学術院 新井仁之
今回は特異値分解を,MATLABを使いながら学んでいく.このノートではエルミート行列の対角化は既習として,特異値標準化・特異値分解の証明(= アルゴリズム)の一つをプログラミングしながら学んでいく.特異値分解の応用として画像データの次元圧縮についても述べる.
1.準備
基本的な記法を設定しておく.
ℝ を実数全体のなす集合,ℂ を複素数全体からなる集合とし,𝕂 は ℝ または ℂ を表す.𝕂 はスカラー体,𝕂 の要素はスカラーと呼ばれる.
N次元縦ベクトル (ただし )全体からなる集合を で表す.これにはスカラー積と和が定義され,N次元線形空間をなしている(説明は既知として省くが,たとえば [1] 参照). には次のエルミート内積(あるいはユークリッド内積)が定義される. また を のユークリッド・ノルムあるいは長さという.以下,特に混乱のない場合は , と略記する.
M行N列の行列とは
である.このような行列全体のなす集合を本ノートでは
で表すことにする.各成分の複素共役をとり転置したものを で表し.A の共役行列という. である. は 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 の固有値, を λ に属する固有ベクトルという.λ に属する k 個の線形独立な固有ベクトルが存在するとき,λ の重複度は k であるという.このとき,この固有値の重複度を込めた個数は k 個であるともいう. の場合,代数学の基本定理から,一般のN 次正方行列 A は重複度も込めて N 個の固有値を持つことが知られている([1]参照). また,エルミート行列の固有値はすべて実数であることも知られている([1]参照).
特異値分解では次の定理を使っていく.
定理1 (エルミート行列の対角化) A を N 次正方行列で,エルミート行列であるとする.A の重複度も込めた N 個の固有値を とする.このとき,ある 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 次正方行列になっている.さらに よりエルミート行列である. の固有値は実数であるが,さらにこのタイプの行列の固有値は非負であることが示せる.そこでその固有値を と大きい順に並べる.
() とおき,これを A の特異値という.
さて, は 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次ユニタリー行列であるから, は の正規直交基底,また は の正規直交基底になっている.また とし, を正の特異値とする.このとき が成り立つ.これを 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 との比較をする. 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年版)があります.