特異値分解

特異値分解 (Singular value decomposition, SVD) の用途: 主成分分析,疑似逆行列の計算,機械学習,パタン認識,数値解析など.任意の形の行列を分解できる.

行列 (Matrix/ComplexMatrix) A に対する特異値分解

  SVD  svd (const Matrix& A, SVD::type svd_type=SVD::std);
  SVD  svd (const Matrix& A, int& info, SVD::type svd_type=SVD::std);
  ComplexSVD  svd (const ComplexMatrix& A, SVD::type svd_type=SVD::std);
  ComplexSVD  svd (const ComplexMatrix& A, int& info, SVD::type svd_type=SVD::std);

によって得られる.

octave では,行列Aは

  A = USV*

のように分解される.ここで U, V はユニタリ行列, V* は V の共役転置を表す. S は対角成分が A の特異値(対角成分以外はゼロ)である. S は大きい順にソートされている (この情報は octave のマニュアルに基づくものではなく, Singular Value Decomposition -- LAPACK Users' Guide に記述されていることからの判断である).

ここで SVD クラス, ComplexSVD クラス のオブジェクトには特異値分解の結果が代入される. info は計算後にゼロ以外なら何らかのエラーが発生したことを示すフラグである. SVD::type は列挙型で,

  00032 class
  00033 OCTAVE_API
  00034 SVD
  00035 {
  00036 public:
  00038   enum type
  00039     {
  00040       std,
  00041       economy,
  00042       sigma_only
  00043     };

のように定義されている.デフォルト値 SVD::std は通常の特異値分解(U,S,Vすべて計算), SVD::sigma_only は S のみ計算, SVD::economy はエコノミーサイズ(UとVの不要な列や行を消去)で計算する.

SVD オブジェクトは以下の public メンバ関数を持つ:

  // S を返す :
  DiagMatrix   singular_values (void) const;
  // U を返す :
  Matrix   left_singular_matrix (void) const;
  // V を返す :
  Matrix   right_singular_matrix (void) const;

サンプル:

  Matrix A(3,6);
  stringstream ss (
    "  4   0   4   6   5   4"
    "  9   6   2   4   1   0"
    "  1   6   9   0   7   5"); ss >> A;
  int info;
  cout<<"A="<<endl<<A;
  // SVD オブジェクトの生成 :
  SVD  svd (A, info);
  cout<<"info= "<<info<<endl;
  cout<<"svd.singular_values()= "<<endl<< svd.singular_values();
  cout<<"svd.left_singular_matrix()= "<<endl<< svd.left_singular_matrix();
  cout<<"svd.right_singular_matrix()= "<<endl<< svd.right_singular_matrix();
  // USV* を計算(Aに一致するか?) :
  cout<<"svd.left_singular_matrix()*svd.singular_values()*svd.right_singular_matrix().transpose()= "<<endl
    << svd.left_singular_matrix()*svd.singular_values()*svd.right_singular_matrix().transpose();
  // エコノミーモードで特異値分解 :
  svd= SVD(A, info, SVD::economy);
  cout<<"info= "<<info<<endl;
  cout<<"svd.singular_values()= "<<endl<< svd.singular_values();
  cout<<"svd.left_singular_matrix()= "<<endl<< svd.left_singular_matrix();
  cout<<"svd.right_singular_matrix()= "<<endl<< svd.right_singular_matrix();
  // USV* を計算(Aに一致するか?) :
  cout<<"svd.left_singular_matrix()*svd.singular_values()*svd.right_singular_matrix().transpose()= "<<endl
    << svd.left_singular_matrix()*svd.singular_values()*svd.right_singular_matrix().transpose();

結果:

  A=
    4 0 4 6 5 4
    9 6 2 4 1 0
    1 6 9 0 7 5
  info= 0
  svd.singular_values()=
    17.6341 0 0 0 0 0
    0 9.51377 0 0 0 0
    0 0 6.12601 0 0 0
  svd.left_singular_matrix()=
    -0.509954 0.0830227 0.856186
    -0.498851 0.782325 -0.372982
    -0.700782 -0.617312 -0.357534
  svd.right_singular_matrix()=
    -0.410016 0.710098 -0.0472778 -0.556602 -0.109191 -0.060699
    -0.408176 0.104068 -0.715489 0.416853 0.235993 0.284919
    -0.529915 -0.384608 -0.0879892 0.0729675 -0.59143 -0.45652
    -0.286668 0.381283 0.595035 0.646778 0.00468758 -0.00584839
    -0.451064 -0.32834 0.229385 -0.224744 0.730871 -0.22679
    -0.314376 -0.289525 0.267234 -0.205602 -0.219999 0.80948
  svd.left_singular_matrix()*svd.singular_values()*svd.right_singular_matrix().transpose()=
    4 -8.88178e-16 4 6 5 4
    9 6 2 4 1 -1.77636e-15
    1 6 9 2.22045e-16 7 5
  info= 0
  svd.singular_values()=
    17.6341 0 0
    0 9.51377 0
    0 0 6.12601
  svd.left_singular_matrix()=
    -0.509954 0.0830227 0.856186
    -0.498851 0.782325 -0.372982
    -0.700782 -0.617312 -0.357534
  svd.right_singular_matrix()=
    -0.410016 0.710098 -0.0472778
    -0.408176 0.104068 -0.715489
    -0.529915 -0.384608 -0.0879892
    -0.286668 0.381283 0.595035
    -0.451064 -0.32834 0.229385
    -0.314376 -0.289525 0.267234
  svd.left_singular_matrix()*svd.singular_values()*svd.right_singular_matrix().transpose()=
    4 -8.88178e-16 4 6 5 4
    9 6 2 4 1 -1.77636e-15
    1 6 9 2.22045e-16 7 5