MATLABでのインクリメンタルSVD実装


8

MATLABでインクリメンタルSVDを実装したライブラリ/ツールボックスはありますか?私はこのペーパーを実装しまし、それは速いですがうまくいきません。私はこれを試しましたが、これでもエラーが速く伝播します(5〜10ポイントの更新内でエラーが高くなります)。

回答:


8

はい。Christopher Bakerは、彼のインクリメンタルSVDメソッドを、IncPACK(imtslプロジェクト内のGitHubにアーカイブされている)と呼ばれるMATLABパッケージに実装しました。それは彼の修士論文に記載されているメソッドを実装しています。ブランドのアルゴリズムがエラーを蓄積する傾向がある理由の簡単な説明は、ベーカーらによる2012年の論文に記載されています。Chahlaouiらによる関連する方法は、左特異部分空間のエラー限界と特異値を議論します。

私はすでにスティーブンの答えのコメントでは、これらの点を言及したが、それは繰り返しクマとの両方BakerのとChahlaouiスケールによる方法トランケートrank-ためのK SVDのMによってn行列。低ランクの近似の場合、m n k項が支配的であり、アルゴリズムのバリアントに応じて、通常8〜12の先行定数があります。O(mnk+nk3)kmメートルk

スティーブンの答えと同様に、チャラウイのアルゴリズムはQR分解から始まります。スティーブンの答えは左特異ベクトルの計算に役立ちますが、行列の密なSVDは、切り捨て前にmnで超線形の複雑さを持ち(O m n 2)になる)、これはおそらく効率を低下させますが、より高くなります正確。RメートルOメートル2

それだけの価値があるので、私はブランドのアルゴリズムを自分で実装しました。これは、ランクの切り捨てに使用される内積許容値に多少敏感です。私はベイカーのパッケージを使用していませんが、ブランドのアルゴリズムではなく、ベイカーのアルゴリズム(または密接に関連しているアルゴリズム)のエラー推定が存在し、ベイカーのアルゴリズムのランク切り捨て許容値が内部ではなく特異値にあるため、より良いと思います製品。


IncPACKパッケージを確認しましたが、seqkl_update関数がありますが、新しい行と列のパラメーターを受け入れるようには見えません。また、紙の要約から(正しくない可能性があるので、すべて読む必要があります)、インクリメンタルと呼ぶマルチパスアプローチのように見えます。
pj 2015年

ベイカーは、シングルパスとマルチパスの両方のアプローチについて説明します。このseqkl関数はメイン関数のように見え、単一パスと複数パスのオプションがあります。シングルパスはによって与えられseqkl_stdpass、を呼び出しますseqkl_update。そのため、おそらくseqkl最初の因数分解に使用し、その後seqkl_updateに列の更新を呼び出すことをお勧めします。
Geoff Oxberry 2015年

ええ、今まで私が見つけたのは列の更新のみで、新しいデータAiは seqkl_updateファイルのコメントであるU(1:m、o:op)に保存されます。しかし、行の更新はどうですか?
pj 2015年

@pj私が読んだことから、ほとんどの文献は列の更新に焦点を当てています。可能であれば、代わりに行列の転置のSVDを計算することもできます。しかし、私はあなたのデータを転置しなければならないことはオプションではないかもしれないと認識しています。アルゴリズムは行の更新に関して再表現できると思いますが、少し作業が必要になる場合があります。
Geoff Oxberry 2015年

最初の2つのリンクは死んでいます。
JoelSjögren16年

4

行列のsvdを計算する1つの方法Xは、最初X=QRにQR分解を使用して因数分解する(安定性のためにピボットを使用するため、これは[Q,R,E] = qr(X,0)Matlabにあります)、次にのsvdを計算しRます。どちらかの行列が非常に長方形である場合、最もコストのかかる計算はQR分解です。

したがって、マトリックスXを別の行または列でインクリメントする場合(これはあなたが意図したとおりのことです)、Qt分解をMatlabのqrinsert関数で更新し、のSVD計算をやり直すことができRます。

大きな正方行列がある場合、SVDの再実行にはR時間がかかるため、この方法はあまり役に立ちません。


これは、ランクの低いsvdでのみ高速です。
dranxo 2015年

