annotate nmf_beta.m @ 1:3ea8ed09af0f tip

additional clarifications
author Dimitrios Giannoulis
date Wed, 13 Mar 2013 11:57:24 +0000
parents 22b10c5b72e8
children
rev   line source
Dimitrios@0 1 function [W,H,errs,vout] = nmf_beta(V,r,varargin)
Dimitrios@0 2 % function [W,H,errs,vout] = nmf_beta(V,r,varargin)
Dimitrios@0 3 %
Dimitrios@0 4 % Implements NMF using the beta-divergence [1]:
Dimitrios@0 5 %
Dimitrios@0 6 % min D(V||W*H) s.t. W>=0, H>=0
Dimitrios@0 7 %
Dimitrios@0 8 % /
Dimitrios@0 9 % | sum(V(:).^beta + (beta-1)*R(:).^beta - ...
Dimitrios@0 10 % | beta*V(:).*R(:).^(beta-1)) / ...
Dimitrios@0 11 % | (beta*(beta-1)) (beta \in{0 1}
Dimitrios@0 12 % where D(V||R) = |
Dimitrios@0 13 % | sum(V(:).*log(V(:)./R(:)) - V(:) + R(:)) (beta=1)
Dimitrios@0 14 % |
Dimitrios@0 15 % | sum(V(:)./R(:) - log(V(:)./R(:)) - 1) (beta=0)
Dimitrios@0 16 % \
Dimitrios@0 17 %
Dimitrios@0 18 % This divergence reduces to the following interesting distances for
Dimitrios@0 19 % certain values of beta:
Dimitrios@0 20 %
Dimitrios@0 21 % - Itakura-Saito (beta=0)
Dimitrios@0 22 % - I-divergence (beta=1)
Dimitrios@0 23 % - Euclidean distance (beta=2)
Dimitrios@0 24 %
Dimitrios@0 25 % Inputs: (all except V and r are optional and passed in in name-value pairs)
Dimitrios@0 26 % V [mat] - Input matrix (n x m)
Dimitrios@0 27 % r [num] - Rank of the decomposition
Dimitrios@0 28 % beta [num] - beta parameter [0]
Dimitrios@0 29 % niter [num] - Max number of iterations to use [100]
Dimitrios@0 30 % thresh [num] - Number between 0 and 1 used to determine convergence;
Dimitrios@0 31 % the algorithm has considered to have converged when:
Dimitrios@0 32 % (err(t-1)-err(t))/(err(1)-err(t)) < thresh
Dimitrios@0 33 % ignored if thesh is empty [[]]
Dimitrios@0 34 % norm_w [num] - Type of normalization to use for columns of W [1]
Dimitrios@0 35 % can be 0 (none), 1 (1-norm), or 2 (2-norm)
Dimitrios@0 36 % norm_h [num] - Type of normalization to use for rows of H [0]
Dimitrios@0 37 % can be 0 (none), 1 (1-norm), 2 (2-norm), or 'a' (sum(H(:))=1)
Dimitrios@0 38 % verb [num] - Verbosity level (0-3, 0 means silent) [1]
Dimitrios@0 39 % W0 [mat] - Initial W values (n x r) [[]]
Dimitrios@0 40 % empty means initialize randomly
Dimitrios@0 41 % H0 [mat] - Initial H values (r x m) [[]]
Dimitrios@0 42 % empty means initialize randomly
Dimitrios@0 43 % W [mat] - Fixed value of W (n x r) [[]]
Dimitrios@0 44 % empty means we should update W at each iteration while
Dimitrios@0 45 % passing in a matrix means that W will be fixed
Dimitrios@0 46 % H [mat] - Fixed value of H (r x m) [[]]
Dimitrios@0 47 % empty means we should update H at each iteration while
Dimitrios@0 48 % passing in a matrix means that H will be fixed
Dimitrios@0 49 % myeps [num] - Small value to add to denominator of updates [1e-20]
Dimitrios@0 50 %
Dimitrios@0 51 % Outputs:
Dimitrios@0 52 % W [mat] - Basis matrix (n x r)
Dimitrios@0 53 % H [mat] - Weight matrix (r x m)
Dimitrios@0 54 % errs [vec] - Error of each iteration of the algorithm
Dimitrios@0 55 %
Dimitrios@0 56 % [1] Cichocki, A. and Amari, S.I. and Zdunek, R. and Kompass, R. and
Dimitrios@0 57 % Hori, G. and He, Z. Extended SMART Algorithms for Non-negative Matrix
Dimitrios@0 58 % Factorization, Artificial Intelligence and Soft Computing, 2006
Dimitrios@0 59 %
Dimitrios@0 60 % 2010-01-14 Graham Grindlay (grindlay@ee.columbia.edu)
Dimitrios@0 61
Dimitrios@0 62 % Copyright (C) 2008-2028 Graham Grindlay (grindlay@ee.columbia.edu)
Dimitrios@0 63 %
Dimitrios@0 64 % This program is free software: you can redistribute it and/or modify
Dimitrios@0 65 % it under the terms of the GNU General Public License as published by
Dimitrios@0 66 % the Free Software Foundation, either version 3 of the License, or
Dimitrios@0 67 % (at your option) any later version.
Dimitrios@0 68 %
Dimitrios@0 69 % This program is distributed in the hope that it will be useful,
Dimitrios@0 70 % but WITHOUT ANY WARRANTY; without even the implied warranty of
Dimitrios@0 71 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Dimitrios@0 72 % GNU General Public License for more details.
Dimitrios@0 73 %
Dimitrios@0 74 % You should have received a copy of the GNU General Public License
Dimitrios@0 75 % along with this program. If not, see <http://www.gnu.org/licenses/>.
Dimitrios@0 76
Dimitrios@0 77 % do some sanity checks
Dimitrios@0 78 if min(min(V)) < 0
Dimitrios@0 79 error('Matrix entries can not be negative');
Dimitrios@0 80 end
Dimitrios@0 81 if min(sum(V,2)) == 0
Dimitrios@0 82 error('Not all entries in a row can be zero');
Dimitrios@0 83 end
Dimitrios@0 84
Dimitrios@0 85 [n,m] = size(V);
Dimitrios@0 86
Dimitrios@0 87 % process arguments
Dimitrios@0 88 [beta, niter, thresh, norm_w, norm_h, verb, myeps, W0, H0, W, H] = ...
Dimitrios@0 89 parse_opt(varargin, 'beta', 0, 'niter', 100, 'thresh', [], ...
Dimitrios@0 90 'norm_w', 1, 'norm_h', 0, 'verb', 1, 'myeps', 1e-20, ...
Dimitrios@0 91 'W0', [], 'H0', [], 'W', [], 'H', []);
Dimitrios@0 92
Dimitrios@0 93 % initialize W based on what we got passed
Dimitrios@0 94 if isempty(W)
Dimitrios@0 95 if isempty(W0)
Dimitrios@0 96 W = rand(n,r);
Dimitrios@0 97 else
Dimitrios@0 98 W = W0;
Dimitrios@0 99 end
Dimitrios@0 100 update_W = true;
Dimitrios@0 101 else
Dimitrios@0 102 update_W = false;
Dimitrios@0 103 end
Dimitrios@0 104
Dimitrios@0 105 % initialize H based on what we got passed
Dimitrios@0 106 if isempty(H)
Dimitrios@0 107 if isempty(H0)
Dimitrios@0 108 H = rand(r,m);
Dimitrios@0 109 else
Dimitrios@0 110 H = H0;
Dimitrios@0 111 end
Dimitrios@0 112 update_H = true;
Dimitrios@0 113 else % we aren't H
Dimitrios@0 114 update_H = false;
Dimitrios@0 115 end
Dimitrios@0 116
Dimitrios@0 117 if norm_w ~= 0
Dimitrios@0 118 % normalize W
Dimitrios@0 119 W = normalize_W(W,norm_w);
Dimitrios@0 120 end
Dimitrios@0 121
Dimitrios@0 122 if norm_h ~= 0
Dimitrios@0 123 % normalize H
Dimitrios@0 124 H = normalize_H(H,norm_h);
Dimitrios@0 125 end
Dimitrios@0 126
Dimitrios@0 127 % initial reconstruction
Dimitrios@0 128 R = W*H;
Dimitrios@0 129
Dimitrios@0 130 errs = zeros(niter,1);
Dimitrios@0 131 for t = 1:niter
Dimitrios@0 132 % update W if requested
Dimitrios@0 133 if update_W
Dimitrios@0 134 W = W .* ( ((R.^(beta-2) .* V)*H') ./ max(R.^(beta-1)*H', myeps) );
Dimitrios@0 135 if norm_w ~= 0
Dimitrios@0 136 W = normalize_W(W,norm_w);
Dimitrios@0 137 end
Dimitrios@0 138 end
Dimitrios@0 139
Dimitrios@0 140 % update reconstruction
Dimitrios@0 141 R = W*H;
Dimitrios@0 142
Dimitrios@0 143 % update H if requested
Dimitrios@0 144 if update_H
Dimitrios@0 145 H = H .* ( (W'*(R.^(beta-2) .* V)) ./ max(W'*R.^(beta-1), myeps) );
Dimitrios@0 146 if norm_h ~= 0
Dimitrios@0 147 H = normalize_H(H,norm_h);
Dimitrios@0 148 end
Dimitrios@0 149 end
Dimitrios@0 150
Dimitrios@0 151 % update reconstruction
Dimitrios@0 152 R = W*H;
Dimitrios@0 153
Dimitrios@0 154 % compute beta-divergence
Dimitrios@0 155 switch beta
Dimitrios@0 156 case 0
Dimitrios@0 157 errs(t) = sum(V(:)./R(:) - log(V(:)./R(:)) - 1);
Dimitrios@0 158 case 1
Dimitrios@0 159 errs(t) = sum(V(:).*log(V(:)./R(:)) - V(:) + R(:));
Dimitrios@0 160 case 2
Dimitrios@0 161 errs(t) = sum(sum((V-W*H).^2));
Dimitrios@0 162 otherwise
Dimitrios@0 163 errs(t) = sum(V(:).^beta + (beta-1)*R(:).^beta - beta*V(:).*R(:).^(beta-1)) / ...
Dimitrios@0 164 (beta*(beta-1));
Dimitrios@0 165 end
Dimitrios@0 166
Dimitrios@0 167 % display error if asked
Dimitrios@0 168 if verb >= 3
Dimitrios@0 169 disp(['nmf_beta: iter=' num2str(t) ', err=' num2str(errs(t)) ...
Dimitrios@0 170 '(beta=' num2str(beta) ')']);
Dimitrios@0 171 end
Dimitrios@0 172
Dimitrios@0 173 % check for convergence if asked
Dimitrios@0 174 if ~isempty(thresh)
Dimitrios@0 175 if t > 2
Dimitrios@0 176 if (errs(t-1)-errs(t))/(errs(1)-errs(t-1)) < thresh
Dimitrios@0 177 break;
Dimitrios@0 178 end
Dimitrios@0 179 end
Dimitrios@0 180 end
Dimitrios@0 181 end
Dimitrios@0 182
Dimitrios@0 183 % display error if asked
Dimitrios@0 184 if verb >= 2
Dimitrios@0 185 disp(['nmf_beta: final_err=' num2str(errs(t)) '(beta=' num2str(beta) ')']);
Dimitrios@0 186 end
Dimitrios@0 187
Dimitrios@0 188 % if we broke early, get rid of extra 0s in the errs vector
Dimitrios@0 189 errs = errs(1:t);
Dimitrios@0 190
Dimitrios@0 191 % needed to conform to function signature required by nmf_alg
Dimitrios@0 192 vout = {};