wolffd@0
|
1 function [P] = cca(D, P, epochs, Mdist, alpha0, lambda0)
|
wolffd@0
|
2
|
wolffd@0
|
3 %CCA Projects data vectors using Curvilinear Component Analysis.
|
wolffd@0
|
4 %
|
wolffd@0
|
5 % P = cca(D, P, epochs, [Dist], [alpha0], [lambda0])
|
wolffd@0
|
6 %
|
wolffd@0
|
7 % P = cca(D,2,10); % projects the given data to a plane
|
wolffd@0
|
8 % P = cca(D,pcaproj(D,2),5); % same, but with PCA initialization
|
wolffd@0
|
9 % P = cca(D, 2, 10, Dist); % same, but the given distance matrix is used
|
wolffd@0
|
10 %
|
wolffd@0
|
11 % Input and output arguments ([]'s are optional):
|
wolffd@0
|
12 % D (matrix) the data matrix, size dlen x dim
|
wolffd@0
|
13 % (struct) data or map struct
|
wolffd@0
|
14 % P (scalar) output dimension
|
wolffd@0
|
15 % (matrix) size dlen x odim, the initial projection
|
wolffd@0
|
16 % epochs (scalar) training length
|
wolffd@0
|
17 % [Dist] (matrix) pairwise distance matrix, size dlen x dlen.
|
wolffd@0
|
18 % If the distances in the input space should
|
wolffd@0
|
19 % be calculated otherwise than as euclidian
|
wolffd@0
|
20 % distances, the distance from each vector
|
wolffd@0
|
21 % to each other vector can be given here,
|
wolffd@0
|
22 % size dlen x dlen. For example PDIST
|
wolffd@0
|
23 % function can be used to calculate the
|
wolffd@0
|
24 % distances: Dist = squareform(pdist(D,'mahal'));
|
wolffd@0
|
25 % [alpha0] (scalar) initial step size, 0.5 by default
|
wolffd@0
|
26 % [lambda0] (scalar) initial radius of influence, 3*max(std(D)) by default
|
wolffd@0
|
27 %
|
wolffd@0
|
28 % P (matrix) size dlen x odim, the projections
|
wolffd@0
|
29 %
|
wolffd@0
|
30 % Unknown values (NaN's) in the data: projections of vectors with
|
wolffd@0
|
31 % unknown components tend to drift towards the center of the
|
wolffd@0
|
32 % projection distribution. Projections of totally unknown vectors are
|
wolffd@0
|
33 % set to unknown (NaN).
|
wolffd@0
|
34 %
|
wolffd@0
|
35 % See also SAMMON, PCAPROJ.
|
wolffd@0
|
36
|
wolffd@0
|
37 % Reference: Demartines, P., Herault, J., "Curvilinear Component
|
wolffd@0
|
38 % Analysis: a Self-Organizing Neural Network for Nonlinear
|
wolffd@0
|
39 % Mapping of Data Sets", IEEE Transactions on Neural Networks,
|
wolffd@0
|
40 % vol 8, no 1, 1997, pp. 148-154.
|
wolffd@0
|
41
|
wolffd@0
|
42 % Contributed to SOM Toolbox 2.0, February 2nd, 2000 by Juha Vesanto
|
wolffd@0
|
43 % Copyright (c) by Juha Vesanto
|
wolffd@0
|
44 % http://www.cis.hut.fi/projects/somtoolbox/
|
wolffd@0
|
45
|
wolffd@0
|
46 % juuso 171297 040100
|
wolffd@0
|
47
|
wolffd@0
|
48 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
49 %% Check arguments
|
wolffd@0
|
50
|
wolffd@0
|
51 error(nargchk(3, 6, nargin)); % check the number of input arguments
|
wolffd@0
|
52
|
wolffd@0
|
53 % input data
|
wolffd@0
|
54 if isstruct(D),
|
wolffd@0
|
55 if strcmp(D.type,'som_map'), D = D.codebook; else D = D.data; end
|
wolffd@0
|
56 end
|
wolffd@0
|
57 [noc dim] = size(D);
|
wolffd@0
|
58 noc_x_1 = ones(noc, 1); % used frequently
|
wolffd@0
|
59 me = zeros(1,dim); st = zeros(1,dim);
|
wolffd@0
|
60 for i=1:dim,
|
wolffd@0
|
61 me(i) = mean(D(find(isfinite(D(:,i))),i));
|
wolffd@0
|
62 st(i) = std(D(find(isfinite(D(:,i))),i));
|
wolffd@0
|
63 end
|
wolffd@0
|
64
|
wolffd@0
|
65 % initial projection
|
wolffd@0
|
66 if prod(size(P))==1,
|
wolffd@0
|
67 P = (2*rand(noc,P)-1).*st(noc_x_1,1:P) + me(noc_x_1,1:P);
|
wolffd@0
|
68 else
|
wolffd@0
|
69 % replace unknown projections with known values
|
wolffd@0
|
70 inds = find(isnan(P)); P(inds) = rand(size(inds));
|
wolffd@0
|
71 end
|
wolffd@0
|
72 [dummy odim] = size(P);
|
wolffd@0
|
73 odim_x_1 = ones(odim, 1); % this is used frequently
|
wolffd@0
|
74
|
wolffd@0
|
75 % training length
|
wolffd@0
|
76 train_len = epochs*noc;
|
wolffd@0
|
77
|
wolffd@0
|
78 % random sample order
|
wolffd@0
|
79 rand('state',sum(100*clock));
|
wolffd@0
|
80 sample_inds = ceil(noc*rand(train_len,1));
|
wolffd@0
|
81
|
wolffd@0
|
82 % mutual distances
|
wolffd@0
|
83 if nargin<4 | isempty(Mdist) | all(isnan(Mdist(:))),
|
wolffd@0
|
84 fprintf(2, 'computing mutual distances\r');
|
wolffd@0
|
85 dim_x_1 = ones(dim,1);
|
wolffd@0
|
86 for i = 1:noc,
|
wolffd@0
|
87 x = D(i,:);
|
wolffd@0
|
88 Diff = D - x(noc_x_1,:);
|
wolffd@0
|
89 N = isnan(Diff);
|
wolffd@0
|
90 Diff(find(N)) = 0;
|
wolffd@0
|
91 Mdist(:,i) = sqrt((Diff.^2)*dim_x_1);
|
wolffd@0
|
92 N = find(sum(N')==dim); %mutual distance unknown
|
wolffd@0
|
93 if ~isempty(N), Mdist(N,i) = NaN; end
|
wolffd@0
|
94 end
|
wolffd@0
|
95 else
|
wolffd@0
|
96 % if the distance matrix is output from PDIST function
|
wolffd@0
|
97 if size(Mdist,1)==1, Mdist = squareform(Mdist); end
|
wolffd@0
|
98 if size(Mdist,1)~=noc,
|
wolffd@0
|
99 error('Mutual distance matrix size and data set size do not match');
|
wolffd@0
|
100 end
|
wolffd@0
|
101 end
|
wolffd@0
|
102
|
wolffd@0
|
103 % alpha and lambda
|
wolffd@0
|
104 if nargin<5 | isempty(alpha0) | isnan(alpha0), alpha0 = 0.5; end
|
wolffd@0
|
105 alpha = potency_curve(alpha0,alpha0/100,train_len);
|
wolffd@0
|
106
|
wolffd@0
|
107 if nargin<6 | isempty(lambda0) | isnan(lambda0), lambda0 = max(st)*3; end
|
wolffd@0
|
108 lambda = potency_curve(lambda0,0.01,train_len);
|
wolffd@0
|
109
|
wolffd@0
|
110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
111 %% Action
|
wolffd@0
|
112
|
wolffd@0
|
113 k=0; fprintf(2, 'iterating: %d / %d epochs\r',k,epochs);
|
wolffd@0
|
114
|
wolffd@0
|
115 for i=1:train_len,
|
wolffd@0
|
116
|
wolffd@0
|
117 ind = sample_inds(i); % sample index
|
wolffd@0
|
118 dx = Mdist(:,ind); % mutual distances in input space
|
wolffd@0
|
119 known = find(~isnan(dx)); % known distances
|
wolffd@0
|
120
|
wolffd@0
|
121 if ~isempty(known),
|
wolffd@0
|
122 % sample vector's projection
|
wolffd@0
|
123 y = P(ind,:);
|
wolffd@0
|
124
|
wolffd@0
|
125 % distances in output space
|
wolffd@0
|
126 Dy = P(known,:) - y(noc_x_1(known),:);
|
wolffd@0
|
127 dy = sqrt((Dy.^2)*odim_x_1);
|
wolffd@0
|
128
|
wolffd@0
|
129 % relative effect
|
wolffd@0
|
130 dy(find(dy==0)) = 1; % to get rid of div-by-zero's
|
wolffd@0
|
131 fy = exp(-dy/lambda(i)) .* (dx(known) ./ dy - 1);
|
wolffd@0
|
132
|
wolffd@0
|
133 % Note that the function F here is e^(-dy/lambda))
|
wolffd@0
|
134 % instead of the bubble function 1(lambda-dy) used in the
|
wolffd@0
|
135 % paper.
|
wolffd@0
|
136
|
wolffd@0
|
137 % Note that here a simplification has been made: the derivatives of the
|
wolffd@0
|
138 % F function have been ignored in calculating the gradient of error
|
wolffd@0
|
139 % function w.r.t. to changes in dy.
|
wolffd@0
|
140
|
wolffd@0
|
141 % update
|
wolffd@0
|
142 P(known,:) = P(known,:) + alpha(i)*fy(:,odim_x_1).*Dy;
|
wolffd@0
|
143 end
|
wolffd@0
|
144
|
wolffd@0
|
145 % track
|
wolffd@0
|
146 if rem(i,noc)==0,
|
wolffd@0
|
147 k=k+1; fprintf(2, 'iterating: %d / %d epochs\r',k,epochs);
|
wolffd@0
|
148 end
|
wolffd@0
|
149
|
wolffd@0
|
150 end
|
wolffd@0
|
151
|
wolffd@0
|
152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
153 %% clear up
|
wolffd@0
|
154
|
wolffd@0
|
155 % calculate error
|
wolffd@0
|
156 error = cca_error(P,Mdist,lambda(train_len));
|
wolffd@0
|
157 fprintf(2,'%d iterations, error %f \n', epochs, error);
|
wolffd@0
|
158
|
wolffd@0
|
159 % set projections of totally unknown vectors as unknown
|
wolffd@0
|
160 unknown = find(sum(isnan(D)')==dim);
|
wolffd@0
|
161 P(unknown,:) = NaN;
|
wolffd@0
|
162
|
wolffd@0
|
163 return;
|
wolffd@0
|
164
|
wolffd@0
|
165
|
wolffd@0
|
166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
167 %% tips
|
wolffd@0
|
168
|
wolffd@0
|
169 % to plot the results, use the code below
|
wolffd@0
|
170
|
wolffd@0
|
171 %subplot(2,1,1),
|
wolffd@0
|
172 %switch(odim),
|
wolffd@0
|
173 % case 1, plot(P(:,1),ones(dlen,1),'x')
|
wolffd@0
|
174 % case 2, plot(P(:,1),P(:,2),'x');
|
wolffd@0
|
175 % otherwise, plot3(P(:,1),P(:,2),P(:,3),'x'); rotate3d on
|
wolffd@0
|
176 %end
|
wolffd@0
|
177 %subplot(2,1,2), dydxplot(P,Mdist);
|
wolffd@0
|
178
|
wolffd@0
|
179 % to a project a new point x in the input space to the output space
|
wolffd@0
|
180 % do the following:
|
wolffd@0
|
181
|
wolffd@0
|
182 % Diff = D - x(noc_x_1,:); Diff(find(isnan(Diff))) = 0;
|
wolffd@0
|
183 % dx = sqrt((Diff.^2)*dim_x_1);
|
wolffd@0
|
184 % p = project_point(P,x,dx); % this function can be found from below
|
wolffd@0
|
185 % tlen = size(p,1);
|
wolffd@0
|
186 % plot(P(:,1),P(:,2),'bx',p(tlen,1),p(tlen,2),'ro',p(:,1),p(:,2),'r-')
|
wolffd@0
|
187
|
wolffd@0
|
188 % similar trick can be made to the other direction
|
wolffd@0
|
189
|
wolffd@0
|
190
|
wolffd@0
|
191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
wolffd@0
|
192 %% subfunctions
|
wolffd@0
|
193
|
wolffd@0
|
194 function vals = potency_curve(v0,vn,l)
|
wolffd@0
|
195
|
wolffd@0
|
196 % curve that decreases from v0 to vn with a rate that is
|
wolffd@0
|
197 % somewhere between linear and 1/t
|
wolffd@0
|
198 vals = v0 * (vn/v0).^([0:(l-1)]/(l-1));
|
wolffd@0
|
199
|
wolffd@0
|
200
|
wolffd@0
|
201 function error = cca_error(P,Mdist,lambda)
|
wolffd@0
|
202
|
wolffd@0
|
203 [noc odim] = size(P);
|
wolffd@0
|
204 noc_x_1 = ones(noc,1);
|
wolffd@0
|
205 odim_x_1 = ones(odim,1);
|
wolffd@0
|
206
|
wolffd@0
|
207 error = 0;
|
wolffd@0
|
208 for i=1:noc,
|
wolffd@0
|
209 known = find(~isnan(Mdist(:,i)));
|
wolffd@0
|
210 if ~isempty(known),
|
wolffd@0
|
211 y = P(i,:);
|
wolffd@0
|
212 Dy = P(known,:) - y(noc_x_1(known),:);
|
wolffd@0
|
213 dy = sqrt((Dy.^2)*odim_x_1);
|
wolffd@0
|
214 fy = exp(-dy/lambda);
|
wolffd@0
|
215 error = error + sum(((Mdist(known,i) - dy).^2).*fy);
|
wolffd@0
|
216 end
|
wolffd@0
|
217 end
|
wolffd@0
|
218 error = error/2;
|
wolffd@0
|
219
|
wolffd@0
|
220
|
wolffd@0
|
221 function [] = dydxplot(P,Mdist)
|
wolffd@0
|
222
|
wolffd@0
|
223 [noc odim] = size(P);
|
wolffd@0
|
224 noc_x_1 = ones(noc,1);
|
wolffd@0
|
225 odim_x_1 = ones(odim,1);
|
wolffd@0
|
226 Pdist = zeros(noc,noc);
|
wolffd@0
|
227
|
wolffd@0
|
228 for i=1:noc,
|
wolffd@0
|
229 y = P(i,:);
|
wolffd@0
|
230 Dy = P - y(noc_x_1,:);
|
wolffd@0
|
231 Pdist(:,i) = sqrt((Dy.^2)*odim_x_1);
|
wolffd@0
|
232 end
|
wolffd@0
|
233
|
wolffd@0
|
234 Pdist = tril(Pdist,-1);
|
wolffd@0
|
235 inds = find(Pdist > 0);
|
wolffd@0
|
236 n = length(inds);
|
wolffd@0
|
237 plot(Pdist(inds),Mdist(inds),'.');
|
wolffd@0
|
238 xlabel('dy'), ylabel('dx')
|
wolffd@0
|
239
|
wolffd@0
|
240
|
wolffd@0
|
241 function p = project_point(P,x,dx)
|
wolffd@0
|
242
|
wolffd@0
|
243 [noc odim] = size(P);
|
wolffd@0
|
244 noc_x_1 = ones(noc,1);
|
wolffd@0
|
245 odim_x_1 = ones(odim,1);
|
wolffd@0
|
246
|
wolffd@0
|
247 % initial projection
|
wolffd@0
|
248 [dummy,i] = min(dx);
|
wolffd@0
|
249 y = P(i,:)+rand(1,odim)*norm(P(i,:))/20;
|
wolffd@0
|
250
|
wolffd@0
|
251 % lambda
|
wolffd@0
|
252 lambda = norm(std(P));
|
wolffd@0
|
253
|
wolffd@0
|
254 % termination
|
wolffd@0
|
255 eps = 1e-3; i_max = noc*10;
|
wolffd@0
|
256
|
wolffd@0
|
257 i=1; p(i,:) = y;
|
wolffd@0
|
258 ready = 0;
|
wolffd@0
|
259 while ~ready,
|
wolffd@0
|
260
|
wolffd@0
|
261 % mutual distances
|
wolffd@0
|
262 Dy = P - y(noc_x_1,:); % differences in output space
|
wolffd@0
|
263 dy = sqrt((Dy.^2)*odim_x_1); % distances in output space
|
wolffd@0
|
264 f = exp(-dy/lambda);
|
wolffd@0
|
265
|
wolffd@0
|
266 fprintf(2,'iteration %d, error %g \r',i,sum(((dx - dy).^2).*f));
|
wolffd@0
|
267
|
wolffd@0
|
268 % all the other vectors push the projected one
|
wolffd@0
|
269 fy = f .* (dx ./ dy - 1) / sum(f);
|
wolffd@0
|
270
|
wolffd@0
|
271 % update
|
wolffd@0
|
272 step = - sum(fy(:,odim_x_1).*Dy);
|
wolffd@0
|
273 y = y + step;
|
wolffd@0
|
274
|
wolffd@0
|
275 i=i+1;
|
wolffd@0
|
276 p(i,:) = y;
|
wolffd@0
|
277 ready = (norm(step)/norm(y) < eps | i > i_max);
|
wolffd@0
|
278
|
wolffd@0
|
279 end
|
wolffd@0
|
280 fprintf(2,'\n');
|
wolffd@0
|
281
|
wolffd@0
|
282 |