├── DEMO_caltech101.m ├── LICENSE.md ├── MV_leastsquare ├── MV_MC_leastsquare_testing.m ├── MV_MC_leastsquare_training.m ├── MYlaplacian.m ├── labels2vec.m └── make_boldG_Laplacian.m ├── README.md ├── addlibs ├── Load_Kernels.m ├── buildEvaluationData.m ├── buildTrainingData.m ├── performance_Y.m ├── plot_confMat.m └── rotateXLabels │ ├── license.txt │ └── rotateXLabels.m └── paper ├── ICML_2013_Manifold_Multiview_SupplementaryMaterial_Final.pdf └── ICML_2013_Manifold_Multiview_final.pdf /DEMO_caltech101.m: -------------------------------------------------------------------------------- 1 | %% Multiview Multiclass Least Square Learning 2 | % Test on Caltech 101 using Vedaldi's kernels 3 | clear; close all; 4 | 5 | %% Load the toolbox and additional libs for the experiment 6 | TOOLBOXPATH = './'; % absolute path where the toolbox is located 7 | addpath([TOOLBOXPATH 'MV_leastsquare/']) % load the toolbox 8 | addpath(genpath([TOOLBOXPATH 'addlibs/'])) % load the add libs 9 | 10 | 11 | %% Set dataset and kernel set paths 12 | KERNELPATH = './'; 13 | 14 | 15 | %% Choose which experience to run 16 | PLOT_CONFMAT = 0; % display confusion matrix at each step 17 | viewlist= [3,6,9,10]; % select the view you want to use (indexes 1...10) 18 | labdata = 5; % training data: max 15 per class 19 | uc = 1; % unlabeled: must be (15 - labdata) 20 | 21 | 22 | %% PARAMETERS of the multiview multiclass least square learning 23 | params_mv.gA = 10^(-5); % RKHS regularization 24 | params_mv.gB = 10^(-6); % between-view regularization 25 | params_mv.gW = 10^(-6); % within-view regularization 26 | params_mv.c = ones(length(viewlist),1)/length(viewlist); % uniform weighting to build the matrix C 27 | params_mv.Laplacian.GraphNormalize = 1; % parameter to compute the laplacian 28 | 29 | % check consistency 30 | if(15-labdata AVERAGE Accuracy: %3.2f%% (%3.2f%%)\n\n', (1-results.errors_test_avg)*100,results.errors_test_std*100); 74 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Loris Bazzani and Minh Ha Quang 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MV_leastsquare/MV_MC_leastsquare_testing.m: -------------------------------------------------------------------------------- 1 | function [estimatedLabels,confidence,labels] = MV_MC_leastsquare_testing(bG_test,At,C,P,m) 2 | % [estimatedLabels,confidence,labels] = MV_MC_leastsquare_testing(bG_test,At,C,P,m) 3 | % Given a solution of the multi-view least square problems, this function 4 | % estimate the labels of new testing examples. 5 | % 6 | % Input: 7 | % - bG_test: \mathbf{G} matrix (see paper, paragraph "Evaluation on a Testing Sample") 8 | % - At: solution of the learning problem (see paper, eq. 43-44) 9 | % - C: Combination operator 10 | % - P: # of classes 11 | % - m: # of views 12 | % 13 | % Output: 14 | % - estimatedLabels: estimated vector values 15 | % - confidence: confidence of classification 16 | % - labels: estimated labels (max of estimatedLabels) 17 | % 18 | 19 | % Loris Bazzani, Minh Ha Quang 20 | 21 | 22 | fprintf('-- Testing ') 23 | tic 24 | 25 | t = size(bG_test,1)/m; 26 | 27 | estimatedLabels = zeros(P,t,'single'); 28 | batch_size = 100; 29 | 30 | % Evaluation Testing 31 | jj = 1; 32 | for i=1:batch_size:t % For one test sample 33 | 34 | idsF = [(i-1)*m+1:min((i+batch_size-1)*m,t*m)]; 35 | 36 | bG_test_tmp = bG_test(idsF,:); 37 | 38 | testKernelTimesA = At*bG_test_tmp'; % of size P*m 39 | for j = 1:m:length(idsF) 40 | testKernelTimesA_now = testKernelTimesA(:,j:j+m-1); 41 | testKernelTimesA_now = testKernelTimesA_now(:); 42 | estimatedLabels(:, jj) = C*testKernelTimesA_now; 43 | jj = jj+1; 44 | end 45 | end 46 | 47 | [confidence,labels] = max(estimatedLabels); % assign labels from vector 48 | 49 | tm = toc; 50 | fprintf('time %3.2f s',tm); 51 | 52 | 53 | -------------------------------------------------------------------------------- /MV_leastsquare/MV_MC_leastsquare_training.m: -------------------------------------------------------------------------------- 1 | function [At,C] = MV_MC_leastsquare_training(bG_trn,L,Ytrain,params_mv,P,l,u,m) 2 | % [At,C] = MV_MC_leastsquare_training(bG_trn,L,Ytrain,params_mv,P,l,u,m) 3 | % Multiview multiclass learning method that is based on least square loss 4 | % function. 5 | % 6 | % Input: 7 | % - bG_trn: \mathbf{G} matrix (see paper, Eq. 40-42) 8 | % - L: multiview Laplacian \mathbf{L} (see paper, Eq. 34-38) 9 | % - Ytrain: Vector representation of the labels (zeros for unlabeled data) 10 | % - params_mv: parameters of the multiview learning method 11 | % - P: # of classes 12 | % - l: # labeled samples 13 | % - u: # unlabeled samples 14 | % - m: # of views 15 | % 16 | % Output: 17 | % - At: solution of the learning problem (see paper, eq. 43-44) 18 | % - C: Combination operator 19 | % 20 | 21 | % Loris Bazzani, Minh Ha Quang 22 | 23 | 24 | fprintf('Training ') 25 | tic; 26 | 27 | Ck = single(eye(P)); 28 | C = kron(single(params_mv.c'), Ck); % Minh 29 | 30 | %% Mm square matrix of size (m,m) 31 | Mm = single(m*eye(m) - ones(m)); 32 | 33 | %% J: diagonal matrix with l entries at 1 and u entries at 0 (ordered in 34 | % accordance to Y) 35 | J = diag(abs(single(Ytrain(1,:)))); 36 | 37 | %% eq 22 38 | % tic, fprintf('Computing B matrix... '), 39 | B = (kron(J, single(params_mv.c*params_mv.c')) + l*params_mv.gB*(kron(eye(u+l,'single'),Mm))+l*params_mv.gW*L)*bG_trn ... 40 | + l*params_mv.gA*eye((u+l)*m,'single'); % Minh 41 | 42 | 43 | Yct = reshape(C'*single(Ytrain), P, (u+l)*m); 44 | Yc = Yct'; 45 | 46 | % tic, fprintf('Solving for A... '), 47 | A = B \ Yc; 48 | At = A'; 49 | % toc, 50 | 51 | tm = toc; 52 | fprintf('time %3.2f s ',tm); -------------------------------------------------------------------------------- /MV_leastsquare/MYlaplacian.m: -------------------------------------------------------------------------------- 1 | function [L,options] = MYlaplacian(options,W) 2 | 3 | % LAPLACIAN Computes the Graph Laplacian of the adjacency graph 4 | % of a data set X 5 | % 6 | % [L,options]=laplacian(options,data) 7 | % 8 | % Inputs: 9 | % 10 | % W : kernel 11 | % 12 | % options: a data structure with the following fields 13 | % (type help ml_options) 14 | % 15 | % options.NORMALIZE= 0 | 1 16 | % (0 for un-normalized and 1 for normalized graph laplacian) 17 | % 18 | % Output 19 | % L : sparse symmetric NxN matrix 20 | % options: updated options structure (options) 21 | % 22 | % Notes: Calls adjacency.m to construct the adjacency graph. This is 23 | % fully vectorized for fast performance. 24 | % 25 | % Author: 26 | % Vikas Sindhwani (vikass@cs.uchicago.edu) 27 | % [modified from Misha Belkin's code] 28 | % Modified by Loris Bazzani 29 | 30 | 31 | % tic; 32 | 33 | D = sum(W(:,:),2); 34 | 35 | if options.GraphNormalize==0 36 | L = spdiags(D,0,speye(size(W,1)))-W; 37 | else % normalized laplacian 38 | % fprintf(1, 'Normalizing the Graph Laplacian\n'); 39 | D(find(D))=sqrt(1./D(find(D))); 40 | D=spdiags(D,0,speye(size(W,1))); 41 | W=D*W*D; 42 | L=speye(size(W,1))-W; 43 | end 44 | 45 | % fprintf(1,['Graph Laplacian computation took ' num2str(toc) ' seconds.\n']); 46 | -------------------------------------------------------------------------------- /MV_leastsquare/labels2vec.m: -------------------------------------------------------------------------------- 1 | function Yvec = labels2vec(labels,P) 2 | 3 | 4 | Yvec = -ones(P,length(labels)); 5 | for i = 1:P 6 | Yvec(i,labels==i) = 1; 7 | end -------------------------------------------------------------------------------- /MV_leastsquare/make_boldG_Laplacian.m: -------------------------------------------------------------------------------- 1 | function [bG,L] = make_boldG_Laplacian(Kernel,params_mv,l,u,m,phase,Lapl) 2 | % [bG,L] = make_boldG_Laplacian(Kernel,params_mv,l,u,m,phase,Lapl) 3 | % Given the Kernel matrix, this function creates the matrices useful for the 4 | % training and the testing, that are, \mathbf{G} and \mathbf{L}. 5 | % 6 | % Input: 7 | % - Kernel: precoputed kernel matrix 8 | % - params_mv: parameters of the multiview learning method 9 | % - l: # labeled samples 10 | % - u: # unlabeled samples 11 | % - m: # of views 12 | % - phase: 'training' or 'testing', building is slightly different (see the code) 13 | % - Lapl (optional): the Laplacian can be built from the data directly. 14 | % Here we use the kernel, but we suggest to use the feature vectors. 15 | % 16 | % Output: 17 | % - bG: \mathbf{G} matrix (see paper, Eq. 40-42) 18 | % - L: multiview Laplacian \mathbf{L} (see paper, Eq. 34-38) 19 | % 20 | 21 | % Loris Bazzani, Minh Ha Quang 22 | 23 | 24 | if strcmp(phase,'training') % training 25 | 26 | bG = single(zeros(m*(l+u),m*(l+u))); 27 | 28 | else % testing 29 | t = size(Kernel,3); 30 | 31 | bG = single(zeros(m*t,m*(l+u))); 32 | end 33 | L = single(zeros(m*(l+u),m*(l+u))); 34 | 35 | 36 | % build bG and L 37 | for mi = 1:m 38 | idx_trn = mi:m:m*(l+u); 39 | 40 | if strcmp(phase,'training') % training 41 | 42 | bG(idx_trn,idx_trn) = single(Kernel(mi,:,:)); 43 | 44 | if nargin<=7 45 | L(idx_trn,idx_trn) = single(MYlaplacian(params_mv.Laplacian, squeeze(Kernel(mi,:,:)))); 46 | else % if you have your own laplacian 47 | L(idx_trn,idx_trn) = single(Lapl(mi,:,:)); 48 | end 49 | 50 | else % testing 51 | 52 | idx_tst = mi:m:m*t; 53 | 54 | bG(idx_tst,idx_trn) = single(squeeze(Kernel(mi,:,:)))'; 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Multiview-learning 2 | ================== 3 | In this package, you find a updated version of the MATLAB code for following paper: 4 | 5 | **A unifying framework for vector-valued manifold regularization and multi-view learning**, 6 | Minh, H. Q., Bazzani, L., and Murino, V., 7 | *In Proceedings of the 30th International Conference on Machine Learning (ICML-13) (pp. 100-108)* 8 | 9 | Please, cite our paper if you use our code: 10 | ``` 11 | @inproceedings{quang2013unifying, 12 | title={A unifying framework for vector-valued manifold regularization and multi-view learning}, 13 | author={Minh, Ha Quang and Bazzani, Loris and Murino, Vittorio}, 14 | booktitle={Proceedings of the 30th International Conference on Machine Learning (ICML-13)}, 15 | pages={100--108}, 16 | year={2013} 17 | } 18 | ``` 19 | 20 | ##REPLICATE EXPERIMENTS 21 | With the toolbox that can be used in combination with your data and kernels, we provide the demo code 22 | that replicates the experiment on the Caltech-101 dataset. 23 | It is very simple: 24 | 25 | 1. download the kernels and metadata provided here (the 15-sample files): 26 | http://www.robots.ox.ac.uk/~vgg/software/MKL/ 27 | 2. put the uncompressed folders in the folder named kernels 28 | 3. open `DEMO_caltech101.m` 29 | 4. modify the TOOLBOXPATH and the KERNELPATH (if needed) 30 | 5. run the code 31 | 32 | 33 | ##USE THE CODE AS BLACKBOX 34 | You can also use the code using your data with the following steps: 35 | 36 | 0. Load the toolbox and additional libs for the experiment 37 | ``` 38 | TOOLBOXPATH = './'; % absolute path where the toolbox is located 39 | addpath([TOOLBOXPATH 'MV_leastsquare/']) % load the toolbox 40 | ``` 41 | 42 | 1. Set PARAMETERS 43 | ``` 44 | params_mv.gA = % RKHS regularization 45 | params_mv.gB = % between-view regularization 46 | params_mv.gW = % within-view regularization 47 | params_mv.c = % vector to build the matrix C (length of the list of views) 48 | params_mv.Laplacian.GraphNormalize = % parameter to compute the laplacian 49 | ``` 50 | 51 | 2. Load your data and compute the following quantities 52 | ``` 53 | l = % # labeled samples 54 | u = % # unlabeled samples 55 | m = % # of views 56 | P = % # of classes, i.e., length of the output vector 57 | K_trn = % training kernel, size = (#views, #samplestrain, #samplestrain) -> labeled first, unlabeled at the end 58 | K_tst = % testing kernel, size = (#views, #samplestrain, #samplestest) 59 | Ytrain = % vector-value representation of the labels of the training samples (use labels2vec.m) % NOTE: it has to have zeros corresponding to the unlabeled samples 60 | YGT = % labels of the testing samples 61 | ``` 62 | 63 | 3. Training 64 | ``` 65 | [bG_trn, L] = make_boldG_Laplacian(K_trn,params_mv,l,u,m,'training'); 66 | % [bG_trn, L] = make_boldG_Laplacian(K_trn,params_mv,l,u,m,'training', Lapl); % in the case you have you Laplacian 67 | [At, C] = MV_MC_leastsquare_training(bG_trn,L,Ytrain,params_mv,P,l,u,m); 68 | ``` 69 | 70 | 4. Testing 71 | ``` 72 | [bG_tst, ~] = make_boldG_Laplacian(K_tst,params_mv,l,u,m,'testing'); 73 | [estimatedLabels, conf, Y_est] = MV_MC_leastsquare_testing(bG_tst,At,C,P,m); 74 | ``` 75 | 76 | 5. Compute classification statistics 77 | ``` 78 | [error_t, errorC] = performance_Y(Y_est,YGT); 79 | ``` 80 | -------------------------------------------------------------------------------- /addlibs/Load_Kernels.m: -------------------------------------------------------------------------------- 1 | paper = 'caltech101'; 2 | 3 | fprintf('* Loading Kernel Matrices...') 4 | tic; 5 | 6 | switch paper 7 | 8 | case 'caltech101' 9 | splitlist = dir([KERNELPATH 'kernels/cal101-ker*']); 10 | nRUNS = length(splitlist); % number of splits 11 | 12 | metalist = dir([KERNELPATH 'kernels/cal101-meta/*.mat']); 13 | P = 102; 14 | 15 | Y = struct([]); 16 | 17 | for s = 1:nRUNS 18 | % load the pre-computed kernels (Vedaldi's MKL paper) 19 | kernlist = dir([KERNELPATH 'kernels/' splitlist(s).name '/*.mat']); 20 | nKern = length(kernlist)/2; 21 | parfor k = 1:nKern % training 22 | kernpath = [KERNELPATH 'kernels/' splitlist(s).name '/' kernlist(k).name]; 23 | Kernels_trn{s,k} = load(kernpath); 24 | Kernels_trn{s,k}.path = kernpath; 25 | end 26 | parfor k = 1:nKern % testing 27 | kernpath = [KERNELPATH 'kernels/' splitlist(s).name '/' kernlist(nKern + k).name]; 28 | Kernels_tst{s,k} = load(kernpath); 29 | Kernels_tst{s,k}.path = kernpath; 30 | end 31 | 32 | % load metadata 33 | metadt(s) = load([KERNELPATH 'kernels/cal101-meta/' metalist(s).name]); 34 | 35 | % Make the Y labels 36 | Y(s).train = labels2vec(metadt(s).trainImageClasses,P); 37 | Y(s).test = labels2vec(metadt(s).testImageClasses,P); 38 | 39 | % Y(s).train = -ones(P,length(metadt(s).trainImageClasses)); 40 | % Y(s).test = -ones(P,length(metadt(s).testImageClasses)); 41 | % for i = 1:P 42 | % Y(s).train(i,metadt(s).trainImageClasses==i) = 1; 43 | % Y(s).test(i,metadt(s).testImageClasses==i) = 1; 44 | % end 45 | end 46 | 47 | nSamples = length(metadt(1).trainImageClasses); 48 | nSperClass = nSamples/P; 49 | l = labdata*P*2; % twice because of the trasformed data 50 | u = uc*P*2; 51 | t = length(metadt(1).testImageClasses); 52 | m = length(viewlist); % 4 views = 4 features 53 | 54 | % extract the classes name (optional for visualization) 55 | c = 1; 56 | className = cell(P,1); 57 | for i = 1:length(metadt(1).trainImageNames)/2 58 | idxcut = strfind(metadt(1).trainImageNames(i),'image'); 59 | idxcut = idxcut{1} - 2; % avoid the "_" char 60 | if i == 1 61 | className{c} = metadt(1).trainImageNames{i}(1:idxcut); 62 | aa = strfind(className{c},'_'); 63 | className{c}(aa) = ' '; 64 | c = c + 1; 65 | else 66 | TMP = metadt(1).trainImageNames{i}(1:idxcut); 67 | aa = strfind(TMP,'_'); 68 | TMP(aa) = ' '; 69 | if ~strcmp(TMP,className{c-1}) 70 | className{c} = TMP; 71 | c = c + 1; 72 | end 73 | end 74 | 75 | end 76 | 77 | 78 | otherwise 79 | error(['Unrecognized experiment [number/feature/type] ' datasetN]); 80 | end 81 | 82 | 83 | tm = toc; 84 | fprintf(' Done, time %3.2f s\n',tm) 85 | -------------------------------------------------------------------------------- /addlibs/buildEvaluationData.m: -------------------------------------------------------------------------------- 1 | %% BUILD THE DATA testing 2 | K_tst = zeros(m,length(lab_idx),t); 3 | for mi = 1:m 4 | K_tst(mi,:,:) = Kernels_tst{nRun,viewlist(mi)}.matrix(lab_idx,:); 5 | end 6 | 7 | Ytest = Y(nRun).test; 8 | [~,YGT] = max(Ytest); 9 | -------------------------------------------------------------------------------- /addlibs/buildTrainingData.m: -------------------------------------------------------------------------------- 1 | %% BUILD THE training DATA 2 | 3 | % compute the indexes of LABELED data to keep 4 | lab_idx_lab = zeros(l,1); 5 | lab_idx_unlab = zeros(u,1); 6 | for i = 1:P % select a subset of unlabeled data based on labels 7 | lab_idx2 = find(Y(nRun).train(i,:)==1); 8 | lab_idx3 = lab_idx2(1:length(lab_idx2)/2); 9 | lab_idx3l = lab_idx3(1:labdata); % labeled 10 | lab_idx3u = lab_idx3(labdata+1:labdata+uc); % unlabeled 11 | 12 | lab_idx3t = lab_idx2(length(lab_idx2)/2+1:end); % transformed images 13 | lab_idx3tl = lab_idx3t(1:labdata); % labeled 14 | lab_idx3tu = lab_idx3t(labdata+1:labdata+uc); % unlabeled 15 | 16 | lab_idx_lab((i-1)*labdata*2+1:i*labdata*2) = [lab_idx3l,lab_idx3tl]; 17 | 18 | lab_idx_unlab((i-1)*uc*2+1:i*uc*2) = [lab_idx3u,lab_idx3tu]; 19 | end 20 | lab_idx = [lab_idx_lab; lab_idx_unlab]; 21 | 22 | K_trn = zeros(m,length(lab_idx),length(lab_idx)); 23 | for mi = 1:m 24 | K_trn(mi,:,:) = Kernels_trn{nRun,viewlist(mi)}.matrix(lab_idx,lab_idx); 25 | end 26 | 27 | Ytrain = Y(nRun).train(:,lab_idx); 28 | Ytrain(:,l+1:end) = 0; % remove labels from unlabeled data 29 | -------------------------------------------------------------------------------- /addlibs/performance_Y.m: -------------------------------------------------------------------------------- 1 | function [error,errorC] = performance_Y(guess,y) 2 | % [error,errorC] = performance_Y(guess,y) 3 | % This function computes the (averaged) classification error given the 4 | % estimates of the samples and the ground truth. 5 | % 6 | % Input: 7 | % guess: estimated labels 8 | % y: ground truth labels 9 | % 10 | % Output: 11 | % error: averaged classification error 12 | % errorC: classification error for each class 13 | % 14 | 15 | % Loris Bazzani, Minh Ha Quang 16 | 17 | % overall accuracy 18 | error=1-sum(guess==y)/length(y); 19 | 20 | % single-class accuracies 21 | errorC = zeros(max(y),1); 22 | for i = 1:max(y) 23 | errorC(i)=1-sum(guess(y==i)==i)/sum(y==i); 24 | end 25 | 26 | fprintf(' -- Accuracy: %3.2f%% \n', (1-error)*100); 27 | -------------------------------------------------------------------------------- /addlibs/plot_confMat.m: -------------------------------------------------------------------------------- 1 | % -------------------------------------------------------------------- 2 | % Plot results 3 | % -------------------------------------------------------------------- 4 | % Compute the confusion matrix 5 | idx = sub2ind([nClasses, nClasses], ... 6 | YGT, Y_est) ; 7 | confus = zeros(nClasses) ; 8 | confus = vl_binsum(confus, ones(size(idx)), idx) ; 9 | 10 | % Plots 11 | figure(1) ; clf; 12 | imagesc(confus) ; 13 | % set(gca,'XTick',1:length(className)) 14 | % set(gca,'XTickLabel',className) 15 | % rotateXLabels(gca, 90); 16 | set(gca,'YTick',1:length(className)) 17 | set(gca,'YTickLabel',className) 18 | rotateXLabels(gca, 90); 19 | title(sprintf('Confusion matrix (%.2f %% accuracy)', ... 20 | 100 * mean(diag(confus)/(t/nClasses)) )) ; 21 | 22 | % plot the error for each class 23 | figure(2); clf 24 | bar(errorC) 25 | set(gca,'XTick',1:length(className)) 26 | set(gca,'XTickLabel',className) 27 | rotateXLabels(gca, 90); -------------------------------------------------------------------------------- /addlibs/rotateXLabels/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2011, The MathWorks, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | * Neither the name of the The MathWorks, Inc. nor the names 14 | of its contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /addlibs/rotateXLabels/rotateXLabels.m: -------------------------------------------------------------------------------- 1 | function hh = rotateXLabels( ax, angle, varargin ) 2 | %rotateXLabels: rotate any xticklabels 3 | % 4 | % hh = rotateXLabels(ax,angle) rotates all XLabels on axes AX by an angle 5 | % ANGLE (in degrees). Handles to the resulting text objects are returned 6 | % in HH. 7 | % 8 | % hh = rotateXLabels(ax,angle,param,value,...) also allows one or more 9 | % optional parameters to be specified. Possible parameters are: 10 | % 'MaxStringLength' The maximum length of label to show (default inf) 11 | % 12 | % Examples: 13 | % >> bar( hsv(5)+0.05 ) 14 | % >> days = {'Monday','Tuesday','Wednesday','Thursday','Friday'}; 15 | % >> set( gca(), 'XTickLabel', days ) 16 | % >> rotateXLabels( gca(), 45 ) 17 | % 18 | % See also: GCA 19 | % BAR 20 | 21 | % Copyright 2006-2012 The MathWorks Ltd. 22 | 23 | error( nargchk( 2, inf, nargin ) ); 24 | if ~isnumeric( angle ) || ~isscalar( angle ) 25 | error( 'RotateXLabels:BadAngle', 'Parameter ANGLE must be a scalar angle in degrees' ) 26 | end 27 | angle = mod( angle, 360 ); 28 | 29 | [maxStringLength] = parseInputs( varargin{:} ); 30 | 31 | % Get the existing label texts and clear them 32 | [vals, labels] = findAndClearExistingLabels( ax, maxStringLength ); 33 | 34 | % Create the new label texts 35 | h = createNewLabels( ax, vals, labels, angle ); 36 | 37 | % Reposition the axes itself to leave space for the new labels 38 | repositionAxes( ax ); 39 | 40 | % If an X-label is present, move it too 41 | repositionXLabel( ax ); 42 | 43 | % Store angle 44 | setappdata( ax, 'RotateXLabelsAngle', angle ); 45 | 46 | % Only send outputs if requested 47 | if nargout 48 | hh = h; 49 | end 50 | 51 | %-------------------------------------------------------------------------% 52 | function [maxStringLength] = parseInputs( varargin ) 53 | % Parse optional inputs 54 | maxStringLength = inf; 55 | if nargin > 0 56 | params = varargin(1:2:end); 57 | values = varargin(2:2:end); 58 | if numel( params ) ~= numel( values ) 59 | error( 'RotateXLabels:BadSyntax', 'Optional arguments must be specified as parameter-value pairs.' ); 60 | end 61 | if any( ~cellfun( 'isclass', params, 'char' ) ) 62 | error( 'RotateXLabels:BadSyntax', 'Optional argument names must be specified as strings.' ); 63 | end 64 | for pp=1:numel( params ) 65 | switch upper( params{pp} ) 66 | case 'MAXSTRINGLENGTH' 67 | maxStringLength = values{pp}; 68 | 69 | otherwise 70 | error( 'RotateXLabels:BadParam', 'Optional parameter ''%s'' not recognised.', params{pp} ); 71 | end 72 | end 73 | end 74 | end % parseInputs 75 | %-------------------------------------------------------------------------% 76 | function [vals,labels] = findAndClearExistingLabels( ax, maxStringLength ) 77 | % Get the current tick positions so that we can place our new labels 78 | vals = get( ax, 'XTick' ); 79 | 80 | % Now determine the labels. We look first at for previously rotated labels 81 | % since if there are some the actual labels will be empty. 82 | ex = findall( ax, 'Tag', 'RotatedXTickLabel' ); 83 | if isempty( ex ) 84 | % Store the positions and labels 85 | labels = get( ax, 'XTickLabel' ); 86 | if isempty( labels ) 87 | % No labels! 88 | return 89 | else 90 | if ~iscell(labels) 91 | labels = cellstr(labels); 92 | end 93 | end 94 | % Clear existing labels so that xlabel is in the right position 95 | set( ax, 'XTickLabel', {}, 'XTickMode', 'Manual' ); 96 | setappdata( ax, 'OriginalXTickLabels', labels ); 97 | else 98 | % Labels have already been rotated, so capture them 99 | labels = getappdata( ax, 'OriginalXTickLabels' ); 100 | delete(ex); 101 | end 102 | % Limit the length, if requested 103 | if isfinite( maxStringLength ) 104 | for ll=1:numel( labels ) 105 | if length( labels{ll} ) > maxStringLength 106 | labels{ll} = labels{ll}(1:maxStringLength); 107 | end 108 | end 109 | end 110 | 111 | end % findAndClearExistingLabels 112 | 113 | %-------------------------------------------------------------------------% 114 | function textLabels = createNewLabels( ax, vals, labels, angle ) 115 | % Work out the ticklabel positions 116 | zlim = get(ax,'ZLim'); 117 | z = zlim(1); 118 | 119 | % We want to work in normalised coords, but this doesn't print 120 | % correctly. Instead we have to work in data units even though it 121 | % makes positioning hard. 122 | ylim = get( ax, 'YLim' ); 123 | if strcmpi( get( ax, 'XAxisLocation' ), 'Top' ) 124 | y = ylim(2); 125 | else 126 | y = ylim(1); 127 | end 128 | 129 | % Now create new text objects in similar positions. 130 | textLabels = -1*ones( numel( vals ), 1 ); 131 | for ll=1:numel(vals) 132 | textLabels(ll) = text( ... 133 | 'Units', 'Data', ... 134 | 'Position', [vals(ll), y, z], ... 135 | 'String', labels{ll}, ... 136 | 'Parent', ax, ... 137 | 'Clipping', 'off', ... 138 | 'Rotation', angle, ... 139 | 'Tag', 'RotatedXTickLabel', ... 140 | 'UserData', vals(ll) ); 141 | end 142 | 143 | % Now copy font properties into the texts 144 | updateFont(); 145 | % Update the alignment of the text 146 | updateAlignment(); 147 | 148 | end % createNewLabels 149 | 150 | %-------------------------------------------------------------------------% 151 | function repositionAxes( ax ) 152 | % Reposition the axes so that there's room for the labels 153 | % Note that we only do this if the OuterPosition is the thing being 154 | % controlled 155 | if ~strcmpi( get( ax, 'ActivePositionProperty' ), 'OuterPosition' ) 156 | return; 157 | end 158 | 159 | % Work out the maximum height required for the labels 160 | labelHeight = getLabelHeight(ax); 161 | 162 | % Remove listeners while we mess around with things, otherwise we'll 163 | % trigger redraws recursively 164 | removeListeners( ax ); 165 | 166 | % Change to normalized units for the position calculation 167 | oldUnits = get( ax, 'Units' ); 168 | set( ax, 'Units', 'Normalized' ); 169 | 170 | % Not sure why, but the extent seems to be proportional to the height of the axes. 171 | % Correct that now. 172 | set( ax, 'ActivePositionProperty', 'Position' ); 173 | pos = get( ax, 'Position' ); 174 | axesHeight = pos(4); 175 | % Make sure we don't adjust away the axes entirely! 176 | heightAdjust = min( (axesHeight*0.9), labelHeight*axesHeight ); 177 | 178 | % Move the axes 179 | if isappdata( ax, 'OriginalAxesPosition' ) 180 | pos = getappdata( ax, 'OriginalAxesPosition' ); 181 | else 182 | pos = get(ax,'Position'); 183 | setappdata( ax, 'OriginalAxesPosition', pos ); 184 | end 185 | if strcmpi( get( ax, 'XAxisLocation' ), 'Bottom' ) 186 | % Move it up and reduce the height 187 | set( ax, 'Position', pos+[0 heightAdjust 0 -heightAdjust] ) 188 | else 189 | % Just reduce the height 190 | set( ax, 'Position', pos+[0 0 0 -heightAdjust] ) 191 | end 192 | set( ax, 'Units', oldUnits ); 193 | set( ax, 'ActivePositionProperty', 'OuterPosition' ); 194 | 195 | % Make sure we find out if axes properties are changed 196 | addListeners( ax ); 197 | 198 | end % repositionAxes 199 | 200 | %-------------------------------------------------------------------------% 201 | function repositionXLabel( ax ) 202 | % Try to work out where to put the xlabel 203 | removeListeners( ax ); 204 | labelHeight = getLabelHeight(ax); 205 | 206 | % Use the new max extent to move the xlabel. We may also need to 207 | % move the title 208 | xlab = get(ax,'XLabel'); 209 | titleh = get( ax, 'Title' ); 210 | set( [xlab,titleh], 'Units', 'Normalized' ); 211 | if strcmpi( get( ax, 'XAxisLocation' ), 'Top' ) 212 | titleExtent = get( xlab, 'Extent' ); 213 | set( xlab, 'Position', [0.5 1+labelHeight-titleExtent(4) 0] ); 214 | set( titleh, 'Position', [0.5 1+labelHeight 0] ); 215 | else 216 | set( xlab, 'Position', [0.5 -labelHeight 0] ); 217 | set( titleh, 'Position', [0.5 1 0] ); 218 | end 219 | addListeners( ax ); 220 | end % repositionXLabel 221 | 222 | %-------------------------------------------------------------------------% 223 | function height = getLabelHeight(ax) 224 | textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' ); 225 | height = 0; 226 | oldUnits = get( textLabels(1), 'Units' ); 227 | set( textLabels, 'Units', 'Normalized' ); 228 | for ll=1:numel(vals) 229 | ext = get( textLabels(ll), 'Extent' ); 230 | if ext(4) > height 231 | height = ext(4); 232 | end 233 | end 234 | set( textLabels, 'Units', oldUnits ); 235 | end % getLabelExtent 236 | 237 | %-------------------------------------------------------------------------% 238 | function updateFont() 239 | % Update the rotated text fonts when the axes font changes 240 | properties = { 241 | 'FontName' 242 | 'FontSize' 243 | 'FontAngle' 244 | 'FontWeight' 245 | 'FontUnits' 246 | }; 247 | propertyValues = get( ax, properties ); 248 | textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' ); 249 | set( textLabels, properties, propertyValues ); 250 | end % updateFont 251 | 252 | function updateAlignment() 253 | textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' ); 254 | angle = get( textLabels(1), 'Rotation' ); 255 | % Depending on the angle, we may need to change the alignment. We change 256 | % alignments within 5 degrees of each 90 degree orientation. 257 | if strcmpi( get( ax, 'XAxisLocation' ), 'Top' ) 258 | if 0 <= angle && angle < 5 259 | set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' ); 260 | elseif 5 <= angle && angle < 85 261 | set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' ); 262 | elseif 85 <= angle && angle < 95 263 | set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' ); 264 | elseif 95 <= angle && angle < 175 265 | set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' ); 266 | elseif 175 <= angle && angle < 185 267 | set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' ); 268 | elseif 185 <= angle && angle < 265 269 | set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' ); 270 | elseif 265 <= angle && angle < 275 271 | set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' ); 272 | elseif 275 <= angle && angle < 355 273 | set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' ); 274 | else % 355-360 275 | set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' ); 276 | end 277 | else 278 | if 0 <= angle && angle < 5 279 | set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' ); 280 | elseif 5 <= angle && angle < 85 281 | set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Top' ); 282 | elseif 85 <= angle && angle < 95 283 | set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Middle' ); 284 | elseif 95 <= angle && angle < 175 285 | set( textLabels, 'HorizontalAlignment', 'Right', 'VerticalAlignment', 'Bottom' ); 286 | elseif 175 <= angle && angle < 185 287 | set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Bottom' ); 288 | elseif 185 <= angle && angle < 265 289 | set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Bottom' ); 290 | elseif 265 <= angle && angle < 275 291 | set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Middle' ); 292 | elseif 275 <= angle && angle < 355 293 | set( textLabels, 'HorizontalAlignment', 'Left', 'VerticalAlignment', 'Top' ); 294 | else % 355-360 295 | set( textLabels, 'HorizontalAlignment', 'Center', 'VerticalAlignment', 'Top' ); 296 | end 297 | end 298 | end 299 | 300 | %-------------------------------------------------------------------------% 301 | function onAxesFontChanged( ~, ~ ) 302 | updateFont(); 303 | repositionAxes( ax ); 304 | repositionXLabel( ax ); 305 | end % onAxesFontChanged 306 | 307 | %-------------------------------------------------------------------------% 308 | function onAxesPositionChanged( ~, ~ ) 309 | % We need to accept the new position, so remove the appdata before 310 | % redrawing 311 | if isappdata( ax, 'OriginalAxesPosition' ) 312 | rmappdata( ax, 'OriginalAxesPosition' ); 313 | end 314 | if isappdata( ax, 'OriginalXLabelPosition' ) 315 | rmappdata( ax, 'OriginalXLabelPosition' ); 316 | end 317 | repositionAxes( ax ); 318 | repositionXLabel( ax ); 319 | end % onAxesPositionChanged 320 | 321 | %-------------------------------------------------------------------------% 322 | function onXAxisLocationChanged( ~, ~ ) 323 | updateAlignment(); 324 | repositionAxes( ax ); 325 | repositionXLabel( ax ); 326 | end % onXAxisLocationChanged 327 | 328 | %-------------------------------------------------------------------------% 329 | function onAxesLimitsChanged( ~, ~ ) 330 | % The limits have moved, so make sure the labels are still ok 331 | textLabels = findall( ax, 'Tag', 'RotatedXTickLabel' ); 332 | xlim = get( ax, 'XLim' ); 333 | ylim = get( ax, 'YLim' ); 334 | if strcmpi( get( ax, 'XAxisLocation'), 'Bottom' ) 335 | pos = [0 ylim(1)]; 336 | else 337 | pos = [0 ylim(2)]; 338 | end 339 | for tt=1:numel( textLabels ) 340 | xval = get( textLabels(tt), 'UserData' ); 341 | pos(1) = xval; 342 | % If the tick is off the edge, make it invisible 343 | if xvalxlim(2) 344 | set( textLabels(tt), 'Visible', 'off', 'Position', pos ) 345 | elseif ~strcmpi( get( textLabels(tt), 'Visible' ), 'on' ) 346 | set( textLabels(tt), 'Visible', 'on', 'Position', pos ) 347 | else 348 | % Just set the position 349 | set( textLabels(tt), 'Position', pos ); 350 | end 351 | end 352 | 353 | repositionXLabel( ax ); 354 | end % onAxesPositionChanged 355 | 356 | %-------------------------------------------------------------------------% 357 | function addListeners( ax ) 358 | % Create listeners. We store the array of listeners in the axes to make 359 | % sure that they have the same life-span as the axes they are listening to. 360 | axh = handle( ax ); 361 | listeners = [ 362 | handle.listener( axh, findprop( axh, 'FontName' ), 'PropertyPostSet', @onAxesFontChanged ) 363 | handle.listener( axh, findprop( axh, 'FontSize' ), 'PropertyPostSet', @onAxesFontChanged ) 364 | handle.listener( axh, findprop( axh, 'FontWeight' ), 'PropertyPostSet', @onAxesFontChanged ) 365 | handle.listener( axh, findprop( axh, 'FontAngle' ), 'PropertyPostSet', @onAxesFontChanged ) 366 | handle.listener( axh, findprop( axh, 'FontUnits' ), 'PropertyPostSet', @onAxesFontChanged ) 367 | handle.listener( axh, findprop( axh, 'OuterPosition' ), 'PropertyPostSet', @onAxesPositionChanged ) 368 | handle.listener( axh, findprop( axh, 'XLim' ), 'PropertyPostSet', @onAxesLimitsChanged ) 369 | handle.listener( axh, findprop( axh, 'YLim' ), 'PropertyPostSet', @onAxesLimitsChanged ) 370 | handle.listener( axh, findprop( axh, 'XAxisLocation' ), 'PropertyPostSet', @onXAxisLocationChanged ) 371 | ]; 372 | setappdata( ax, 'RotateXLabelsListeners', listeners ); 373 | end % addListeners 374 | 375 | %-------------------------------------------------------------------------% 376 | function removeListeners( ax ) 377 | % Rempove any property listeners whilst we are fiddling with the axes 378 | if isappdata( ax, 'RotateXLabelsListeners' ) 379 | delete( getappdata( ax, 'RotateXLabelsListeners' ) ); 380 | rmappdata( ax, 'RotateXLabelsListeners' ); 381 | end 382 | end % removeListeners 383 | 384 | 385 | 386 | end % EOF -------------------------------------------------------------------------------- /paper/ICML_2013_Manifold_Multiview_SupplementaryMaterial_Final.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorisbaz/Multiview-learning/d800c1dc5da45825e7950f11d14ab21918d288c7/paper/ICML_2013_Manifold_Multiview_SupplementaryMaterial_Final.pdf -------------------------------------------------------------------------------- /paper/ICML_2013_Manifold_Multiview_final.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorisbaz/Multiview-learning/d800c1dc5da45825e7950f11d14ab21918d288c7/paper/ICML_2013_Manifold_Multiview_final.pdf --------------------------------------------------------------------------------