目的
- MatlabのSVD(特異値分解)とPythonのSVDの振る舞いの違いを調べる.
- 同じ分解を得ることを期待していたが, 得られなかったためその原因を調べたい.
- [2017 1/31 11:40 追記] 解決しました! 手っ取り早くMatlabのSVDをPythonでやりたい人は下記のリンクへ
方法
-
Matlabのrand関数で生成した$m \times n, m<n$の行列ランダムな行列を生成. ($m=20, n=50$)
データ: Googleスプレッドシートからコピーまたはmatファイルをダウンロード. -
Matlabの関数
svds
を用いてSVDを行う. Pythonのnumpy.linalg.svd
関数を用いてSVDを行う. この際, 最も大きい特異値とそれに係る特異ベクトルについて比較を行う.- Matlabでは
svds(A,k)
により行列Aの最も大きいk個の特異値/特異ベクトルを得る. - Pythonでは
numpy.linalg.svd(A)
により行列Aの特異値/特異ベクトルを降順に得る.
- Matlabでは
-
出力された特異値, 左特異ベクトル, 右特異ベクトルをプロットして比較する.
コードと出力
Matlab
>> file_name = 'ファイルのある場所/random_mat.mat';
>> file_data = load(file_name);
>> data = file_data.data;
[U S V flag] = svds((data), 1); % SVDのサブセット, 最大特異値の出力を得る
>> U'
1 列から 12 列
0.2245 0.2040 0.2040 0.2177 0.2319 0.2341 0.2295 0.2020 0.1833 0.2041 0.2266 0.2597
13 列から 20 列
0.2235 0.2244 0.2418 0.2249 0.2409 0.2307 0.2062 0.2443
>> S
S =
15.3810
>> V'
1 列から 12 列
0.1722 0.1295 0.1461 0.1505 0.1550 0.1368 0.1490 0.1064 0.1726 0.1539 0.1277 0.1579
13 列から 24 列
0.1508 0.1461 0.1372 0.1189 0.1264 0.1490 0.1351 0.1326 0.1604 0.1393 0.1482 0.1407
25 列から 36 列
0.1607 0.1165 0.1639 0.1267 0.1293 0.1426 0.1419 0.1283 0.1586 0.1445 0.1452 0.1595
37 列から 48 列
0.1137 0.1127 0.1629 0.1240 0.1341 0.1257 0.1646 0.1342 0.1221 0.1149 0.1182 0.1754
49 列から 50 列
0.1214 0.1347
Python
>> import scipy.io as sio
>> file_name = 'ファイルのある場所/random_mat.mat';
>> file_data = sio.loadmat(file_name)
>> data = file_data['data']
>> U, S, V = np.linalg.svd(data) # SVDの結果を降順に得る
>> In [26]: U[0]
Out[26]:
array([-0.22454768, 0.17010075, -0.20816028, 0.1740133 , -0.13221578,
0.12589231, -0.1504946 , 0.08601189, -0.15019956, 0.0628708 ,
0.00390582, -0.63042417, -0.17370565, 0.11313939, -0.34084198,
0.02180581, 0.36452862, 0.14897765, -0.16904068, -0.12814262])
In [27]: S[0]
Out[27]: 15.380960870979512
In [29]: V[0]
Out[29]:
array([-0.17217701, -0.12945966, -0.14610378, -0.15052239, -0.15495316,
-0.1368263 , -0.14904164, -0.10638937, -0.17259396, -0.1539205 ,
-0.12765933, -0.1579254 , -0.15083483, -0.14612175, -0.13719313,
-0.11890223, -0.12643451, -0.14904031, -0.1350735 , -0.13258259,
-0.16038814, -0.13927807, -0.14819803, -0.14068011, -0.16073848,
-0.11647424, -0.16391321, -0.12670191, -0.12928473, -0.14260052,
-0.14186614, -0.12825514, -0.15857603, -0.14445415, -0.14522958,
-0.15945624, -0.11366243, -0.11273067, -0.16286158, -0.12396521,
-0.13413132, -0.12574699, -0.16463963, -0.13424388, -0.12212364,
-0.11485005, -0.11818812, -0.17539564, -0.12136569, -0.13473575])
Matlabの出力をPythonにインポートして出力の比較
Matlabの出力のmatファイル
Pythonの出力のpickleファイル
Pythonのコード
# matlabでの結果のインポート
>> result_name = 'ファイルのある場所/matlab_result.mat'
>> result_file = sio.loadmat(result_name)
>> matlab_U = result_file['U']
>> matlab_S = result_file['S']
>> matlab_V = result_file['V']
# 特異値の比較
In [55]: S[0] - matlab_S
Out[55]: array([[ 1.77635684e-15]])
比較
特異値
MatlabとPythonでの誤差が1.77635684e-15であることからほぼ同じであると言って良い.
左特異ベクトル
右特異ベクトル
おや...?? 似た波形をしている.実際に符号を反転させてみると,
同じベクトルのようだ.
結論
- $m \times n, m<n$の行列に対してMatlabとPythonのSVDの出力の比較を行った.
- MatlabとPythonにおいて, 同じ特異値を得ることがわかった.一方で、違う特異ベクトルを得ることがわかった.
- 原因: 特異値を降順に並べ替えたとき特異値は一意に定まる. 一方で特異ベクトルは一意に定まらないため.
- SVDの一意性に調べたが, 特異値の一意性を分解の一意性と勘違いしていたためにこんなことを調べる羽目になってしまった...
- 一方で右特異ベクトルは符号を絶対値で正にしてやると同じになることがわかったから, まぁいっか.
-
どうにかMatlabとPythonで同じSVDをしたい...→ 解決しました! MatlabのSVD(特異値分解)のScipyでの再現についての検証$\bf U = A\rm | \bf V \rm |^{\rm -1}\sum^{\rm -1}$とかで$\bf U$を計算しなおすとか?