| 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 |