wolffd@0
|
1 function [mix, options, errlog] = gmmem(mix, x, options)
|
wolffd@0
|
2 %GMMEM EM algorithm for Gaussian mixture model.
|
wolffd@0
|
3 %
|
wolffd@0
|
4 % Description
|
wolffd@0
|
5 % [MIX, OPTIONS, ERRLOG] = GMMEM(MIX, X, OPTIONS) uses the Expectation
|
wolffd@0
|
6 % Maximization algorithm of Dempster et al. to estimate the parameters
|
wolffd@0
|
7 % of a Gaussian mixture model defined by a data structure MIX. The
|
wolffd@0
|
8 % matrix X represents the data whose expectation is maximized, with
|
wolffd@0
|
9 % each row corresponding to a vector. The optional parameters have
|
wolffd@0
|
10 % the following interpretations.
|
wolffd@0
|
11 %
|
wolffd@0
|
12 % OPTIONS(1) is set to 1 to display error values; also logs error
|
wolffd@0
|
13 % values in the return argument ERRLOG. If OPTIONS(1) is set to 0, then
|
wolffd@0
|
14 % only warning messages are displayed. If OPTIONS(1) is -1, then
|
wolffd@0
|
15 % nothing is displayed.
|
wolffd@0
|
16 %
|
wolffd@0
|
17 % OPTIONS(3) is a measure of the absolute precision required of the
|
wolffd@0
|
18 % error function at the solution. If the change in log likelihood
|
wolffd@0
|
19 % between two steps of the EM algorithm is less than this value, then
|
wolffd@0
|
20 % the function terminates.
|
wolffd@0
|
21 %
|
wolffd@0
|
22 % OPTIONS(5) is set to 1 if a covariance matrix is reset to its
|
wolffd@0
|
23 % original value when any of its singular values are too small (less
|
wolffd@0
|
24 % than MIN_COVAR which has the value eps). With the default value of
|
wolffd@0
|
25 % 0 no action is taken.
|
wolffd@0
|
26 %
|
wolffd@0
|
27 % OPTIONS(14) is the maximum number of iterations; default 100.
|
wolffd@0
|
28 %
|
wolffd@0
|
29 % The optional return value OPTIONS contains the final error value
|
wolffd@0
|
30 % (i.e. data log likelihood) in OPTIONS(8).
|
wolffd@0
|
31 %
|
wolffd@0
|
32 % See also
|
wolffd@0
|
33 % GMM, GMMINIT
|
wolffd@0
|
34 %
|
wolffd@0
|
35
|
wolffd@0
|
36 % Copyright (c) Ian T Nabney (1996-2001)
|
wolffd@0
|
37
|
wolffd@0
|
38 % Check that inputs are consistent
|
wolffd@0
|
39 errstring = consist(mix, 'gmm', x);
|
wolffd@0
|
40 if ~isempty(errstring)
|
wolffd@0
|
41 error(errstring);
|
wolffd@0
|
42 end
|
wolffd@0
|
43
|
wolffd@0
|
44 [ndata, xdim] = size(x);
|
wolffd@0
|
45
|
wolffd@0
|
46 % Sort out the options
|
wolffd@0
|
47 if (options(14))
|
wolffd@0
|
48 niters = options(14);
|
wolffd@0
|
49 else
|
wolffd@0
|
50 niters = 100;
|
wolffd@0
|
51 end
|
wolffd@0
|
52
|
wolffd@0
|
53 display = options(1);
|
wolffd@0
|
54 store = 0;
|
wolffd@0
|
55 if (nargout > 2)
|
wolffd@0
|
56 store = 1; % Store the error values to return them
|
wolffd@0
|
57 errlog = zeros(1, niters);
|
wolffd@0
|
58 end
|
wolffd@0
|
59 test = 0;
|
wolffd@0
|
60 if options(3) > 0.0
|
wolffd@0
|
61 test = 1; % Test log likelihood for termination
|
wolffd@0
|
62 end
|
wolffd@0
|
63
|
wolffd@0
|
64 check_covars = 0;
|
wolffd@0
|
65 if options(5) >= 1
|
wolffd@0
|
66 if display >= 0
|
wolffd@0
|
67 disp('check_covars is on');
|
wolffd@0
|
68 end
|
wolffd@0
|
69 check_covars = 1; % Ensure that covariances don't collapse
|
wolffd@0
|
70 MIN_COVAR = eps; % Minimum singular value of covariance matrix
|
wolffd@0
|
71 init_covars = mix.covars;
|
wolffd@0
|
72 end
|
wolffd@0
|
73
|
wolffd@0
|
74 % Main loop of algorithm
|
wolffd@0
|
75 for n = 1:niters
|
wolffd@0
|
76
|
wolffd@0
|
77 % Calculate posteriors based on old parameters
|
wolffd@0
|
78 [post, act] = gmmpost(mix, x);
|
wolffd@0
|
79
|
wolffd@0
|
80 % Calculate error value if needed
|
wolffd@0
|
81 if (display | store | test)
|
wolffd@0
|
82 prob = act*(mix.priors)';
|
wolffd@0
|
83 % Error value is negative log likelihood of data
|
wolffd@0
|
84 e = - sum(log(prob));
|
wolffd@0
|
85 if store
|
wolffd@0
|
86 errlog(n) = e;
|
wolffd@0
|
87 end
|
wolffd@0
|
88 if display > 0
|
wolffd@0
|
89 fprintf(1, 'Cycle %4d Error %11.6f\n', n, e);
|
wolffd@0
|
90 end
|
wolffd@0
|
91 if test
|
wolffd@0
|
92 if (n > 1 & abs(e - eold) < options(3))
|
wolffd@0
|
93 options(8) = e;
|
wolffd@0
|
94 return;
|
wolffd@0
|
95 else
|
wolffd@0
|
96 eold = e;
|
wolffd@0
|
97 end
|
wolffd@0
|
98 end
|
wolffd@0
|
99 end
|
wolffd@0
|
100
|
wolffd@0
|
101 % Adjust the new estimates for the parameters
|
wolffd@0
|
102 new_pr = sum(post, 1);
|
wolffd@0
|
103 new_c = post' * x;
|
wolffd@0
|
104
|
wolffd@0
|
105 % Now move new estimates to old parameter vectors
|
wolffd@0
|
106 mix.priors = new_pr ./ ndata;
|
wolffd@0
|
107
|
wolffd@0
|
108 mix.centres = new_c ./ (new_pr' * ones(1, mix.nin));
|
wolffd@0
|
109
|
wolffd@0
|
110 switch mix.covar_type
|
wolffd@0
|
111 case 'spherical'
|
wolffd@0
|
112 n2 = dist2(x, mix.centres);
|
wolffd@0
|
113 for j = 1:mix.ncentres
|
wolffd@0
|
114 v(j) = (post(:,j)'*n2(:,j));
|
wolffd@0
|
115 end
|
wolffd@0
|
116 mix.covars = ((v./new_pr))./mix.nin;
|
wolffd@0
|
117 if check_covars
|
wolffd@0
|
118 % Ensure that no covariance is too small
|
wolffd@0
|
119 for j = 1:mix.ncentres
|
wolffd@0
|
120 if mix.covars(j) < MIN_COVAR
|
wolffd@0
|
121 mix.covars(j) = init_covars(j);
|
wolffd@0
|
122 end
|
wolffd@0
|
123 end
|
wolffd@0
|
124 end
|
wolffd@0
|
125 case 'diag'
|
wolffd@0
|
126 for j = 1:mix.ncentres
|
wolffd@0
|
127 diffs = x - (ones(ndata, 1) * mix.centres(j,:));
|
wolffd@0
|
128 mix.covars(j,:) = sum((diffs.*diffs).*(post(:,j)*ones(1, ...
|
wolffd@0
|
129 mix.nin)), 1)./new_pr(j);
|
wolffd@0
|
130 end
|
wolffd@0
|
131 if check_covars
|
wolffd@0
|
132 % Ensure that no covariance is too small
|
wolffd@0
|
133 for j = 1:mix.ncentres
|
wolffd@0
|
134 if min(mix.covars(j,:)) < MIN_COVAR
|
wolffd@0
|
135 mix.covars(j,:) = init_covars(j,:);
|
wolffd@0
|
136 end
|
wolffd@0
|
137 end
|
wolffd@0
|
138 end
|
wolffd@0
|
139 case 'full'
|
wolffd@0
|
140 for j = 1:mix.ncentres
|
wolffd@0
|
141 diffs = x - (ones(ndata, 1) * mix.centres(j,:));
|
wolffd@0
|
142 diffs = diffs.*(sqrt(post(:,j))*ones(1, mix.nin));
|
wolffd@0
|
143 mix.covars(:,:,j) = (diffs'*diffs)/new_pr(j);
|
wolffd@0
|
144 end
|
wolffd@0
|
145 if check_covars
|
wolffd@0
|
146 % Ensure that no covariance is too small
|
wolffd@0
|
147 for j = 1:mix.ncentres
|
wolffd@0
|
148 if min(svd(mix.covars(:,:,j))) < MIN_COVAR
|
wolffd@0
|
149 mix.covars(:,:,j) = init_covars(:,:,j);
|
wolffd@0
|
150 end
|
wolffd@0
|
151 end
|
wolffd@0
|
152 end
|
wolffd@0
|
153 case 'ppca'
|
wolffd@0
|
154 for j = 1:mix.ncentres
|
wolffd@0
|
155 diffs = x - (ones(ndata, 1) * mix.centres(j,:));
|
wolffd@0
|
156 diffs = diffs.*(sqrt(post(:,j))*ones(1, mix.nin));
|
wolffd@0
|
157 [tempcovars, tempU, templambda] = ...
|
wolffd@0
|
158 ppca((diffs'*diffs)/new_pr(j), mix.ppca_dim);
|
wolffd@0
|
159 if length(templambda) ~= mix.ppca_dim
|
wolffd@0
|
160 error('Unable to extract enough components');
|
wolffd@0
|
161 else
|
wolffd@0
|
162 mix.covars(j) = tempcovars;
|
wolffd@0
|
163 mix.U(:, :, j) = tempU;
|
wolffd@0
|
164 mix.lambda(j, :) = templambda;
|
wolffd@0
|
165 end
|
wolffd@0
|
166 end
|
wolffd@0
|
167 if check_covars
|
wolffd@0
|
168 if mix.covars(j) < MIN_COVAR
|
wolffd@0
|
169 mix.covars(j) = init_covars(j);
|
wolffd@0
|
170 end
|
wolffd@0
|
171 end
|
wolffd@0
|
172 otherwise
|
wolffd@0
|
173 error(['Unknown covariance type ', mix.covar_type]);
|
wolffd@0
|
174 end
|
wolffd@0
|
175 end
|
wolffd@0
|
176
|
wolffd@0
|
177 options(8) = -sum(log(gmmprob(mix, x)));
|
wolffd@0
|
178 if (display >= 0)
|
wolffd@0
|
179 disp(maxitmess);
|
wolffd@0
|
180 end
|
wolffd@0
|
181 |