@Stephenはい、それは私が欲しいものです、列と行を追加します(つまり、1つのデータポイントを追加します)。私が試した方法も、最初にQRを実行してから更新すると思います。私の主な懸念はエラーです。私が気づいたのは、あまり変更されない古いポイントのsvdです。5〜6の増分更新が巨大なエラーで破損した後、新しいポイントの場合も同じです。
pj 2015年

RXk

@GeoffOxberry、それが私が言ったことです。正方行列がある場合、これは効率的ではありません。スキニー/ファットマトリックスがあることがよくあり、このアルゴリズムはMatlabの既存の関数を使用して簡単に実装できる
Stephen

@dranxo、まあ、あなたが非常に長方形であるなら、それは比較的速いです。真のインクリメンタルSVDほど高速ではありませんが、非常に長方形である場合、内部SVDのコストは取るに足らないものであり、これと実装の容易さのバランスをとることができます
Stephen

4

列の追加を処理できるメソッドは次のとおりです:http : //pcc.byu.edu/resources.html。行の追加を処理するように更新しました。

function [Up1,Sp,Vp1] = addblock_svd_update2( Uarg, Sarg, Varg, Aarg, force_orth )

  U = Varg;
  V = Uarg;
  S = Sarg;
  A = Aarg';

  current_rank = size( U, 2 );
  m = U' * A;
  p = A - U*m;
  P = orth( p );
  P = [ P zeros(size(P,1), size(p,2)-size(P,2)) ];
  Ra = P' * p;
  z = zeros( size(m) );
  K = [ S m ; z' Ra ];
  [tUp,tSp,tVp] = svds( K, current_rank );
  Sp = tSp;
  Up = [ U P ] * tUp;
  Vp = V * tVp( 1:current_rank, : );
  Vp = [ Vp ; tVp( current_rank+1:size(tVp,1), : ) ];
  if ( force_orth )
    [UQ,UR] = qr( Up, 0 );
    [VQ,VR] = qr( Vp, 0 );
    [tUp,tSp,tVp] = svds( UR * Sp * VR', current_rank );
    Up = UQ * tUp;
    Vp = VQ * tVp;
    Sp = tSp;
  end;

  Up1 = Vp;
  Vp1 = Up;

return;

でテストする

X = [[ 2.180116   2.493767  -0.047867;
       -1.562426  2.292670   0.139761;
       0.919099  -0.887082  -1.197149;
       0.333190  -0.632542  -0.013330]];

A = [1 1 1];
X2 = [X; A];
[U,S,V] = svds(X);

[Up,Sp,Vp] = addblock_svd_update2(U, S, V, A, true);

Up
Sp
Vp

[U2,S2,V2] = svds(X2);
U2
S2
V2

両側のU、S、Vの結果が同じであることがわかります。

また、Pythonバージョン、

import numpy as np
import scipy.linalg as lin

def  addblock_svd_update( Uarg, Sarg, Varg, Aarg, force_orth = False):
  U = Varg
  V = Uarg
  S = np.eye(len(Sarg),len(Sarg))*Sarg
  A = Aarg.T

  current_rank = U.shape[1]
  m = np.dot(U.T,A)
  p = A - np.dot(U,m)
  P = lin.orth(p)
  Ra = np.dot(P.T,p)
  z = np.zeros(m.shape)
  K = np.vstack(( np.hstack((S,m)), np.hstack((z.T,Ra)) ))
  tUp,tSp,tVp = lin.svd(K);
  tUp = tUp[:,:current_rank]
  tSp = np.diag(tSp[:current_rank])
  tVp = tVp[:,:current_rank]
  Sp = tSp
  Up = np.dot(np.hstack((U,P)),tUp)
  Vp = np.dot(V,tVp[:current_rank,:])
  Vp = np.vstack((Vp, tVp[current_rank:tVp.shape[0], :]))

  if force_orth:
    UQ,UR = lin.qr(Up,mode='economic')
    VQ,VR = lin.qr(Vp,mode='economic')
    tUp,tSp,tVp = lin.svd( np.dot(np.dot(UR,Sp),VR.T));
    tSp = np.diag(tSp)
    Up = np.dot(UQ,tUp)
    Vp = np.dot(VQ,tVp)
    Sp = tSp;

  Up1 = Vp;
  Vp1 = Up;

  return Up1,Sp,Vp1

2

インクリメンタルSVDの代わりに、階層近似近似直交分解HAPODがあります。その実装はgithub:http : //git.io/hapodにあります。HAPODには厳密なエラー範囲があり、特殊なケースは増分バリアントです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.