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 = {};
|