├── CLOMV_w.m ├── Consensus One-step Multi-view Subspace Clustering.pdf ├── EProjSimplex_new.m ├── L2_distance_1.m ├── LICENSE ├── README.md ├── RandOrthhMat.m ├── bbcsport_seg14of4.mat ├── cal_obj.m ├── calculate.m ├── constructW_PKN.m ├── eig1.m ├── measure ├── Clustering8Measure.m ├── Contingency.m ├── RandIndex.m ├── compute_f.m └── compute_nmi.m └── run.m /CLOMV_w.m: -------------------------------------------------------------------------------- 1 | function [ress,FV,Fstar, LV,WV,Yres,obj]=CLOMV_w(X,ylabel,B,alpha,gamma,para3,para4,FV,WV,LV) 2 | 3 | MaxIter = 100; 4 | v1=length(X);% # of view 5 | n=length(ylabel); % # of samples 6 | c=length(unique(ylabel));% # of clusters 7 | R = eye(c); 8 | Fstar = RandOrthhMat(n,c); 9 | % Fstar = rand(n,c); 10 | T = eye(c); 11 | 12 | % initial Y of one 1 in each row 13 | Ik = eye(c); 14 | randorder = randperm(size(Ik,1)); 15 | numceil = ceil(n/c); 16 | largeY = repmat(Ik(randorder,:),numceil,1);% (size(A,1)*M, size(A,2)*N) 17 | Yres = largeY(1:n,:);% N*k 18 | [~,res_label] = max(Yres,[],2); 19 | %% update Wv, Fv, Fstar, Y, R 20 | for i=1:MaxIter 21 | resold = res_label; 22 | % disp([num2str(i),'iter']); 23 | for num = 1:v1% begin a view 24 | Fv=FV{num}; 25 | xx=X{num}'*X{num};% n*n dim 26 | 27 | P = calculate(Fv); 28 | %% Zv 29 | % % distance 30 | % for ij=1:n 31 | % h=distance(Fv,n,ij);% 1*n // h_ij = norm( f_ij - fj )^2 32 | % Zv(:,ij)=B{num}*(xx(:,ij) - 1/4*para3*h') ;% hyper para�? beta 33 | % end 34 | 35 | % % closed-form 36 | Zv = 0.5 * B{num}*(2*xx-P/2); 37 | 38 | 39 | %%-projection 40 | Zv = Zv - diag(diag(Zv)); 41 | for ii = 1:size(Zv,2) 42 | idx= 1:size(Zv,2); 43 | idx(ii) = []; 44 | Zv(ii,idx) = EProjSimplex_new(Zv(ii,idx)); 45 | end 46 | 47 | 48 | % nor L 49 | D = diag(sum(Zv)); 50 | L = D-(Zv+Zv')/2; 51 | ZV{num} = Zv; 52 | LV{num} = L; 53 | end 54 | 55 | %% F* 56 | SumFv = zeros(size(FV{1})); 57 | for num = 1:v1 58 | SumFv = SumFv + FV{num}; 59 | end 60 | G = Yres.^gamma; 61 | FGR=2*para4*SumFv+G*R'; 62 | 63 | [Uf,~,Vf] = svd(FGR,'econ'); 64 | Fstar = Uf*Vf'; 65 | 66 | % [a,~] = kmeans(Fstar,c); 67 | % [Fstar_kmresult(i,:)] = Clustering8Measure(ylabel,a); 68 | % disp(['Fstar_acc: ', num2str(Fstar_kmresult(i,7))]); 69 | 70 | % if i == 1 71 | % % Fstar_tsne{i} = tsne(Fstar, ylabel); 72 | % % name = ['iter',num2str(i)]; 73 | % % saveas(gcf,name,'epsc'); 74 | % pause 75 | % elseif i==25 76 | % Fstar_tsne = tsne(Fstar, ylabel); 77 | % pause 78 | % elseif i==50 79 | % Fstar_tsne = tsne(Fstar, ylabel); 80 | % pause 81 | % elseif i==75 82 | % Fstar_tsne = tsne(Fstar, ylabel); 83 | % pause 84 | % elseif i==100 85 | % Fstar_tsne = tsne(Fstar, ylabel); 86 | % pause 87 | % end 88 | 89 | %% Fv Reweighting 90 | for vidx = 1:v1 91 | fv=FV{vidx}; 92 | lv = LV{vidx}; 93 | 94 | [~,L_lmd,~] = eig1(lv,c); 95 | L_lmd_max = max(L_lmd); 96 | Lmx= L_lmd_max*eye(size(lv)) - lv; 97 | 98 | for rep = 1:100 99 | M = 2 * para4 * Lmx * fv+2 * Fstar; 100 | [Um,~,Vm] = svd(M,'econ'); 101 | fv = Um*Vm'; 102 | 103 | fobj(rep+1) = trace( fv' * Lmx * fv ) + 2 * trace( fv' * Fstar ); 104 | if rep>4 && ((fobj(rep)-fobj(rep-1))/fobj(rep)<1e-3) 105 | break; 106 | end 107 | end 108 | FV{vidx}=fv; 109 | end 110 | 111 | % %% Fv solveF 112 | % opts.record = 0; 113 | % opts.mxitr = 1000;%1000 114 | % opts.xtol = 1e-5; 115 | % opts.gtol = 1e-5; 116 | % opts.ftol = 1e-8; 117 | % out.tau = 1e-3; 118 | % 119 | % for vidx = 1:v1 120 | % fv=FV{vidx}; 121 | % lv=LV{vidx}; 122 | % [fv,out]=solveF(fv,@fun1,opts,para4/para3,Fstar,T,lv); 123 | % FV{vidx}=fv; 124 | % end 125 | % obj(i,3)=cal_obj(X,n,c,v1,alpha,gamma,para3,para4,ZV,LV,FV,Fstar,R,Yres); 126 | 127 | %% R 128 | % max tr(R'Fstar'G) 129 | [Ur,~,Vr] = svd(Fstar'*G,'econ'); 130 | R = Ur*Vr'; 131 | 132 | %% updata Y 133 | E=zeros(n,c); 134 | for ei = 1:n 135 | for ec = 1:c 136 | E(ei,ec) = norm( T(ec,:) - Fstar(ei,:) * R , 2)^2; 137 | end 138 | end 139 | 140 | if gamma == 1 141 | for yi = 1:n 142 | [~,yindex]=min(E(yi,:)); 143 | Yres(yi,yindex)=1;% n*c result 144 | end 145 | [~,res_label] = max(Yres,[],2); 146 | else 147 | Yup = E.^(1/(1-gamma)); % n × k 148 | Ydown = sum(Yup,2);% n × 1 //sum of a row 149 | Yres = Yup ./ repmat(Ydown,1,c); % n × k result 150 | 151 | [~, res_label] = max(Yres, [], 2);% 152 | end 153 | 154 | %disp('After Y'); 155 | obj(i,5)=cal_obj(X,n,c,v1,alpha,gamma,ZV,LV,FV,Fstar,R,Yres); 156 | 157 | [ress(i,:)] = Clustering8Measure(ylabel,res_label); 158 | 159 | % % convergence 160 | objres(i) = norm(res_label - resold)/norm(resold); 161 | if i>99 && (norm(res_label - resold)/norm(resold)<1e-3) 162 | break 163 | end 164 | end%interaction end 165 | end 166 | 167 | % %% hi = || fi - fj ||^2 168 | % function [all]=distance(F,n,ij) 169 | % for ji=1:n 170 | % all(ji)=(norm(F(ij,:)-F(ji,:)))^2; 171 | % end 172 | % end 173 | % 174 | % function [F,G]=fun1(P,alpha,Q,T,L) % Q=F* P=Fv T=I 175 | % G=2 * L * P - 2 * alpha * Q * T; 176 | % F=trace(P'*L*P)+(norm(Q-P*T,'fro'))^2; 177 | % end 178 | -------------------------------------------------------------------------------- /Consensus One-step Multi-view Subspace Clustering.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeaninezpp/COMVSC/6a111746454c081204f45cb567792f13fd57f810/Consensus One-step Multi-view Subspace Clustering.pdf -------------------------------------------------------------------------------- /EProjSimplex_new.m: -------------------------------------------------------------------------------- 1 | function [x ft] = EProjSimplex_new(v, k) 2 | 3 | % 4 | %% Problem 5 | % 6 | % min 1/2 || x - v||^2 7 | % s.t. x>=0, 1'x=k 8 | % 9 | 10 | if nargin < 2 11 | k = 1; 12 | end; 13 | 14 | ft=1; 15 | n = length(v); 16 | 17 | v0 = v-mean(v) + k/n; 18 | %vmax = max(v0); 19 | vmin = min(v0); 20 | if vmin < 0 21 | f = 1; 22 | lambda_m = 0; 23 | while abs(f) > 10^-10 24 | v1 = v0 - lambda_m; 25 | posidx = v1>0; 26 | npos = sum(posidx); 27 | g = -npos; 28 | f = sum(v1(posidx)) - k; 29 | lambda_m = lambda_m - f/g; 30 | ft=ft+1; 31 | if ft > 100 32 | x = max(v1,0); 33 | break; 34 | end; 35 | end; 36 | x = max(v1,0); 37 | 38 | else 39 | x = v0; 40 | end; -------------------------------------------------------------------------------- /L2_distance_1.m: -------------------------------------------------------------------------------- 1 | % compute squared Euclidean distance 2 | % ||A-B||^2 = ||A||^2 + ||B||^2 - 2*A'*B 3 | function d = L2_distance_1(a,b) 4 | % a,b: two matrices. each column is a data 5 | % d: distance matrix of a and b 6 | if (size(a,1) == 1) 7 | a = [a; zeros(1,size(a,2))]; 8 | b = [b; zeros(1,size(b,2))]; 9 | end 10 | aa=sum(a.*a); bb=sum(b.*b); ab=a'*b; 11 | d = repmat(aa',[1 size(bb,2)]) + repmat(bb,[size(aa,2) 1]) - 2*ab; 12 | 13 | d = real(d); 14 | d = max(d,0); 15 | 16 | % % force 0 on the diagonal? 17 | % if (df==1) 18 | % d = d.*(1-eye(size(d))); 19 | % end 20 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Jeaninezpp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Implementaion of Consensus One-step Multi-view Subspace Clustering 2 | [stars-img]: https://img.shields.io/github/stars/Jeaninezpp/COMVSC?style=plastic 3 | [stars-url]: https://github.com/Jeaninezpp/COMVSC/stargazers 4 | [fork-img]: https://img.shields.io/github/forks/Jeaninezpp/COMVSC?style=plastic 5 | [fork-url]: https://github.com/Jeaninezpp/COMVSC/network/members 6 | 7 | [![GitHub stars][stars-img]][stars-url] 8 | [![GitHub forks][fork-img]][fork-url] 9 | 10 | This package is an MATLAB implementation of paper 'Consensus One-step Multi-view Subspace Clustering' in IEEE TKDE. 11 | 12 | If you want to run this algorithm, you can check the `run.m` file. 13 | 14 | File `CLOMV_w` is the core part of our algorithm. It contains the details of the optimization. 15 | 16 | You can find the datasets used in our paper in [this repository](https://github.com/Jeaninezpp/multi-view-datasets) . 17 | 18 | --- 19 | 20 | 21 | If you find it helpful, please cite this work. 22 | ``` 23 | @ARTICLE{9298842, 24 | author={P. {Zhang} and X. {Liu} and J. {Xiong} and S. {Zhou} and W. {Zhao} and E. {Zhu} and Z. {Cai}}, 25 | journal={IEEE Transactions on Knowledge and Data Engineering}, 26 | title={Consensus One-step Multi-view Subspace Clustering}, 27 | year={2020}, 28 | volume={}, 29 | number={}, 30 | pages={1-1}, 31 | doi={10.1109/TKDE.2020.3045770}} 32 | ``` 33 | If you have any question, please contact zhangpei@nudt.edu.cn or jeaninezpp@gmail.com 34 | -------------------------------------------------------------------------------- /RandOrthhMat.m: -------------------------------------------------------------------------------- 1 | function M=RandOrthhMat(n, k, tol) 2 | % M = RANDORTHMAT(n) 3 | % generates a random n x k orthogonal real matrix. 4 | % 5 | % M = RANDORTHMAT(n,tol) 6 | % explicitly specifies a thresh value that measures linear dependence 7 | % of a newly formed column with the existing columns. Defaults to 1e-6. 8 | % 9 | % In this version the generated matrix distribution *is* uniform over the manifold 10 | % O(n) w.r.t. the induced R^(n^2) Lebesgue measure, at a slight computational 11 | % overhead (randn + normalization, as opposed to rand ). 12 | % 13 | % (c) Ofek Shilon , 2006. 14 | 15 | 16 | if nargin < 3 17 | tol=1e-6; 18 | end 19 | 20 | M = zeros(n, k); % prealloc 21 | 22 | % gram-schmidt on random column vectors 23 | 24 | vi = randn(n,1); 25 | % the n-dimensional normal distribution has spherical symmetry, which implies 26 | % that after normalization the drawn vectors would be uniformly distributed on the 27 | % n-dimensional unit sphere. 28 | 29 | M(:,1) = vi ./ norm(vi); 30 | 31 | for i=2:k 32 | nrm = 0; 33 | while nrm size(A,1) 10 | c = size(A,1); 11 | end; 12 | 13 | if nargin < 3 14 | isMax = 1; 15 | isSym = 1; 16 | end; 17 | 18 | if nargin < 4 19 | isSym = 1; 20 | end; 21 | 22 | if isSym == 1 23 | A = max(A,A'); 24 | end; 25 | [v d] = eig(A); 26 | d = diag(d); 27 | %d = real(d); 28 | if isMax == 0 %升序,取前c小 29 | [d1, idx] = sort(d); 30 | else %降序,取前c大 31 | [d1, idx] = sort(d,'descend'); 32 | end; 33 | 34 | idx1 = idx(1:c);%取前c个 35 | eigval = d(idx1);%取前c个特征值 36 | eigvec = v(:,idx1);%取前c个特征向量 37 | 38 | eigval_full = d(idx); -------------------------------------------------------------------------------- /measure/Clustering8Measure.m: -------------------------------------------------------------------------------- 1 | function result = Clustering8Measure(Y, predY) 2 | 3 | if size(Y,2) ~= 1 4 | Y = Y'; 5 | end; 6 | if size(predY,2) ~= 1 7 | predY = predY'; 8 | end; 9 | 10 | n = length(Y); 11 | 12 | uY = unique(Y); 13 | nclass = length(uY); 14 | Y0 = zeros(n,1); 15 | if nclass ~= max(Y) 16 | for i = 1:nclass 17 | Y0(find(Y == uY(i))) = i; 18 | end; 19 | Y = Y0; 20 | end; 21 | 22 | uY = unique(predY); 23 | nclass = length(uY); 24 | predY0 = zeros(n,1); 25 | if nclass ~= max(predY) 26 | for i = 1:nclass 27 | predY0(find(predY == uY(i))) = i; 28 | end; 29 | predY = predY0; 30 | end; 31 | 32 | 33 | Lidx = unique(Y); classnum = length(Lidx); 34 | predLidx = unique(predY); pred_classnum = length(predLidx); 35 | 36 | % purity 37 | correnum = 0; 38 | for ci = 1:pred_classnum 39 | incluster = Y(find(predY == predLidx(ci))); 40 | % cnub = unique(incluster); 41 | % inclunub = 0; 42 | % for cnubi = 1:length(cnub) 43 | % inclunub(cnubi) = length(find(incluster == cnub(cnubi))); 44 | % end; 45 | inclunub = hist(incluster, 1:max(incluster)); if isempty(inclunub) inclunub=0;end; 46 | correnum = correnum + max(inclunub); 47 | end; 48 | Purity = correnum/length(predY); 49 | 50 | %if pred_classnum 51 | res = bestMap(Y, predY); 52 | % accuarcy 53 | ACC = length(find(Y == res))/length(Y); 54 | % NMI 55 | MIhat = MutualInfo(Y,res); 56 | 57 | [Fscore Precision Recall] = compute_f(Y, predY); 58 | [nmi Entropy] = compute_nmi(Y, predY); 59 | AR=RandIndex(Y, predY); 60 | 61 | result = [Fscore Precision Recall nmi AR Entropy ACC Purity]; 62 | %result = [Fscore Precision Recall nmi AR Entropy]; 63 | 64 | 65 | 66 | %% 67 | function [newL2, c] = bestMap(L1,L2) 68 | %bestmap: permute labels of L2 match L1 as good as possible 69 | % [newL2] = bestMap(L1,L2); 70 | 71 | %=========== 72 | L1 = L1(:); 73 | L2 = L2(:); 74 | if size(L1) ~= size(L2) 75 | error('size(L1) must == size(L2)'); 76 | end 77 | L1 = L1 - min(L1) + 1; % min (L1) <- 1; 78 | L2 = L2 - min(L2) + 1; % min (L2) <- 1; 79 | %=========== make bipartition graph ============ 80 | nClass = max(max(L1), max(L2)); 81 | G = zeros(nClass); 82 | for i=1:nClass 83 | for j=1:nClass 84 | G(i,j) = length(find(L1 == i & L2 == j)); 85 | end 86 | end 87 | %=========== assign with hungarian method ====== 88 | [c,t] = hungarian(-G); 89 | newL2 = zeros(nClass,1); 90 | for i=1:nClass 91 | newL2(L2 == i) = c(i); 92 | end 93 | 94 | 95 | 96 | 97 | 98 | %% 99 | function MIhat = MutualInfo(L1,L2) 100 | % mutual information 101 | 102 | %=========== 103 | L1 = L1(:); 104 | L2 = L2(:); 105 | if size(L1) ~= size(L2) 106 | error('size(L1) must == size(L2)'); 107 | end 108 | L1 = L1 - min(L1) + 1; % min (L1) <- 1; 109 | L2 = L2 - min(L2) + 1; % min (L2) <- 1; 110 | %=========== make bipartition graph ============ 111 | nClass = max(max(L1), max(L2)); 112 | G = zeros(nClass); 113 | for i=1:nClass 114 | for j=1:nClass 115 | G(i,j) = length(find(L1 == i & L2 == j))+eps; 116 | end 117 | end 118 | sumG = sum(G(:)); 119 | %=========== calculate MIhat 120 | P1 = sum(G,2); P1 = P1/sumG; 121 | P2 = sum(G,1); P2 = P2/sumG; 122 | H1 = sum(-P1.*log2(P1)); 123 | H2 = sum(-P2.*log2(P2)); 124 | P12 = G/sumG; 125 | PPP = P12./repmat(P2,nClass,1)./repmat(P1,1,nClass); 126 | PPP(abs(PPP) < 1e-12) = 1; 127 | MI = sum(P12(:) .* log2(PPP(:))); 128 | MIhat = MI / max(H1,H2); 129 | %%%%%%%%%%%%% why complex ? %%%%%%%% 130 | MIhat = real(MIhat); 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | %% 140 | function [C,T]=hungarian(A) 141 | %HUNGARIAN Solve the Assignment problem using the Hungarian method. 142 | % 143 | %[C,T]=hungarian(A) 144 | %A - a square cost matrix. 145 | %C - the optimal assignment. 146 | %T - the cost of the optimal assignment. 147 | %s.t. T = trace(A(C,:)) is minimized over all possible assignments. 148 | 149 | % Adapted from the FORTRAN IV code in Carpaneto and Toth, "Algorithm 548: 150 | % Solution of the assignment problem [H]", ACM Transactions on 151 | % Mathematical Software, 6(1):104-111, 1980. 152 | 153 | % v1.0 96-06-14. Niclas Borlin, niclas@cs.umu.se. 154 | % Department of Computing Science, Ume?University, 155 | % Sweden. 156 | % All standard disclaimers apply. 157 | 158 | % A substantial effort was put into this code. If you use it for a 159 | % publication or otherwise, please include an acknowledgement or at least 160 | % notify me by email. /Niclas 161 | 162 | [m,n]=size(A); 163 | 164 | if (m~=n) 165 | error('HUNGARIAN: Cost matrix must be square!'); 166 | end 167 | 168 | % Save original cost matrix. 169 | orig=A; 170 | 171 | % Reduce matrix. 172 | A=hminired(A); 173 | 174 | % Do an initial assignment. 175 | [A,C,U]=hminiass(A); 176 | 177 | % Repeat while we have unassigned rows. 178 | while (U(n+1)) 179 | % Start with no path, no unchecked zeros, and no unexplored rows. 180 | LR=zeros(1,n); 181 | LC=zeros(1,n); 182 | CH=zeros(1,n); 183 | RH=[zeros(1,n) -1]; 184 | 185 | % No labelled columns. 186 | SLC=[]; 187 | 188 | % Start path in first unassigned row. 189 | r=U(n+1); 190 | % Mark row with end-of-path label. 191 | LR(r)=-1; 192 | % Insert row first in labelled row set. 193 | SLR=r; 194 | 195 | % Repeat until we manage to find an assignable zero. 196 | while (1) 197 | % If there are free zeros in row r 198 | if (A(r,n+1)~=0) 199 | % ...get column of first free zero. 200 | l=-A(r,n+1); 201 | 202 | % If there are more free zeros in row r and row r in not 203 | % yet marked as unexplored.. 204 | if (A(r,l)~=0 & RH(r)==0) 205 | % Insert row r first in unexplored list. 206 | RH(r)=RH(n+1); 207 | RH(n+1)=r; 208 | 209 | % Mark in which column the next unexplored zero in this row 210 | % is. 211 | CH(r)=-A(r,l); 212 | end 213 | else 214 | % If all rows are explored.. 215 | if (RH(n+1)<=0) 216 | % Reduce matrix. 217 | [A,CH,RH]=hmreduce(A,CH,RH,LC,LR,SLC,SLR); 218 | end 219 | 220 | % Re-start with first unexplored row. 221 | r=RH(n+1); 222 | % Get column of next free zero in row r. 223 | l=CH(r); 224 | % Advance "column of next free zero". 225 | CH(r)=-A(r,l); 226 | % If this zero is last in the list.. 227 | if (A(r,l)==0) 228 | % ...remove row r from unexplored list. 229 | RH(n+1)=RH(r); 230 | RH(r)=0; 231 | end 232 | end 233 | 234 | % While the column l is labelled, i.e. in path. 235 | while (LC(l)~=0) 236 | % If row r is explored.. 237 | if (RH(r)==0) 238 | % If all rows are explored.. 239 | if (RH(n+1)<=0) 240 | % Reduce cost matrix. 241 | [A,CH,RH]=hmreduce(A,CH,RH,LC,LR,SLC,SLR); 242 | end 243 | 244 | % Re-start with first unexplored row. 245 | r=RH(n+1); 246 | end 247 | 248 | % Get column of next free zero in row r. 249 | l=CH(r); 250 | 251 | % Advance "column of next free zero". 252 | CH(r)=-A(r,l); 253 | 254 | % If this zero is last in list.. 255 | if(A(r,l)==0) 256 | % ...remove row r from unexplored list. 257 | RH(n+1)=RH(r); 258 | RH(r)=0; 259 | end 260 | end 261 | 262 | % If the column found is unassigned.. 263 | if (C(l)==0) 264 | % Flip all zeros along the path in LR,LC. 265 | [A,C,U]=hmflip(A,C,LC,LR,U,l,r); 266 | % ...and exit to continue with next unassigned row. 267 | break; 268 | else 269 | % ...else add zero to path. 270 | 271 | % Label column l with row r. 272 | LC(l)=r; 273 | 274 | % Add l to the set of labelled columns. 275 | SLC=[SLC l]; 276 | 277 | % Continue with the row assigned to column l. 278 | r=C(l); 279 | 280 | % Label row r with column l. 281 | LR(r)=l; 282 | 283 | % Add r to the set of labelled rows. 284 | SLR=[SLR r]; 285 | end 286 | end 287 | end 288 | 289 | % Calculate the total cost. 290 | T=sum(orig(logical(sparse(C,1:size(orig,2),1)))); 291 | 292 | 293 | function A=hminired(A) 294 | %HMINIRED Initial reduction of cost matrix for the Hungarian method. 295 | % 296 | %B=assredin(A) 297 | %A - the unreduced cost matris. 298 | %B - the reduced cost matrix with linked zeros in each row. 299 | 300 | % v1.0 96-06-13. Niclas Borlin, niclas@cs.umu.se. 301 | 302 | [m,n]=size(A); 303 | 304 | % Subtract column-minimum values from each column. 305 | colMin=min(A); 306 | A=A-colMin(ones(n,1),:); 307 | 308 | % Subtract row-minimum values from each row. 309 | rowMin=min(A')'; 310 | A=A-rowMin(:,ones(1,n)); 311 | 312 | % Get positions of all zeros. 313 | [i,j]=find(A==0); 314 | 315 | % Extend A to give room for row zero list header column. 316 | A(1,n+1)=0; 317 | for k=1:n 318 | % Get all column in this row. 319 | cols=j(k==i)'; 320 | % Insert pointers in matrix. 321 | A(k,[n+1 cols])=[-cols 0]; 322 | end 323 | 324 | 325 | function [A,C,U]=hminiass(A) 326 | %HMINIASS Initial assignment of the Hungarian method. 327 | % 328 | %[B,C,U]=hminiass(A) 329 | %A - the reduced cost matrix. 330 | %B - the reduced cost matrix, with assigned zeros removed from lists. 331 | %C - a vector. C(J)=I means row I is assigned to column J, 332 | % i.e. there is an assigned zero in position I,J. 333 | %U - a vector with a linked list of unassigned rows. 334 | 335 | % v1.0 96-06-14. Niclas Borlin, niclas@cs.umu.se. 336 | 337 | [n,np1]=size(A); 338 | 339 | % Initalize return vectors. 340 | C=zeros(1,n); 341 | U=zeros(1,n+1); 342 | 343 | % Initialize last/next zero "pointers". 344 | LZ=zeros(1,n); 345 | NZ=zeros(1,n); 346 | 347 | for i=1:n 348 | % Set j to first unassigned zero in row i. 349 | lj=n+1; 350 | j=-A(i,lj); 351 | 352 | % Repeat until we have no more zeros (j==0) or we find a zero 353 | % in an unassigned column (c(j)==0). 354 | 355 | while (C(j)~=0) 356 | % Advance lj and j in zero list. 357 | lj=j; 358 | j=-A(i,lj); 359 | 360 | % Stop if we hit end of list. 361 | if (j==0) 362 | break; 363 | end 364 | end 365 | 366 | if (j~=0) 367 | % We found a zero in an unassigned column. 368 | 369 | % Assign row i to column j. 370 | C(j)=i; 371 | 372 | % Remove A(i,j) from unassigned zero list. 373 | A(i,lj)=A(i,j); 374 | 375 | % Update next/last unassigned zero pointers. 376 | NZ(i)=-A(i,j); 377 | LZ(i)=lj; 378 | 379 | % Indicate A(i,j) is an assigned zero. 380 | A(i,j)=0; 381 | else 382 | % We found no zero in an unassigned column. 383 | 384 | % Check all zeros in this row. 385 | 386 | lj=n+1; 387 | j=-A(i,lj); 388 | 389 | % Check all zeros in this row for a suitable zero in another row. 390 | while (j~=0) 391 | % Check the in the row assigned to this column. 392 | r=C(j); 393 | 394 | % Pick up last/next pointers. 395 | lm=LZ(r); 396 | m=NZ(r); 397 | 398 | % Check all unchecked zeros in free list of this row. 399 | while (m~=0) 400 | % Stop if we find an unassigned column. 401 | if (C(m)==0) 402 | break; 403 | end 404 | 405 | % Advance one step in list. 406 | lm=m; 407 | m=-A(r,lm); 408 | end 409 | 410 | if (m==0) 411 | % We failed on row r. Continue with next zero on row i. 412 | lj=j; 413 | j=-A(i,lj); 414 | else 415 | % We found a zero in an unassigned column. 416 | 417 | % Replace zero at (r,m) in unassigned list with zero at (r,j) 418 | A(r,lm)=-j; 419 | A(r,j)=A(r,m); 420 | 421 | % Update last/next pointers in row r. 422 | NZ(r)=-A(r,m); 423 | LZ(r)=j; 424 | 425 | % Mark A(r,m) as an assigned zero in the matrix . . . 426 | A(r,m)=0; 427 | 428 | % ...and in the assignment vector. 429 | C(m)=r; 430 | 431 | % Remove A(i,j) from unassigned list. 432 | A(i,lj)=A(i,j); 433 | 434 | % Update last/next pointers in row r. 435 | NZ(i)=-A(i,j); 436 | LZ(i)=lj; 437 | 438 | % Mark A(r,m) as an assigned zero in the matrix . . . 439 | A(i,j)=0; 440 | 441 | % ...and in the assignment vector. 442 | C(j)=i; 443 | 444 | % Stop search. 445 | break; 446 | end 447 | end 448 | end 449 | end 450 | 451 | % Create vector with list of unassigned rows. 452 | 453 | % Mark all rows have assignment. 454 | r=zeros(1,n); 455 | rows=C(C~=0); 456 | r(rows)=rows; 457 | empty=find(r==0); 458 | 459 | % Create vector with linked list of unassigned rows. 460 | U=zeros(1,n+1); 461 | U([n+1 empty])=[empty 0]; 462 | 463 | 464 | function [A,C,U]=hmflip(A,C,LC,LR,U,l,r) 465 | %HMFLIP Flip assignment state of all zeros along a path. 466 | % 467 | %[A,C,U]=hmflip(A,C,LC,LR,U,l,r) 468 | %Input: 469 | %A - the cost matrix. 470 | %C - the assignment vector. 471 | %LC - the column label vector. 472 | %LR - the row label vector. 473 | %U - the 474 | %r,l - position of last zero in path. 475 | %Output: 476 | %A - updated cost matrix. 477 | %C - updated assignment vector. 478 | %U - updated unassigned row list vector. 479 | 480 | % v1.0 96-06-14. Niclas Borlin, niclas@cs.umu.se. 481 | 482 | n=size(A,1); 483 | 484 | while (1) 485 | % Move assignment in column l to row r. 486 | C(l)=r; 487 | 488 | % Find zero to be removed from zero list.. 489 | 490 | % Find zero before this. 491 | m=find(A(r,:)==-l); 492 | 493 | % Link past this zero. 494 | A(r,m)=A(r,l); 495 | 496 | A(r,l)=0; 497 | 498 | % If this was the first zero of the path.. 499 | if (LR(r)<0) 500 | ...remove row from unassigned row list and return. 501 | U(n+1)=U(r); 502 | U(r)=0; 503 | return; 504 | else 505 | 506 | % Move back in this row along the path and get column of next zero. 507 | l=LR(r); 508 | 509 | % Insert zero at (r,l) first in zero list. 510 | A(r,l)=A(r,n+1); 511 | A(r,n+1)=-l; 512 | 513 | % Continue back along the column to get row of next zero in path. 514 | r=LC(l); 515 | end 516 | end 517 | 518 | 519 | function [A,CH,RH]=hmreduce(A,CH,RH,LC,LR,SLC,SLR) 520 | %HMREDUCE Reduce parts of cost matrix in the Hungerian method. 521 | % 522 | %[A,CH,RH]=hmreduce(A,CH,RH,LC,LR,SLC,SLR) 523 | %Input: 524 | %A - Cost matrix. 525 | %CH - vector of column of 'next zeros' in each row. 526 | %RH - vector with list of unexplored rows. 527 | %LC - column labels. 528 | %RC - row labels. 529 | %SLC - set of column labels. 530 | %SLR - set of row labels. 531 | % 532 | %Output: 533 | %A - Reduced cost matrix. 534 | %CH - Updated vector of 'next zeros' in each row. 535 | %RH - Updated vector of unexplored rows. 536 | 537 | % v1.0 96-06-14. Niclas Borlin, niclas@cs.umu.se. 538 | 539 | n=size(A,1); 540 | 541 | % Find which rows are covered, i.e. unlabelled. 542 | coveredRows=LR==0; 543 | 544 | % Find which columns are covered, i.e. labelled. 545 | coveredCols=LC~=0; 546 | 547 | r=find(~coveredRows); 548 | c=find(~coveredCols); 549 | 550 | % Get minimum of uncovered elements. 551 | m=min(min(A(r,c))); 552 | 553 | % Subtract minimum from all uncovered elements. 554 | A(r,c)=A(r,c)-m; 555 | 556 | % Check all uncovered columns.. 557 | for j=c 558 | % ...and uncovered rows in path order.. 559 | for i=SLR 560 | % If this is a (new) zero.. 561 | if (A(i,j)==0) 562 | % If the row is not in unexplored list.. 563 | if (RH(i)==0) 564 | % ...insert it first in unexplored list. 565 | RH(i)=RH(n+1); 566 | RH(n+1)=i; 567 | % Mark this zero as "next free" in this row. 568 | CH(i)=j; 569 | end 570 | % Find last unassigned zero on row I. 571 | row=A(i,:); 572 | colsInList=-row(row<0); 573 | if (length(colsInList)==0) 574 | % No zeros in the list. 575 | l=n+1; 576 | else 577 | l=colsInList(row(colsInList)==0); 578 | end 579 | % Append this zero to end of list. 580 | A(i,l)=-j; 581 | end 582 | end 583 | end 584 | 585 | % Add minimum to all doubly covered elements. 586 | r=find(coveredRows); 587 | c=find(coveredCols); 588 | 589 | % Take care of the zeros we will remove. 590 | [i,j]=find(A(r,c)<=0); 591 | 592 | i=r(i); 593 | j=c(j); 594 | 595 | for k=1:length(i) 596 | % Find zero before this in this row. 597 | lj=find(A(i(k),:)==-j(k)); 598 | % Link past it. 599 | A(i(k),lj)=A(i(k),j(k)); 600 | % Mark it as assigned. 601 | A(i(k),j(k))=0; 602 | end 603 | 604 | A(r,c)=A(r,c)+m; 605 | -------------------------------------------------------------------------------- /measure/Contingency.m: -------------------------------------------------------------------------------- 1 | function Cont=Contingency(Mem1,Mem2) 2 | 3 | if nargin < 2 | min(size(Mem1)) > 1 | min(size(Mem2)) > 1 4 | error('Contingency: Requires two vector arguments') 5 | return 6 | end 7 | 8 | Cont=zeros(max(Mem1),max(Mem2)); 9 | 10 | for i = 1:length(Mem1); 11 | Cont(Mem1(i),Mem2(i))=Cont(Mem1(i),Mem2(i))+1; 12 | end 13 | -------------------------------------------------------------------------------- /measure/RandIndex.m: -------------------------------------------------------------------------------- 1 | function [AR,RI,MI,HI]=RandIndex(c1,c2) 2 | %RANDINDEX - calculates Rand Indices to compare two partitions 3 | % ARI=RANDINDEX(c1,c2), where c1,c2 are vectors listing the 4 | % class membership, returns the "Hubert & Arabie adjusted Rand index". 5 | % [AR,RI,MI,HI]=RANDINDEX(c1,c2) returns the adjusted Rand index, 6 | % the unadjusted Rand index, "Mirkin's" index and "Hubert's" index. 7 | % 8 | % See L. Hubert and P. Arabie (1985) "Comparing Partitions" Journal of 9 | % Classification 2:193-218 10 | 11 | %(C) David Corney (2000) D.Corney@cs.ucl.ac.uk 12 | 13 | if nargin < 2 | min(size(c1)) > 1 | min(size(c2)) > 1 14 | error('RandIndex: Requires two vector arguments') 15 | return 16 | end 17 | 18 | C=Contingency(c1,c2); %form contingency matrix 19 | 20 | n=sum(sum(C)); 21 | nis=sum(sum(C,2).^2); %sum of squares of sums of rows 22 | njs=sum(sum(C,1).^2); %sum of squares of sums of columns 23 | 24 | t1=nchoosek(n,2); %total number of pairs of entities 25 | t2=sum(sum(C.^2)); %sum over rows & columnns of nij^2 26 | t3=.5*(nis+njs); 27 | 28 | %Expected index (for adjustment) 29 | nc=(n*(n^2+1)-(n+1)*nis-(n+1)*njs+2*(nis*njs)/n)/(2*(n-1)); 30 | 31 | A=t1+t2-t3; %no. agreements 32 | D= -t2+t3; %no. disagreements 33 | 34 | if t1==nc 35 | AR=0; %avoid division by zero; if k=1, define Rand = 0 36 | else 37 | AR=(A-nc)/(t1-nc); %adjusted Rand - Hubert & Arabie 1985 38 | end 39 | 40 | RI=A/t1; %Rand 1971 %Probability of agreement 41 | MI=D/t1; %Mirkin 1970 %p(disagreement) 42 | HI=(A-D)/t1; %Hubert 1977 %p(agree)-p(disagree) 43 | 44 | -------------------------------------------------------------------------------- /measure/compute_f.m: -------------------------------------------------------------------------------- 1 | function [f,p,r] = compute_f(T,H) 2 | 3 | if length(T) ~= length(H), 4 | size(T) 5 | size(H) 6 | end; 7 | 8 | N = length(T); 9 | numT = 0; 10 | numH = 0; 11 | numI = 0; 12 | for n=1:N, 13 | Tn = (T(n+1:end))==T(n); 14 | Hn = (H(n+1:end))==H(n); 15 | numT = numT + sum(Tn); 16 | numH = numH + sum(Hn); 17 | numI = numI + sum(Tn .* Hn); 18 | end; 19 | p = 1; 20 | r = 1; 21 | f = 1; 22 | if numH > 0, 23 | p = numI / numH; 24 | end; 25 | if numT > 0, 26 | r = numI / numT; 27 | end; 28 | if (p+r) == 0, 29 | f = 0; 30 | else 31 | f = 2 * p * r / (p + r); 32 | end; 33 | -------------------------------------------------------------------------------- /measure/compute_nmi.m: -------------------------------------------------------------------------------- 1 | function [nmi clust_ent] = compute_nmi (T, H) 2 | 3 | N = length(T); 4 | classes = unique(T); 5 | clusters = unique(H); 6 | num_class = length(classes); 7 | num_clust = length(clusters); 8 | 9 | %%compute number of points in each class 10 | for j=1:num_class 11 | index_class = (T(:)==classes(j)); 12 | D(j) = sum(index_class); 13 | end 14 | 15 | %%mutual information 16 | mi = 0; 17 | A = zeros(num_clust, num_class); 18 | avgent = 0; 19 | for i=1:num_clust 20 | %number of points in cluster 'i' 21 | index_clust = (H(:)==clusters(i)); 22 | B(i) = sum(index_clust); 23 | for j=1:num_class 24 | index_class = (T(:)==classes(j)); 25 | %%compute number of points in class 'j' that end up in cluster 'i' 26 | A(i,j) = sum(index_class.*index_clust); 27 | if (A(i,j) ~= 0) 28 | miarr(i,j) = A(i,j)/N * log2 (N*A(i,j)/(B(i)*D(j))); 29 | %%average entropy calculation 30 | avgent = avgent - (B(i)/N) * (A(i,j)/B(i)) * log2 (A(i,j)/B(i)); 31 | else 32 | miarr(i,j) = 0; 33 | end 34 | mi = mi + miarr(i,j); 35 | 36 | 37 | 38 | end 39 | end 40 | 41 | %%class entropy 42 | class_ent = 0; 43 | for i=1:num_class 44 | class_ent = class_ent + D(i)/N * log2(N/D(i)); 45 | end 46 | 47 | %%clustering entropy 48 | clust_ent = 0; 49 | for i=1:num_clust 50 | clust_ent = clust_ent + B(i)/N * log2(N/B(i)); 51 | end 52 | 53 | %%normalized mutual information 54 | nmi = 2*mi / (clust_ent + class_ent); -------------------------------------------------------------------------------- /run.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear; 3 | addpath ./measure ./tsne 4 | path = './'; 5 | datapath = '../0datasets/'; 6 | 7 | 8 | datasetname = {'bbcsport_seg14of4','yaleA_3view'}; 9 | for di = 1 % Datasets 10 | dataName = datasetname{di}; 11 | load([datapath,'/',dataName,'.mat'],'X','Y'); % Ubuntu 12 | disp(['\n\n Current dataset : ',dataName]); 13 | 14 | viewN = length(X); 15 | k = length(unique(Y)); 16 | N = length(Y); 17 | 18 | for iv = 1:viewN 19 | X{iv} = mapstd(X{iv}',0,1); 20 | data{iv} = X{iv}'; 21 | end 22 | % X{i} is d * N 23 | % data{i} is N *d 24 | 25 | FV = cell(size(X)); 26 | WV= cell(size(X)); 27 | LV = cell(size(X)); 28 | for i =1:viewN 29 | W = constructW_PKN(X{i}, 5, 1); % X : d*n / 5 neighbors / is symmetric 30 | D = diag(sum(W)); 31 | L = D-W; 32 | [Fv, ~, ~]=eig1(L,k,0); 33 | FV{i} = Fv; 34 | LV{i} = L; 35 | WV{i} = W; 36 | end 37 | 38 | 39 | lmd = 2.^[3:2:13]; 40 | gma = [1.3:0.2:2.7]; 41 | para3=1; 42 | para4=1; % Fixed 43 | 44 | 45 | B=cell(size(X)); % B=inv(X'X+alpha*I)^(-1) 46 | idx = 1; 47 | for i=1:length(lmd) 48 | for vnum=1:viewN 49 | % B{vnum}= inv(X{vnum}'*X{vnum}+lmd(i)*eye(size(X{1},2))); 50 | I_d{vnum} = eye(size(X{vnum},1)); % X : d*N 51 | I_n{vnum} = eye(size(X{vnum},2)); 52 | B{vnum} = (1/lmd(i))*(I_n{vnum}-(1/lmd(i))*X{vnum}'*inv(I_d{vnum}+(1/lmd(i))*X{vnum}*X{vnum}')*X{vnum}); 53 | end 54 | for j=1:length(gma) 55 | tic; 56 | [ress,Fv,Fstar,Lv,Wv,Yres,obj]=CLOMV_w(X,Y,B,lmd(i),gma(j),para3,para4,FV,WV,LV); 57 | t(idx)=toc; 58 | res_comvsc = ress(end,:); 59 | Result_11COMVSC(idx,:) = [lmd(i) gma(j) res_comvsc]; 60 | disp(['Para1 = ',num2str(lmd(i)),' Para2 = ',num2str(gma(j)), ' ACC = ', num2str(res_comvsc(:,7)),' Time = ',num2str(t(idx))]); 61 | ourFscore(i,j) = res_comvsc(1); ourPrecision(i,j) = res_comvsc(2); ourRecall(i,j) = res_comvsc(3); 62 | ourNmi(i,j) = res_comvsc(4);ourAR(i,j) = res_comvsc(5); ourEntropy(i,j) = res_comvsc(6); 63 | ourACC(i,j) = res_comvsc(7); ourPurity(i,j) = res_comvsc(8); 64 | % dlmwrite(['./', dataName, '.txt'], Result_11COMVSC(idx,:),'-append','delimiter','\t','newline','pc'); 65 | idx = idx+1; 66 | end 67 | end 68 | maxresult = max(Result_11COMVSC,[],1); 69 | Allresult(1,:) = maxresult(3:10); 70 | fprintf('- Finish Proposed Method \n '); 71 | 72 | save(['./' , dataName ,'_Our.mat'], 't','Allresult'); 73 | 74 | end 75 | --------------------------------------------------------------------------------