%FIXEDCC Construction of fixed combiners, back-end routine % % V = FIXEDCC(A,W,TYPE,NAME,PAR) % % INPUT % A Dataset % W A set of classifier mappings % TYPE String defining the type of combination rule % NAME The name of this combination rule, arbitrary % PAR Possible parameter for combiner defined by TYPE % % OUTPUT % V Mapping % % DESCRIPTION % Define a mapping V which applies the combination rule TYPE to the % set of mappings W. The set of mappings W should be a parallel % combination (see MAPPINGS). % % TYPE defines the combining rule and can be any of the following: % average, min, max, mean, median, prod, vote, % % Note that average is only possible for affine 2-class classifiers. % % When W is a set of classifiers and A a dataset (possibly the result % of B*W, where W is again a set of classifiers) then: % % V = FIXEDCC(W,[],TYPE) combines the mappings W with the comb. rule TYPE % V = FIXEDCC(A,[],TYPE) computes the combining output for dataset A % V = FIXEDCC(A,W,TYPE) computes the combining output for dataset A, % where W is trained using A % % This is a back-end routine. Users should directly call the fixed combiners. % % EXAMPLES % See prex_combining. % % SEE ALSO (PRTools Guide) % MAPPINGS, VOTEC, MAXC, MEANC, MEDIANC, MINC, PRODC, AVERAGEC % $Id: fixedcc.m,v 1.3 2009/02/04 10:53:10 duin Exp $ function v = fixedcc(a,w,type,name,par) if nargin == 0 % Without input, just return an completely empty combiner mapping (even % without defining the combination rule): v = prmapping(mfilename,'combiner'); return end if nargin < 5, par = []; end if isempty(a) % just return a combiner mapping with just the combining rule v = prmapping(mfilename,'combiner',{[],type,name,par}); elseif isa(a,'prmapping') & isempty(w) % v = comb_classifier*fixedcc, % we combine a set of classifiers, not interesting, except for the % 'average' type, there a new affine transformation is computed: if isuntrained(a) % store untrained comb_classifier v = prmapping(mfilename,'untrained',{a,type,name,par}); else % handle or store trained comb_classifier and all info [nclass,classlist] = renumlab(getlabels(a)); % Special case is to average affine coefficients for 'average' combining switch type case 'average' if ~(isaffine(a) & size(classlist,1) == 2) error('Average combining only possible for affine 2-class classifiers') end n = length(a.data); rot = zeros(size(a,1),1); off = 0; for j=1:length(a.data) rot = rot + a.data{j}.data.rot(:,1); off = off + a.data{j}.data.offset(1); end v = affine(rot/n,off/n); otherwise % Standard procedure: make a new trained mapping v = prmapping(mfilename,'trained',{a,type,name,par}); end v = set(v,'size_in',size(a,1),'size_out',size(classlist,1),'labels',classlist); v = setcost(v,a); end elseif isa(a,'prdataset') & isempty(w) % Call like v = dataset*fixedcc, % Here the work will be done: % the dataset has already been mapped through the mappings, and the outputs % should be processed according to the combiner type. % get all the relevant parameters: [m,k] = size(a); featlist = getfeatlab(a); if isempty(featlist) prwarning(2,'No class names given: numbering inserted') nclass = [1:k]'; classlist = [1:k]'; else [nclass,classlist] = renumlab(featlist); end c = size(classlist,1); d = zeros(size(a,1),c); b = +a; % the classifier outputs to be processed %DXD: I need for my one-class classifiers that the feature domains %are retained: newfeatdom = getfeatdom(a); if ~isempty(newfeatdom) newfeatdom = newfeatdom(1:size(d,2)); end % for each of the classes the outputs should now be combined to a new % one, using the combining rule: for j=1:c J = find(nclass==j); switch type case 'min' d(:,j) = min(b(:,J),[],2); case 'max' d(:,j) = max(b(:,J),[],2); case 'mean' d(:,j) = mean(b(:,J),2); case 'prod' %d(:,j) = prod(b(:,J),2); d(:,j) = exp(sum(log(b(:,J)),2)); case 'median' d(:,j) = median(b(:,J),2); case 'vote' % Assumes that classifier outcomes are well ordered in b % For voting we cannot combine the basic classifier outputs, % but we should use the classifier labels: n = size(a,2) / c; if ~isint(n) error('All classifiers should refer to all classes') end % First get the votes for each of the classes: [dummy,fl] = renumlab(featlist); mlab = zeros(m,n); % we are in a loop over all clases, but we treat here all classes % simultaneously and break below. First run over all classifiers for j=1:n J = [(j-1)*c+1:j*c]; labels = labeld(a(:,J)); [dummy,nlab,ll] = renumlab(fl,labels); mlab(:,j) = nlab; end % Then count the number of votes for every class for j=1:c d(:,j) = (sum(mlab==j,2)+1)/(n+c); end %DXD: for this voting rule, the feature domain will change: for k=1:c newfeatdom{k} = [0 inf; 0 inf]; end % and we are done break case 'perc' d(:,j) = prctile(b(:,J),par,2); case 'average' error([newline 'Average combiner should directly call the classifiers' ... newline 'e.g. A*AVERAGEC([W1 W2 W3]), or A*([W1 W2 W3]*AVERAGEC)']) otherwise error(['Unknown method for fixed combining: ' type]) end end v = setdata(a,d,classlist); %DXD: I need for my one-class classifiers that the feature domains %are retained: if ~isempty(newfeatdom) v = setfeatdom(v,newfeatdom); end elseif (isa(a,'double') || isa(a,'prdataset')) && isa(w,'prmapping') % call like v = dataset * trained combiner (e.g. a*votec([u v w])) % This means that we first have to map the data through the mappings, and % then map this new dataset through the combiner: if strcmp(getmapping_file(w),mfilename) % Then we already have a nice combining rule: % get the relevant parameters: type = w.data{2}; name = w.data{3}; par - w.data{4}; % Evaluate the mapped data (a*w.data{1}) by this combining rule: v = feval(mfilename,a*w.data{1},[],type,name,par); else % We will use the parameters given in the argument list % evaluate the mapped data (a*w) by this combining rule: v = feval(mfilename,a*w,[],type,name,par); end else % this should not happen error('Call cannot be parsed') end if isa(v,'prmapping') v = setname(v,name); end return