├── KernelPCA ├── Kernel.m ├── KernelPCA.m ├── KernelPCAOption.m └── KernelPCAVisualization.m ├── README.md ├── data ├── TE.mat ├── banana.mat ├── circle.mat ├── diagnosis.mat └── helix.mat ├── demo_DR.m ├── demo_DR_Contirbution.m ├── demo_DR_Reconstruction.m ├── demo_FD.m ├── demo_FD_Diagnosis.m └── demo_kernel_function.m /KernelPCA/Kernel.m: -------------------------------------------------------------------------------- 1 | classdef Kernel < handle 2 | %{ 3 | Kernel function 4 | 5 | INPUT 6 | X data (n*d) 7 | Y data (m*d) 8 | 9 | OUTPUT 10 | K kernel matrix (n*m) 11 | 12 | ---------------------------------------------------------------------- 13 | 14 | type - 15 | 16 | linear : k(x,y) = x'*y 17 | polynomial : k(x,y) = (γ*x'*y+c)^d 18 | gaussian : k(x,y) = exp(-γ*||x-y||^2) 19 | sigmoid : k(x,y) = tanh(γ*x'*y+c) 20 | laplacian : k(x,y) = exp(-γ*||x-y||) 21 | 22 | 23 | degree - d 24 | offset - c 25 | gamma - γ 26 | 27 | ---------------------------------------------------------------------- 28 | 29 | Version 1.1, 11-MAY-2021 30 | Email: iqiukp@outlook.com 31 | ---------------------------------------------------------------------- 32 | %} 33 | 34 | properties 35 | type = 'gaussian' 36 | offset = 0 37 | gamma = 0.1 38 | degree = 2 39 | end 40 | 41 | methods 42 | function obj = Kernel(varargin) 43 | inputValue = varargin; 44 | nParameter = size(inputValue, 2)/2; 45 | supportedKernelFunc = {'linear', 'gaussian', 'polynomial', 'sigmoid', 'laplacian'}; 46 | for n = 1:nParameter 47 | parameter = inputValue{(n-1)*2+1}; 48 | value = inputValue{(n-1)*2+2}; 49 | if strcmp(parameter, 'type') 50 | if ~any(strcmp(value, supportedKernelFunc)) 51 | errorText = sprintf([ 52 | 'Unsupported kernel function.\n',... 53 | 'Use one of these kernel functions:\n', ... 54 | 'linear, gaussian, polynomial, sigmoid, laplacian.']); 55 | error(errorText) 56 | end 57 | end 58 | obj.(parameter) = value; 59 | end 60 | end 61 | 62 | function K = computeMatrix(obj, x, y) 63 | K = zeros(size(x, 1), size(y, 1)); 64 | % compute the kernel matrix 65 | switch obj.type 66 | case 'linear' % linear kernel function 67 | K = x*y'; 68 | 69 | case 'gaussian' % gaussian kernel function 70 | try 71 | K = exp(-obj.gamma*pdist2(x, y, 'squaredeuclidean')); 72 | catch 73 | sx = sum(x.^2, 2); 74 | sy = sum(y.^2, 2); 75 | xy = 2*x*y'; 76 | K = exp((bsxfun(@minus, bsxfun(@minus, xy, sx), sy'))*obj.gamma); 77 | end 78 | 79 | case 'polynomial' % polynomial kernel function 80 | K = (obj.gamma*x*y'+obj.offset).^double(obj.degree); 81 | 82 | case 'sigmoid' % sigmoid kernel function 83 | K = tanh(obj.gamma*x*y'+obj.offset); 84 | 85 | case 'laplacian' % laplacian kernel function 86 | K = exp(-obj.gamma*pdist2(x, y, 'cityblock')); 87 | end 88 | end 89 | end 90 | end -------------------------------------------------------------------------------- /KernelPCA/KernelPCA.m: -------------------------------------------------------------------------------- 1 | classdef KernelPCA < handle 2 | %{ 3 | Kernel Principal component analysis (KPCA) 4 | 5 | Version 2.2, 14-MAY-2021 6 | Email: iqiukp@outlook.com 7 | ------------------------------------------------------------------- 8 | %} 9 | 10 | properties 11 | data 12 | label 13 | numComponents 14 | explainedLevel 15 | kernelFunc = Kernel('type', 'gaussian', 'gamma', 0.5) 16 | lambda 17 | coefficient % principal component coefficients 18 | score % principal component scores. 19 | cumContribution % cumulative contribution rate 20 | newData % Transform the mapping data back to original space 21 | T2 22 | SPE 23 | T2Limit 24 | SPELimit 25 | numSPEAlarm 26 | numT2Alarm 27 | accuracySPE 28 | accuracyT2 29 | eigenvalueTolerance = 1e-8 % tolerance for eigenvalues 30 | alpha = 1 % hyperparameter of the ridge regression that learns the reconstruction 31 | theta = 0.7 % experience parameter of fault diagnosis 32 | significanceLevel = 0.95 33 | display = 'on' 34 | temporary 35 | diagnosis = [] 36 | runningTime 37 | end 38 | 39 | properties (Dependent) 40 | numSamples 41 | numFeatures 42 | end 43 | 44 | methods 45 | function obj = KernelPCA(parameter) 46 | name_ = fieldnames(parameter); 47 | for i = 1:size(name_, 1) 48 | obj.(name_{i, 1}) = parameter.(name_{i, 1}); 49 | end 50 | KernelPCAOption.checkInputForDiagnosis(obj); 51 | end 52 | 53 | function obj = train(obj, data) 54 | tStart = tic; 55 | obj.data = data; 56 | obj.label = ones(obj.numSamples, 1); 57 | 58 | % compute the kernel matrix 59 | K = obj.kernelFunc.computeMatrix(obj.data, obj.data); 60 | % centralize the kernel matrix 61 | unit = ones(obj.numSamples, obj.numSamples)/obj.numSamples; 62 | K_c = K-unit*K-K*unit+unit*K*unit; 63 | % compute the eigenvalues and eigenvectors 64 | [obj.coefficient, obj.lambda] = obj.computeEigenvalue(K_c); 65 | % 66 | obj.score = K_c* obj.coefficient(:, 1:obj.numComponents); 67 | obj.newData = obj.reconstruct; 68 | obj.temporary.K = K; 69 | obj.temporary.K_c = K_c; 70 | obj.temporary.unit = unit; 71 | obj.computeControlLimit; 72 | 73 | % compute accuracy 74 | T2AlarmIndex = find(obj.T2 > obj.T2Limit); 75 | SPEAlarmIndex = find(obj.SPE > obj.SPELimit); 76 | obj.numSPEAlarm = length(SPEAlarmIndex); 77 | obj.numT2Alarm = length(T2AlarmIndex); 78 | 79 | label_ = obj.label; 80 | label_(SPEAlarmIndex) = -1; 81 | obj.accuracySPE = sum(label_ == obj.label)/obj.numSamples; 82 | label_ = obj.label; 83 | label_(T2AlarmIndex) = -1; 84 | obj.accuracyT2 = sum(label_ == obj.label)/obj.numSamples; 85 | % 86 | obj.runningTime = toc(tStart); 87 | if strcmp(obj.display, 'on') 88 | KernelPCAOption.displayTrain(obj) 89 | end 90 | end 91 | 92 | function results = test(obj, data, varargin) 93 | % test the model from the given data 94 | tStart = tic; 95 | results.evaluation = 'off'; 96 | if nargin == 3 97 | results.evaluation = 'on'; 98 | testLabel = varargin{1}; 99 | end 100 | Kt = obj.kernelFunc.computeMatrix(data, obj.data); 101 | % centralize the kernel matrix 102 | unit = ones(size(data, 1), obj.numSamples)/obj.numSamples; 103 | Kt_c = Kt-unit*obj.temporary.K-Kt*obj.temporary.unit+unit*obj.temporary.K*obj.temporary.unit; 104 | % 105 | results.numSamples = size(data, 1); 106 | results.data = data; 107 | results.score = Kt_c*obj.coefficient(:, 1:obj.numComponents); 108 | 109 | % compute Hotelling's T2 statistic 110 | results.T2 = diag(results.score/diag(obj.lambda(1:obj.numComponents))*results.score'); 111 | % compute the squared prediction error (SPE) 112 | results.SPE = sum((Kt_c*obj.coefficient).^2, 2)-sum(results.score.^2 , 2); 113 | 114 | % compute accuracy 115 | results.T2AlarmIndex = find(results.T2 > obj.T2Limit); 116 | results.SPEAlarmIndex = find(results.SPE > obj.SPELimit); 117 | if strcmp(results.evaluation, 'on') 118 | label_ = ones(size(results.data, 1), 1); 119 | label_(results.SPEAlarmIndex) = -1; 120 | results.accuracySPE = sum(label_ == testLabel)/results.numSamples; 121 | label_ = ones(size(results.data, 1), 1); 122 | label_(results.T2AlarmIndex) = -1; 123 | results.accuracyT2 = sum(label_ == testLabel)/results.numSamples; 124 | end 125 | results.numSPEAlarm = length(results.SPEAlarmIndex); 126 | results.numT2Alarm = length(results.T2AlarmIndex); 127 | results.temporary.Kt = Kt; 128 | results.runningTime = toc(tStart); 129 | 130 | if strcmp(obj.display, 'on') 131 | KernelPCAOption.displayTest(results) 132 | end 133 | 134 | % fault diagnosis 135 | if strcmp(obj.diagnosis.switch, 'on') 136 | results = obj.diagnose(results); 137 | end 138 | end 139 | 140 | function newData = reconstruct(obj) 141 | % Transform the mapping data back to original space. 142 | % References 143 | % ---------- 144 | % Bakır G H, Weston J, Schölkopf B. Learning to find pre-images[J]. 145 | % Advances in neural information processing systems, 2004, 16: 449-456. 146 | 147 | K_1 = obj.kernelFunc.computeMatrix(obj.score, obj.score); 148 | K_1_ = K_1; 149 | for i = 1:obj.numSamples 150 | K_1(i, i) = K_1(i, i)+obj.alpha; 151 | end 152 | dual_coef = mldivide(K_1, obj.data); 153 | K_2 = K_1_; 154 | newData = K_2*dual_coef; 155 | end 156 | 157 | function [coefficient, lambda] = computeEigenvalue(obj, K_c) 158 | % compute the eigenvalues and eigenvectors 159 | rng('default') 160 | [V, D, ~] = svd(K_c/obj.numSamples, 'econ'); 161 | % ill-conditioned matrix 162 | if ~(isreal(V)) || ~(isreal(D)) 163 | V = real(V); 164 | D = real(D); 165 | end 166 | lambda_ = diag(D); 167 | obj.cumContribution = cumsum(lambda_/sum(lambda_)); 168 | 169 | if isempty(obj.numComponents) 170 | obj.numComponents = obj.numFeatures; 171 | else 172 | if obj.numComponents >= 1 173 | obj.numComponents = obj.numComponents; 174 | else 175 | obj.explainedLevel = obj.numComponents; 176 | obj.numComponents = find(obj.cumContribution >= obj.numComponents, 1, 'first'); 177 | end 178 | end 179 | lambda = lambda_; 180 | try 181 | coefficient = V./sqrt(obj.numSamples*lambda_)'; 182 | catch 183 | coefficient = zeros(obj.numSamples, obj.numSamples); 184 | for i = 1:obj.numSamples 185 | coefficient(:, i) = V(:, i)/sqrt(obj.numSamples*lambda_(i, 1)); 186 | end 187 | end 188 | end 189 | 190 | function computeControlLimit(obj) 191 | % compute the squared prediction error (SPE) 192 | temp1 = obj.temporary.K_c*obj.coefficient; 193 | temp2 = obj.temporary.K_c*obj.coefficient(:, 1:obj.numComponents); 194 | obj.SPE = sum(temp1.^2, 2)-sum(temp2.^2, 2); 195 | obj.T2 = diag(obj.score/diag(obj.lambda(1:obj.numComponents))*obj.score'); 196 | 197 | % compute the T2 limit (the F-Distribution) 198 | k = obj.numComponents*(obj.numSamples-1)/(obj.numSamples-obj.numComponents); 199 | obj.T2Limit = k*finv(obj.significanceLevel, obj.numComponents, obj.numSamples-obj.numComponents); 200 | 201 | % compute the SPE limit (the Chi-square Distribution) 202 | a = mean(obj.SPE); 203 | b = var(obj.SPE); 204 | g = b/2/a; 205 | h = 2*a^2/b; 206 | obj.SPELimit = g*chi2inv(obj.significanceLevel, h); 207 | end 208 | 209 | function results = diagnose(obj, results, varargin) 210 | % falut diagnosis 211 | tStart = tic; 212 | fprintf('\n') 213 | fprintf('*** Fault diagnosis ***\n') 214 | fprintf('Fault diagnosis start...\n') 215 | results.diagnosis = obj.diagnosis; 216 | data_ = results.data; 217 | results.diagnosis.data = data_(results.diagnosis.start:results.diagnosis.end, :); 218 | % contribution plots of train data 219 | if ~exist('.\data', 'dir') 220 | mkdir data; 221 | end 222 | file_ = dir('.\data\*.mat'); 223 | name_ = {file_(1:length(file_)).name}'; 224 | if ismember('diagnosis.mat', name_) 225 | load('.\data\diagnosis.mat', 'tmp_') 226 | tmp__ = KernelPCAOption.saveCheckObj(obj); 227 | if isequal(tmp__, tmp_) 228 | load('.\data\diagnosis.mat', 'T2CpsTrain', 'SPECpsTrain') 229 | else 230 | [T2CpsTrain, SPECpsTrain] = obj.computeContribution(results, 'train'); 231 | tmp_ = KernelPCAOption.saveCheckObj(obj); 232 | save('.\data\diagnosis.mat', 'T2CpsTrain', 'SPECpsTrain', 'tmp_') 233 | end 234 | else 235 | [T2CpsTrain, SPECpsTrain] = obj.computeContribution(results, 'train'); 236 | tmp_ = KernelPCAOption.saveCheckObj(obj); 237 | save('.\data\diagnosis.mat', 'T2CpsTrain', 'SPECpsTrain', 'tmp_') 238 | end 239 | 240 | % contribution plots of test data 241 | [T2CpsTest, SPECpsTest] = obj.computeContribution(results, 'test'); 242 | % normalize the contribution plots 243 | T2CpsTrainMu = mean(T2CpsTrain); 244 | T2CpsTrainStd = std(T2CpsTrain); 245 | 246 | try 247 | T2Cps = bsxfun(@rdivide, bsxfun(@minus, T2CpsTest, T2CpsTrainMu), T2CpsTrainStd); 248 | catch 249 | mu_array = repmat(T2CpsTrainMu, size(T2CpsTest,1), 1); 250 | st_array = repmat(T2CpsTrainStd, size(T2CpsTest,1), 1); 251 | T2Cps = (T2CpsTest-mu_array)./st_array; 252 | end 253 | SPECpsTrainMu = mean(SPECpsTrain); 254 | SPECpsTrainStd = std(SPECpsTrain); 255 | try 256 | SPECps = bsxfun(@rdivide, bsxfun(@minus, SPECpsTest, SPECpsTrainMu), SPECpsTrainStd); 257 | catch 258 | mu_array = repmat(SPECpsTrainMu, size(SPECpsTest,1), 1); 259 | st_array = repmat(SPECpsTrainStd, size(SPECpsTest,1), 1); 260 | SPECps = (SPECpsTest-mu_array)./st_array; 261 | end 262 | 263 | % store the results 264 | results.diagnosis.T2Cps = T2Cps; 265 | results.diagnosis.SPECps = SPECps; 266 | 267 | % 268 | T2Cps_ = mean(abs(T2Cps), 1); 269 | results.diagnosis.meanT2Cps = T2Cps_/sum(T2Cps_, 2); 270 | 271 | SPECps_ = mean(abs(SPECps), 1); 272 | results.diagnosis.meanSPECps = SPECps_/sum(SPECps_, 2); 273 | 274 | % 275 | [value, index] = sort(results.diagnosis.meanT2Cps, 'descend'); 276 | results.diagnosis.faultVariabeT2.value = value; 277 | results.diagnosis.faultVariabeT2.index = index; 278 | 279 | [value, index] = sort(results.diagnosis.meanSPECps, 'descend'); 280 | results.diagnosis.faultVariabeSPE.value = value; 281 | results.diagnosis.faultVariabeSPE.index = index; 282 | 283 | results.diagnosis.runningTime = toc(tStart); 284 | if strcmp(obj.display, 'on') 285 | KernelPCAOption.displayDiagnose(results) 286 | end 287 | end 288 | 289 | function [T2Cps, SPECps] = computeContribution(obj, result, type) 290 | 291 | % Compute the Contribution Plots (CPs) 292 | % 293 | % Reference 294 | % [1] Deng X, Tian X. A new fault isolation method based on unified 295 | % contribution plots[C]//Proceedings of the 30th Chinese Control 296 | % Conference. IEEE, 2011: 4280-4285. 297 | % ------------------------------------------------------------------- 298 | % Thanks for the code provided by Rui. 299 | % -------------------------------------------------------------------- 300 | 301 | data_ = obj.data; 302 | switch type 303 | case 'train' 304 | Kt = obj.temporary.K; 305 | Y = data_; 306 | case 'test' 307 | Kt = result.temporary.Kt; 308 | Y = result.diagnosis.data; 309 | end 310 | 311 | K = obj.temporary.K; 312 | M = size(data_, 1); 313 | [Mt, d] = size(Y); 314 | 315 | A_T2 = obj.coefficient(:, 1:obj.numComponents)*... 316 | diag(obj.lambda(1:obj.numComponents))^(-1)*... 317 | obj.coefficient(:, 1:obj.numComponents)'; 318 | 319 | A_SPE = obj.coefficient(:, 1:obj.numComponents)*... 320 | obj.coefficient(:, 1:obj.numComponents)'; 321 | newY = Y*obj.theta; 322 | 323 | % initialization 324 | Knew = zeros(Mt, M); 325 | Knew_d1 = zeros(1, M); 326 | Knew_d2 = zeros(Mt, M); 327 | T2Cps = zeros(Mt, d); 328 | SPECps = zeros(Mt, d); 329 | Knew_s = zeros(Mt, M); 330 | sigma = sqrt(1/2/obj.kernelFunc.gamma); 331 | 332 | % compute contribution of statistic 333 | for i = 1:Mt 334 | for j = 1:d 335 | for k = 1:M 336 | Knew(i, k) = Kt(i, k); 337 | Knew_d1(k) = Knew(i, k)*2*obj.theta*(newY(i, j)-data_(k, j))/(-sigma^2); % derivative 338 | Knew_d2(i, k) = -2*Knew_d1(k); 339 | end 340 | Knew_d1_s = Knew_d1-ones(1, M)*mean(Knew_d1); 341 | Knew_s(i, :) = Knew(i, :)-ones(1, M)*K/M-Knew(i, :)*ones(M) ... 342 | /M+ones(1, M)/M*K*ones(M)/M; 343 | % contribution of T2 344 | T2Cps(i, j) = Y(i, j)*(Knew_d1_s*A_T2*Knew_s(i, :)' ... 345 | +Knew_s(i, :)*A_T2*Knew_d1_s'); 346 | % contribution of SPE 347 | SPECps(i, j)= Y(i, j)*mean(Knew_d2(i, :))-Y(i, j) ... 348 | *(Knew_d1_s*A_SPE*Knew_s(i, :)'+Knew_s(i, :)*A_SPE*Knew_d1_s'); 349 | end 350 | end 351 | end 352 | 353 | function numSamples = get.numSamples(obj) 354 | numSamples= size(obj.data, 1); 355 | end 356 | 357 | function numFeatures = get.numFeatures(obj) 358 | numFeatures= size(obj.data, 2); 359 | end 360 | end 361 | end 362 | -------------------------------------------------------------------------------- /KernelPCA/KernelPCAOption.m: -------------------------------------------------------------------------------- 1 | classdef KernelPCAOption < handle 2 | 3 | methods(Static) 4 | function checkInputForDiagnosis(obj) 5 | tmp_.switch = 'off'; 6 | tmp_.start = []; 7 | tmp_.end = []; 8 | errorText = sprintf('Incorrected input for diagnosis.\n'); 9 | if isempty(obj.diagnosis) 10 | obj.diagnosis = tmp_; 11 | else 12 | if ~strcmp(obj.kernelFunc.type, 'gaussian') 13 | error('Only fault diagnosis of Gaussian kernel is supported.') 14 | end 15 | if ~isa(obj.diagnosis, 'double') 16 | error(errorText); 17 | end 18 | numInput = length(obj.diagnosis); 19 | switch numInput 20 | case 1 21 | tmp_.switch = 'on'; 22 | tmp_.start = obj.diagnosis(1); 23 | tmp_.end = tmp_.start; 24 | case 2 25 | if obj.diagnosis(1) > obj.diagnosis(2) 26 | error(errorText) 27 | end 28 | tmp_.switch = 'on'; 29 | tmp_.start = obj.diagnosis(1); 30 | tmp_.end = obj.diagnosis(2); 31 | otherwise 32 | error(errorText) 33 | end 34 | obj.diagnosis = tmp_; 35 | end 36 | end 37 | 38 | function tmp_ = saveCheckObj(kpca) 39 | names_ = fieldnames(kpca.kernelFunc); 40 | for i = 1:length(names_) 41 | tmp_.kernelFunc.(names_{i}) = (kpca.kernelFunc.(names_{i})); 42 | end 43 | tmp_.data = kpca.data; 44 | tmp_.numComponents = kpca.numComponents; 45 | tmp_.explainedLevel = kpca.explainedLevel; 46 | tmp_.eigenvalueTolerance = kpca.eigenvalueTolerance; 47 | tmp_.significanceLevel = kpca.significanceLevel; 48 | end 49 | 50 | function displayTrain(kpca) 51 | fprintf('\n') 52 | fprintf('*** KPCA model training finished ***\n') 53 | fprintf('running time = %.4f seconds\n', kpca.runningTime) 54 | fprintf('kernel function = %s \n', kpca.kernelFunc.type) 55 | fprintf('number of samples = %d \n', kpca.numSamples) 56 | fprintf('number of features = %d \n', kpca.numFeatures) 57 | fprintf('number of components = %d \n', kpca.numComponents) 58 | fprintf('number of T2 alarm = %d \n', kpca.numT2Alarm) 59 | fprintf('number of SPE alarm = %d \n', kpca.numSPEAlarm) 60 | fprintf('accuracy of T2 = %.4f%% \n', 100*kpca.accuracyT2) 61 | fprintf('accuracy of SPE = %.4f%% \n', 100*kpca.accuracySPE) 62 | end 63 | 64 | function displayTest(results) 65 | fprintf('\n') 66 | fprintf('*** KPCA model test finished ***\n') 67 | fprintf('running time = %.4f seconds\n', results.runningTime) 68 | fprintf('number of test data = %d \n', results.numSamples) 69 | fprintf('number of T2 alarm = %d \n', results.numT2Alarm) 70 | fprintf('number of SPE alarm = %d \n', results.numSPEAlarm) 71 | if strcmp(results.evaluation, 'on') 72 | fprintf('accuracy of T2 = %.4f%% \n', 100*results.accuracyT2) 73 | fprintf('accuracy of SPE = %.4f%% \n', 100*results.accuracySPE) 74 | end 75 | fprintf('\n') 76 | end 77 | 78 | function displayDiagnose(results) 79 | fprintf('Fault diagnosis finished.\n') 80 | fprintf('running time = %.4f seconds\n', results.diagnosis.runningTime) 81 | fprintf('start point = %d \n', results.diagnosis.start) 82 | fprintf('ending point = %d \n', results.diagnosis.end) 83 | tmp_T2 = results.diagnosis.faultVariabeT2.index; 84 | tmp_SPE = results.diagnosis.faultVariabeSPE.index; 85 | if length(tmp_T2) > 3 86 | numShow = 3; 87 | else 88 | numShow = length(tmp_T2); 89 | end 90 | fprintf('fault variables (T2) = %s \n', num2str(tmp_T2(1:numShow))) 91 | fprintf('fault variables (SPE) = %s \n', num2str(tmp_SPE(1:numShow))) 92 | fprintf('\n') 93 | end 94 | end 95 | end -------------------------------------------------------------------------------- /KernelPCA/KernelPCAVisualization.m: -------------------------------------------------------------------------------- 1 | classdef KernelPCAVisualization < handle 2 | %{ 3 | CLASS DESCRIPTION 4 | 5 | Visualization of trained KPCA model and test results. 6 | 7 | ----------------------------------------------------------------- 8 | 9 | Version 1.1, 14-MAY-2021 10 | Email: iqiukp@outlook.com 11 | ----------------------------------------------------------------- 12 | %} 13 | 14 | properties (Constant) 15 | positionForOne = [300 150 600 300] 16 | positionForTwo = [300 150 800 600] 17 | colorStatistics = [125, 45, 141]/255 18 | colorLimit = [213, 81, 36]/255 19 | colorFace = [213, 81, 36]/255 20 | colorScatter = [0, 114, 189;162, 20, 47]/255; 21 | colorContribution = [162, 20, 47]/255 22 | lineWidthStatistics = 2 23 | lineWidthBox = 1.1 24 | scatterSize = 36 25 | tickDirection = 'in' 26 | end 27 | 28 | methods 29 | function cumContribution(obj, kpca) 30 | dims_ = find(kpca.cumContribution > 0.99, 1, 'first'); 31 | cumContribution_ = kpca.cumContribution(1:dims_)*100; 32 | figure 33 | set(gcf, 'position', obj.positionForOne) 34 | hold on 35 | plot(cumContribution_,... 36 | 'color', obj.colorContribution,... 37 | 'LineStyle', '-', 'LineWidth', obj.lineWidthStatistics) 38 | xlim([1, length(cumContribution_)]); 39 | ylim([cumContribution_(1), 100]) 40 | posi = [kpca.numComponents, cumContribution_(kpca.numComponents)]; 41 | % connected line 42 | line([posi(1, 1), posi(1, 1)], [cumContribution_(1), posi(1, 2)],... 43 | 'color', 'k', 'LineStyle', '--', 'LineWidth', obj.lineWidthBox); 44 | line([1, posi(1, 1)], [posi(1, 2), posi(1, 2)],... 45 | 'color', 'k', 'LineStyle', '--', 'LineWidth', obj.lineWidthBox); 46 | % 47 | plot(posi(1, 1), posi(1, 2),... 48 | 'Marker', 'o',... 49 | 'MarkerSize', 8,... 50 | 'MarkerEdgeColor', obj.colorContribution,... 51 | 'MarkerFaceColor', obj.colorContribution); 52 | 53 | testStr = ['(', num2str(posi(1, 1)), ', ', num2str(posi(1, 2)), '%)']; 54 | text(posi(1, 1)*1.1, posi(1, 2)*0.95, testStr, 'FontWeight', 'bold') 55 | ytickformat('%d%%') 56 | xlabel('Component number') 57 | ylabel('Cumulative contributions') 58 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 59 | box(gca, 'on'); 60 | end 61 | 62 | function trainResults(obj, kpca) 63 | figure 64 | set(gcf, 'position', obj.positionForTwo) 65 | subplot(2, 1, 1) 66 | plot(kpca.SPELimit*ones(kpca.numSamples, 1),... 67 | 'color', obj.colorLimit,... 68 | 'LineStyle', '-',.... 69 | 'LineWidth', obj.lineWidthStatistics) 70 | hold on 71 | plot(kpca.SPE,... 72 | 'color', obj.colorStatistics,... 73 | 'LineStyle', '-',.... 74 | 'LineWidth', obj.lineWidthStatistics) 75 | legend({'SPE limit', 'SPE'}) 76 | xlabel('Samples') 77 | ylabel('SPE') 78 | set(gca, 'LineWidth', obj.lineWidthBox,... 79 | 'TickDir', obj.tickDirection, 'yscale','log') 80 | box(gca, 'on'); 81 | 82 | subplot(2, 1, 2) 83 | plot(kpca.T2Limit*ones(kpca.numSamples, 1),... 84 | 'color', obj.colorLimit,... 85 | 'LineStyle', '-',.... 86 | 'LineWidth', obj.lineWidthStatistics) 87 | hold on 88 | plot(kpca.T2,... 89 | 'color', obj.colorStatistics,... 90 | 'LineStyle', '-',.... 91 | 'LineWidth', obj.lineWidthStatistics) 92 | legend({'T2 limit','T2'}) 93 | xlabel('Samples') 94 | ylabel('T2') 95 | set(gca, 'LineWidth', obj.lineWidthBox,... 96 | 'TickDir', obj.tickDirection, 'yscale','log') 97 | box(gca, 'on'); 98 | end 99 | 100 | function testResults(obj, kpca, results) 101 | figure 102 | set(gcf, 'position', obj.positionForTwo) 103 | subplot(2, 1, 1) 104 | plot(kpca.SPELimit*ones(results.numSamples, 1),... 105 | 'color', obj.colorLimit,... 106 | 'LineStyle', '-',.... 107 | 'LineWidth', obj.lineWidthStatistics) 108 | hold on 109 | plot(results.SPE,... 110 | 'color', obj.colorStatistics,... 111 | 'LineStyle', '-',.... 112 | 'LineWidth', obj.lineWidthStatistics) 113 | legend({'SPE limit','SPE'}) 114 | xlabel('Samples') 115 | ylabel('SPE') 116 | set(gca, 'LineWidth', obj.lineWidthBox,... 117 | 'TickDir', obj.tickDirection, 'yscale','log') 118 | box(gca, 'on'); 119 | 120 | subplot(2, 1, 2) 121 | plot(kpca.T2Limit*ones(results.numSamples, 1),... 122 | 'color', obj.colorLimit,... 123 | 'LineStyle', '-',.... 124 | 'LineWidth', obj.lineWidthStatistics) 125 | hold on 126 | plot(results.T2,... 127 | 'color', obj.colorStatistics,... 128 | 'LineStyle', '-',.... 129 | 'LineWidth', obj.lineWidthStatistics) 130 | legend({'T2 limit','T2'}) 131 | xlabel('Samples') 132 | ylabel('T2') 133 | set(gca, 'LineWidth', obj.lineWidthBox,... 134 | 'TickDir', obj.tickDirection, 'yscale','log') 135 | box(gca, 'on'); 136 | end 137 | 138 | function score(obj, kpca) 139 | if kpca.numFeatures < 2 || kpca.numFeatures > 3 ... 140 | || kpca.numComponents < 2 || kpca.numComponents > 3 141 | error('This demonstration only supports visualization of 2D or 3D data.') 142 | end 143 | for i = 1:3 144 | colormap_(:, i) = linspace(obj.colorScatter(1,i),obj.colorScatter(2,i),kpca.numSamples); 145 | end 146 | 147 | % original data 148 | figure 149 | set(gcf, 'position', obj.positionForOne) 150 | subplot(1, 2, 1) 151 | switch kpca.numFeatures 152 | case 2 153 | scatter(kpca.data(:, 1), kpca.data(:, 2),... 154 | obj.scatterSize, colormap_, 'filled') 155 | 156 | case 3 157 | scatter3(kpca.data(:, 1), kpca.data(:, 2), kpca.data(:, 3),... 158 | obj.scatterSize, colormap_, 'filled') 159 | end 160 | colormap(gca, 'parula') 161 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 162 | box(gca, 'on'); 163 | title('Original data') 164 | 165 | % scores 166 | subplot(1, 2, 2) 167 | switch kpca.numComponents 168 | case 2 169 | scatter(kpca.score(:, 1), kpca.score(:, 2),... 170 | obj.scatterSize, colormap_, 'filled') 171 | 172 | case 3 173 | scatter3(kpca.score(:, 1), kpca.score(:, 2), kpca.score(:, 3),... 174 | obj.scatterSize, colormap_, 'filled') 175 | end 176 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 177 | box(gca, 'on'); 178 | title('Mapping data'); 179 | end 180 | 181 | function reconstruction(obj, kpca) 182 | if kpca.numFeatures < 2 || kpca.numFeatures > 3 ... 183 | || kpca.numComponents < 2 || kpca.numComponents > 3 184 | error('This demonstration only supports visualization of 2D or 3D data.') 185 | end 186 | 187 | for i = 1:3 188 | colormap_(:, i) = linspace(obj.colorScatter(1,i),obj.colorScatter(2,i),kpca.numSamples); 189 | end 190 | 191 | % original data 192 | figure 193 | set(gcf, 'position', obj.positionForOne) 194 | subplot(1, 2, 1) 195 | switch kpca.numFeatures 196 | case 2 197 | scatter(kpca.data(:, 1), kpca.data(:, 2),... 198 | obj.scatterSize, colormap_, 'filled') 199 | 200 | case 3 201 | scatter3(kpca.data(:, 1), kpca.data(:, 2), kpca.data(:, 3),... 202 | obj.scatterSize, colormap_, 'filled') 203 | end 204 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 205 | box(gca, 'on'); 206 | title('Original data') 207 | 208 | subplot(1, 2, 2) 209 | switch kpca.numFeatures 210 | case 2 211 | scatter(kpca.newData(:, 1), kpca.newData(:, 2),... 212 | obj.scatterSize, colormap_, 'filled') 213 | 214 | case 3 215 | scatter3(kpca.newData(:, 1), kpca.newData(:, 2), kpca.newData(:, 3),... 216 | obj.scatterSize, colormap_, 'filled') 217 | end 218 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 219 | box(gca, 'on'); 220 | title('Reconstructed data'); 221 | end 222 | 223 | function diagnosis(obj, results) 224 | figure 225 | set(gcf,'position', obj.positionForTwo) 226 | subplot(2, 1, 1) 227 | bar(results.diagnosis.meanSPECps,... 228 | 'FaceColor', obj.colorFace,... 229 | 'EdgeColor', 'k',... 230 | 'LineWidth', obj.lineWidthStatistics); 231 | xlabel('Variables') 232 | ylabel('SPE contributions') 233 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 234 | titleStr = ['Sampling points between ', num2str(results.diagnosis.start),... 235 | ' and ', num2str(results.diagnosis.end)]; 236 | title(titleStr) 237 | 238 | subplot(2, 1, 2) 239 | bar(results.diagnosis.meanT2Cps,... 240 | 'FaceColor', obj.colorFace,... 241 | 'EdgeColor', 'k',... 242 | 'LineWidth', obj.lineWidthStatistics); 243 | xlabel('Variables') 244 | ylabel('T2 contributions') 245 | set(gca, 'LineWidth', obj.lineWidthBox, 'TickDir', obj.tickDirection) 246 | title(titleStr) 247 | end 248 | end 249 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
MATLAB code for dimensionality reduction, fault detection, and fault diagnosis using KPCA
8 |Version 2.2, 14-MAY-2021
9 |Email: iqiukp@outlook.com
10 | 11 |
122 |
123 |
127 |
128 |
156 |
157 |
197 |
198 |
234 |
235 |
283 |
284 |
296 |
297 |
347 |
348 |