├── README.md ├── sample.cpp ├── .gitignore └── robust_pca.h /README.md: -------------------------------------------------------------------------------- 1 | # Robust Principal Component Analysis 2 | -------------------------------------- 3 | 4 | [P.S Huang, S.D. Chen, P. Smaragdis, and M. HasegawaJohnson, "Singing-voice separation from monaural recordings using robust principal component analysis", Proc. ICASSP 2012.](http://www.mirlab.org/conference_papers/International_Conference/ICASSP%202012/pdfs/0000057.pdf) 5 | 6 | Matlabコードは公開されてるけど使いにくいのでベースだけC++で書きなおした版 7 | 8 | 本家のデモとかこのへんにある -> [https://sites.google.com/site/singingvoiceseparationrpca/](https://sites.google.com/site/singingvoiceseparationrpca/) 9 | 10 | 11 | 12 | 面白い方法だけど、行列がデカイと計算もけっこう時間かかる 13 | 14 | ## メモ 15 | 16 | * RedSVDとか使って高速化しようと思ったけど途中でやめた 17 | * ↑に上げた論文だけじゃわからなくて色々調べて実装した(曖昧) -------------------------------------------------------------------------------- /sample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "robust_pca.h" 4 | 5 | using Eigen::MatrixXd; 6 | 7 | int main() { 8 | MatrixXd D = MatrixXd::Random(5, 5); 9 | D = D.array() * D.array(); 10 | 11 | MatrixXd A = MatrixXd::Zero(5, 5); 12 | MatrixXd E = MatrixXd::Zero(5, 5); 13 | 14 | std::cout << "The original matirix; D = \n" << D << std::endl; 15 | 16 | // Perform Robust PCA 17 | sp::ml::robust_pca(D, A, E); 18 | std::cout << "Estimated row rank matrix: A = \n" << A << std::endl; 19 | std::cout << "Estimated sparse matrix: E = \n" << E << std::endl; 20 | 21 | std::cout << "Reconstructed matrix: A + E =:\n" << A + E << std::endl; 22 | 23 | std::cout << "Reconstruction Error = " << (D - (A + E)).norm() << std::endl; 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### C++ ### 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Compiled Dynamic libraries 11 | *.so 12 | *.dylib 13 | *.dll 14 | 15 | # Compiled Static libraries 16 | *.lai 17 | *.la 18 | *.a 19 | *.lib 20 | 21 | # Executables 22 | *.exe 23 | *.out 24 | *.app 25 | 26 | 27 | ### Emacs ### 28 | # -*- mode: gitignore; -*- 29 | *~ 30 | \#*\# 31 | /.emacs.desktop 32 | /.emacs.desktop.lock 33 | *.elc 34 | auto-save-list 35 | tramp 36 | .\#* 37 | 38 | # Org-mode 39 | .org-id-locations 40 | *_archive 41 | 42 | # flymake-mode 43 | *_flymake.* 44 | 45 | # eshell files 46 | /eshell/history 47 | /eshell/lastdir 48 | 49 | # elpa packages 50 | /elpa/ 51 | 52 | 53 | ### vim ### 54 | [._]*.s[a-w][a-z] 55 | [._]s[a-w][a-z] 56 | *.un~ 57 | Session.vim 58 | .netrwhist 59 | *~ 60 | 61 | 62 | ### Waf ### 63 | # for projects that use Waf for building: http://code.google.com/p/waf/ 64 | .waf-* 65 | .lock-* 66 | -------------------------------------------------------------------------------- /robust_pca.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 Ryuichi Yamamoto 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to 5 | // deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | // sell copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | // IN THE SOFTWARE. 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace sp { 29 | namespace ml { 30 | 31 | namespace internal { 32 | 33 | template 34 | inline int count_larger_than(const Vector& v, ValueType value) { 35 | int count = 0; 36 | 37 | for (int i = 0; i < v.size(); ++i) { 38 | if (v[i] > value) ++count; 39 | } 40 | 41 | return count; 42 | } 43 | 44 | /** 45 | * Robust Principal Component Analysis (RPCA) using the inexact augumented 46 | * Lagrance multiplier. 47 | * @param D observation matrix (D = A + E) 48 | * @param A row-rank matrix 49 | * @param E sparse matrix 50 | */ 51 | template 52 | void robust_pca_inexact_alm( 53 | Eigen::Matrix& D, 54 | Eigen::Matrix& A, 55 | Eigen::Matrix& E) { 56 | typedef typename Eigen::Matrix 57 | Matrix; 58 | typedef typename Eigen::Array 59 | Array; 60 | typedef typename Eigen::Matrix Vector; 61 | 62 | const int M = D.rows(); 63 | const int N = D.cols(); 64 | 65 | using std::cout; 66 | using std::endl; 67 | 68 | // supplementary variable 69 | Matrix Y = D; 70 | A = Matrix::Zero(M, N); 71 | E = Matrix::Zero(M, N); 72 | Array zero = Matrix::Zero(M, N); 73 | 74 | // parameters 75 | const double lambda = 1.0 / sqrt(std::max(M, N)); 76 | const double rho = 1.5; 77 | 78 | Eigen::JacobiSVD svd_only_singlar_values(Y); 79 | const double norm_two = 80 | svd_only_singlar_values.singularValues()(0); // can be tuned 81 | const double norm_inf = Y.array().abs().maxCoeff() / lambda; 82 | const double dual_norm = std::max(norm_two, norm_inf); 83 | const double d_norm = D.norm(); 84 | Y /= dual_norm; 85 | 86 | double mu = 1.25 / norm_two; 87 | const double mu_bar = mu * 1.0e+7; 88 | 89 | bool converged = false; 90 | int max_iter = 1000; 91 | double error_tolerance = 1.0e-7; 92 | int iter = 0; 93 | int total_svd = 0; 94 | int sv = 10; 95 | while (!converged) { 96 | // update sparse matrix E 97 | Array temp_T = D - A + (1.0 / mu) * Y; 98 | E = (temp_T - lambda / mu).max(zero) + (temp_T + lambda / mu).min(zero); 99 | 100 | // force non-negative 101 | E = E.array().max(zero); // 論文には書いてない 102 | //cout << E << endl; 103 | 104 | // SVD 105 | Eigen::JacobiSVD svd(D - E + 1.0 / mu * Y, 106 | Eigen::ComputeFullU | Eigen::ComputeFullV); 107 | Matrix U = svd.matrixU(); 108 | Matrix V = svd.matrixV(); 109 | Vector singularValues = svd.singularValues(); 110 | 111 | // trancate dimention 112 | int svp = count_larger_than(singularValues, 1 / mu); 113 | if (svp < sv) { 114 | sv = std::min(svp + 1, N); 115 | } else { 116 | sv = std::min(svp + static_cast(0.05 * N + 0.5), N); 117 | } 118 | 119 | // update A 120 | Matrix S_th = 121 | (singularValues.head(svp).array() - 1.0 / mu).matrix().asDiagonal(); 122 | A = U.leftCols(svp) * S_th * V.leftCols(svp).transpose(); 123 | 124 | // force non-negative 125 | A = A.array().max(zero); 126 | 127 | total_svd += 1; 128 | Matrix Z = D - A - E; 129 | Y = Y + mu * Z; 130 | mu = std::min(mu * rho, mu_bar); 131 | 132 | // objective function 133 | double objective = Z.norm() / d_norm; 134 | 135 | if (objective < error_tolerance) { 136 | converged = true; 137 | } 138 | 139 | if (++iter >= max_iter) { 140 | break; 141 | } 142 | } 143 | } 144 | 145 | } // end namespace internal 146 | 147 | /** 148 | * Interface of RPCA 149 | * @param D: observation matrix (D = A + E) 150 | * @param A: row-rank matrix 151 | * @param E: sparse matrix 152 | */ 153 | template 154 | void robust_pca(Eigen::Matrix& D, 155 | Eigen::Matrix& A, 156 | Eigen::Matrix& E) { 157 | internal::robust_pca_inexact_alm(D, A, E); 158 | } 159 | 160 | } // end namespace ml 161 | } // end namespace sp 162 | --------------------------------------------------------------------------------