├── README.md
├── RvmModel
├── BaseRVM.m
├── Kernel.m
├── RvmOptimization.m
├── RvmOptimizationOption.m
└── RvmOption.m
├── SB2_Release_200
├── Readme.txt
├── SB2_ControlSettings.m
├── SB2_Diagnostic.m
├── SB2_FormatTime.m
├── SB2_FullStatistics.m
├── SB2_Initialisation.m
├── SB2_Likelihoods.m
├── SB2_Manual.pdf
├── SB2_ParameterSettings.m
├── SB2_PosteriorMode.m
├── SB2_PreProcessBasis.m
├── SB2_Sigmoid.m
├── SB2_UserOptions.m
├── SparseBayes.m
├── SparseBayesDemo.m
└── licence.txt
├── custom_option.m
├── data
├── UCI_data.mat
└── sinc_data.mat
├── demo_RVC.m
├── demo_RVR.m
├── demo_hybrid_kernel.m
├── demo_hybrid_kernel_optimization.m
├── demo_kernel_function.m
└── demo_optimization.m
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Relevance Vector Machine (RVM)
6 |
7 | MATLAB code for Relevance Vector Machine
8 | Version 2.1, 31-AUG-2021
9 | Email: iqiukp@outlook.com
10 |
11 |
12 |
13 | [](https://www.mathworks.com/matlabcentral/fileexchange/69407-relevance-vector-machine-rvm)
14 |

15 |

16 |

17 |

18 |

19 |

20 |
21 |
22 |
23 |
24 | ## Main features
25 |
26 | - RVM model for binary classification (RVC) or regression (RVR)
27 | - Multiple kinds of kernel functions (linear, gaussian, polynomial, sigmoid, laplacian)
28 | - Hybrid kernel functions (K =w1×K1+w2×K2+...+wn×Kn)
29 | - Parameter Optimization using Bayesian optimization, Genetic Algorithm, and Particle Swarm Optimization
30 |
31 | ## Notices
32 |
33 | - This version of the code is not compatible with the versions lower than R2016b.
34 | - Detailed applications please see the demonstrations.
35 | - This code is for reference only.
36 |
37 | ## Citation
38 | ```
39 | @article{tipping2001sparse,
40 | title={Sparse Bayesian learning and the relevance vector machine},
41 | author={Tipping, Michael E},
42 | journal={Journal of machine learning research},
43 | volume={1},
44 | number={Jun},
45 | pages={211--244},
46 | year={2001}
47 | }
48 | ```
49 | ```
50 | @article{qiu2021soft,
51 | title={Soft sensor development based on kernel dynamic time warping and a relevant vector machine for unequal-length batch processes},
52 | author={Qiu, Kepeng and Wang, Jianlin and Wang, Rutong and Guo, Yongqi and Zhao, Liqiang},
53 | journal={Expert Systems with Applications},
54 | volume={182},
55 | pages={115223},
56 | year={2021},
57 | publisher={Elsevier}
58 | }
59 | ```
60 |
61 |
62 | ## How to use
63 |
64 | ### 01. Classification using RVM (RVC)
65 |
66 | A demo for classification using RVM
67 | ```MATLAB
68 | clc
69 | clear all
70 | close all
71 | addpath(genpath(pwd))
72 |
73 | % use fisheriris dataset
74 | load fisheriris
75 | inds = ~strcmp(species, 'setosa');
76 | data_ = meas(inds, 3:4);
77 | label_ = species(inds);
78 | cvIndices = crossvalind('HoldOut', length(data_), 0.3);
79 | trainData = data_(cvIndices, :);
80 | trainLabel = label_(cvIndices, :);
81 | testData = data_(~cvIndices, :);
82 | testLabel = label_(~cvIndices, :);
83 |
84 | % kernel function
85 | kernel = Kernel('type', 'gaussian', 'gamma', 0.2);
86 |
87 | % parameter
88 | parameter = struct( 'display', 'on',...
89 | 'type', 'RVC',...
90 | 'kernelFunc', kernel);
91 | rvm = BaseRVM(parameter);
92 |
93 | % RVM model training, testing, and visualization
94 | rvm.train(trainData, trainLabel);
95 | results = rvm.test(testData, testLabel);
96 | rvm.draw(results)
97 | ```
98 |
99 | results:
100 | ```MATLAB
101 | *** RVM model (classification) train finished ***
102 | running time = 0.1604 seconds
103 | iterations = 20
104 | number of samples = 70
105 | number of RVs = 2
106 | ratio of RVs = 2.8571%
107 | accuracy = 94.2857%
108 |
109 |
110 | *** RVM model (classification) test finished ***
111 | running time = 0.0197 seconds
112 | number of samples = 30
113 | accuracy = 96.6667%
114 | ```
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | ### 02. Regression using RVM (RVR)
123 |
124 | A demo for regression using RVM
125 | ```MATLAB
126 | clc
127 | clear all
128 | close all
129 | addpath(genpath(pwd))
130 |
131 | % sinc funciton
132 | load sinc_data
133 | trainData = x;
134 | trainLabel = y;
135 | testData = xt;
136 | testLabel = yt;
137 |
138 | % kernel function
139 | kernel = Kernel('type', 'gaussian', 'gamma', 0.1);
140 |
141 | % parameter
142 | parameter = struct( 'display', 'on',...
143 | 'type', 'RVR',...
144 | 'kernelFunc', kernel);
145 | rvm = BaseRVM(parameter);
146 |
147 | % RVM model training, testing, and visualization
148 | rvm.train(trainData, trainLabel);
149 | results = rvm.test(testData, testLabel);
150 | rvm.draw(results)
151 | ```
152 | results:
153 | ```MATLAB
154 | *** RVM model (regression) train finished ***
155 | running time = 0.1757 seconds
156 | iterations = 76
157 | number of samples = 100
158 | number of RVs = 6
159 | ratio of RVs = 6.0000%
160 | RMSE = 0.1260
161 | R2 = 0.8821
162 | MAE = 0.0999
163 |
164 |
165 | *** RVM model (regression) test finished ***
166 | running time = 0.0026 seconds
167 | number of samples = 50
168 | RMSE = 0.1424
169 | R2 = 0.8553
170 | MAE = 0.1106
171 | ```
172 |
173 |
174 |
175 |
176 |
177 |
178 | ### 03. Kernel funcions
179 |
180 | A class named ***Kernel*** is defined to compute kernel function matrix.
181 | ```MATLAB
182 | %{
183 | type -
184 |
185 | linear : k(x,y) = x'*y
186 | polynomial : k(x,y) = (γ*x'*y+c)^d
187 | gaussian : k(x,y) = exp(-γ*||x-y||^2)
188 | sigmoid : k(x,y) = tanh(γ*x'*y+c)
189 | laplacian : k(x,y) = exp(-γ*||x-y||)
190 |
191 |
192 | degree - d
193 | offset - c
194 | gamma - γ
195 | %}
196 | kernel = Kernel('type', 'gaussian', 'gamma', value);
197 | kernel = Kernel('type', 'polynomial', 'degree', value);
198 | kernel = Kernel('type', 'linear');
199 | kernel = Kernel('type', 'sigmoid', 'gamma', value);
200 | kernel = Kernel('type', 'laplacian', 'gamma', value);
201 | ```
202 | For example, compute the kernel matrix between **X** and **Y**
203 | ```MATLAB
204 | X = rand(5, 2);
205 | Y = rand(3, 2);
206 | kernel = Kernel('type', 'gaussian', 'gamma', 2);
207 | kernelMatrix = kernel.computeMatrix(X, Y);
208 | >> kernelMatrix
209 |
210 | kernelMatrix =
211 |
212 | 0.5684 0.5607 0.4007
213 | 0.4651 0.8383 0.5091
214 | 0.8392 0.7116 0.9834
215 | 0.4731 0.8816 0.8052
216 | 0.5034 0.9807 0.7274
217 | ```
218 |
219 | ### 04. Hybrid kernel
220 |
221 | A demo for regression using RVM with hybrid_kernel (K =w1×K1+w2×K2+...+wn×Kn)
222 | ```MATLAB
223 | clc
224 | clear all
225 | close all
226 | addpath(genpath(pwd))
227 |
228 | % sinc funciton
229 | load sinc_data
230 | trainData = x;
231 | trainLabel = y;
232 | testData = xt;
233 | testLabel = yt;
234 |
235 | % kernel function
236 | kernel_1 = Kernel('type', 'gaussian', 'gamma', 0.3);
237 | kernel_2 = Kernel('type', 'polynomial', 'degree', 2);
238 | kernelWeight = [0.5, 0.5];
239 | % parameter
240 | parameter = struct( 'display', 'on',...
241 | 'type', 'RVR',...
242 | 'kernelFunc', [kernel_1, kernel_2],...
243 | 'kernelWeight', kernelWeight);
244 | rvm = BaseRVM(parameter);
245 |
246 | % RVM model training, testing, and visualization
247 | rvm.train(trainData, trainLabel);
248 | results = rvm.test(testData, testLabel);
249 | rvm.draw(results)
250 | ```
251 |
252 | ### 05. Parameter Optimization for single-kernel-RVM
253 |
254 | A demo for RVM model with Parameter Optimization
255 |
256 | ```MATLAB
257 | clc
258 | clear all
259 | close all
260 | addpath(genpath(pwd))
261 |
262 | % use fisheriris dataset
263 | load fisheriris
264 | inds = ~strcmp(species, 'setosa');
265 | data_ = meas(inds, 3:4);
266 | label_ = species(inds);
267 | cvIndices = crossvalind('HoldOut', length(data_), 0.3);
268 | trainData = data_(cvIndices, :);
269 | trainLabel = label_(cvIndices, :);
270 | testData = data_(~cvIndices, :);
271 | testLabel = label_(~cvIndices, :);
272 |
273 | % kernel function
274 | kernel = Kernel('type', 'gaussian', 'gamma', 5);
275 |
276 | % parameter optimization
277 | opt.method = 'bayes'; % bayes, ga, pso
278 | opt.display = 'on';
279 | opt.iteration = 20;
280 |
281 | % parameter
282 | parameter = struct( 'display', 'on',...
283 | 'type', 'RVC',...
284 | 'kernelFunc', kernel,...
285 | 'optimization', opt);
286 | rvm = BaseRVM(parameter);
287 |
288 | % RVM model training, testing, and visualization
289 | rvm.train(trainData, trainLabel);
290 | results = rvm.test(trainData, trainLabel);
291 | rvm.draw(results)
292 |
293 | ```
294 |
295 | results:
296 | ```MATLAB
297 | *** RVM model (classification) train finished ***
298 | running time = 13.3356 seconds
299 | iterations = 88
300 | number of samples = 70
301 | number of RVs = 4
302 | ratio of RVs = 5.7143%
303 | accuracy = 97.1429%
304 | Optimized parameter table
305 |
306 | gaussian_gamma
307 | ______________
308 |
309 | 7.8261
310 |
311 | *** RVM model (classification) test finished ***
312 | running time = 0.0195 seconds
313 | number of samples = 70
314 | accuracy = 97.1429%
315 | ```
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 | ### 06. Parameter Optimization for hybrid-kernel-RVM
324 |
325 | A demo for RVM model with Parameter Optimization
326 |
327 | ```MATLAB
328 | %{
329 | A demo for hybrid-kernel RVM model with Parameter Optimization
330 | %}
331 |
332 |
333 | clc
334 | clear all
335 | close all
336 | addpath(genpath(pwd))
337 |
338 | % data
339 | load UCI_data
340 | trainData = x;
341 | trainLabel = y;
342 | testData = xt;
343 | testLabel = yt;
344 |
345 | % kernel function
346 | kernel_1 = Kernel('type', 'gaussian', 'gamma', 0.5);
347 | kernel_2 = Kernel('type', 'polynomial', 'degree', 2);
348 |
349 | % parameter optimization
350 | opt.method = 'bayes'; % bayes, ga, pso
351 | opt.display = 'on';
352 | opt.iteration = 30;
353 |
354 | % parameter
355 | parameter = struct( 'display', 'on',...
356 | 'type', 'RVR',...
357 | 'kernelFunc', [kernel_1, kernel_2],...
358 | 'optimization', opt);
359 | rvm = BaseRVM(parameter);
360 |
361 | % RVM model training, testing, and visualization
362 | rvm.train(trainData, trainLabel);
363 | results = rvm.test(testData, testLabel);
364 | rvm.draw(results)
365 |
366 | ```
367 |
368 | results:
369 | ```MATLAB
370 | *** RVM model (regression) train finished ***
371 | running time = 24.4042 seconds
372 | iterations = 377
373 | number of samples = 264
374 | number of RVs = 22
375 | ratio of RVs = 8.3333%
376 | RMSE = 0.4864
377 | R2 = 0.7719
378 | MAE = 0.3736
379 | Optimized parameter 1×6 table
380 |
381 | gaussian_gamma polynomial_gamma polynomial_offset polynomial_degree gaussian_weight polynomial_weight
382 | ______________ ________________ _________________ _________________ _______________ _________________
383 |
384 | 22.315 13.595 44.83 6 0.042058 0.95794
385 |
386 |
387 |
388 |
389 | *** RVM model (regression) test finished ***
390 | running time = 0.0008 seconds
391 | number of samples = 112
392 | RMSE = 0.7400
393 | R2 = 0.6668
394 | MAE = 0.4867
395 | ```
396 |
397 |
398 |
399 |
400 |
401 | ### 07. Cross Validation
402 |
403 | In this code, two cross-validation methods are supported: 'K-Folds' and 'Holdout'.
404 | For example, the cross-validation of 5-Folds is
405 | ```MATLAB
406 | parameter = struct( 'display', 'on',...
407 | 'type', 'RVC',...
408 | 'kernelFunc', kernel,...
409 | 'KFold', 5);
410 | ```
411 | For example, the cross-validation of the Holdout method with a ratio of 0.3 is
412 | ```MATLAB
413 | parameter = struct( 'display', 'on',...
414 | 'type', 'RVC',...
415 | 'kernelFunc', kernel,...
416 | 'HoldOut', 0.3);
417 | ```
418 |
419 | ### 08. Other option
420 | ```MATLAB
421 | %% custom optimization option
422 | %{
423 | opt.method = 'bayes'; % bayes, ga, pso
424 | opt.display = 'on';
425 | opt.iteration = 20;
426 | opt.point = 10;
427 |
428 | % gaussian kernel function
429 | opt.gaussian.parameterName = {'gamma'};
430 | opt.gaussian.parameterType = {'real'};
431 | opt.gaussian.lowerBound = 2^-6;
432 | opt.gaussian.upperBound = 2^6;
433 |
434 | % laplacian kernel function
435 | opt.laplacian.parameterName = {'gamma'};
436 | opt.laplacian.parameterType = {'real'};
437 | opt.laplacian.lowerBound = 2^-6;
438 | opt.laplacian.upperBound = 2^6;
439 |
440 | % polynomial kernel function
441 | opt.polynomial.parameterName = {'gamma'; 'offset'; 'degree'};
442 | opt.polynomial.parameterType = {'real'; 'real'; 'integer'};
443 | opt.polynomial.lowerBound = [2^-6; 2^-6; 1];
444 | opt.polynomial.upperBound = [2^6; 2^6; 7];
445 |
446 | % sigmoid kernel function
447 | opt.sigmoid.parameterName = {'gamma'; 'offset'};
448 | opt.sigmoid.parameterType = {'real'; 'real'};
449 | opt.sigmoid.lowerBound = [2^-6; 2^-6];
450 | opt.sigmoid.upperBound = [2^6; 2^6];
451 | %}
452 |
453 | %% RVM model parameter
454 | %{
455 | 'display' : 'on', 'off'
456 | 'type' : 'RVR', 'RVC'
457 | 'kernelFunc' : kernel function
458 | 'KFolds' : cross validation, for example, 5
459 | 'HoldOut' : cross validation, for example, 0.3
460 | 'freeBasis' : 'on', 'off'
461 | 'maxIter' : max iteration, for example, 1000
462 | %}
463 | ```
464 |
465 |
466 |
467 |
468 |
--------------------------------------------------------------------------------
/RvmModel/BaseRVM.m:
--------------------------------------------------------------------------------
1 | classdef BaseRVM < handle & matlab.mixin.Copyable
2 | %{
3 | Version 2.1, 31-AUG-2021
4 | Email: iqiukp@outlook.com
5 | %}
6 |
7 | properties
8 | data
9 | label
10 | label_
11 | type = 'RVR' % classification regression
12 | kernelFunc = Kernel('type', 'gaussian', 'gamma', 0.5)
13 | kernelFuncName
14 | kernelType = 'single'
15 | kernelWeight
16 | relevanceVectors
17 | relevanceVectorIndices
18 | numRelevanceVectors
19 | numIterations
20 | numKernel
21 | rationRelevanceVectors
22 | alpha
23 | alphaTolerance = 1e-6
24 | relevanceVectorAlpha
25 | freeBasis = 'off'
26 | runningTime
27 | maxIter = 1000
28 | bias
29 | sigma
30 | beta
31 | PHI
32 | weight
33 | predictedLabel
34 | predictedLabel_
35 | display = 'on'
36 | optimization
37 | crossValidation
38 | performance
39 | evaluationMode = 'test'
40 | classNames
41 | end
42 |
43 | properties (Dependent)
44 | numSamples
45 | numFeatures
46 | end
47 |
48 | methods
49 | % create an object of RVM
50 | function obj = BaseRVM(parameter)
51 | RvmOption.setParameter(obj, parameter);
52 | end
53 |
54 | % train RVM model
55 | function varargout = train(obj, data, label) % varargin
56 | tStart = tic;
57 | obj.label = label;
58 | obj.data = data;
59 | obj.classNames = unique(label);
60 | if strcmp(obj.type, 'RVC')
61 | obj.label_ = RvmOption.checkLabel(obj, label);
62 | else
63 | obj.label_ = label;
64 | end
65 | % parameter optimization
66 | if strcmp(obj.optimization.switch, 'on')
67 | RvmOptimization.getModel(obj);
68 | else
69 | getModel(obj);
70 | end
71 | % % cross validation
72 | % if strcmp(obj.crossValidation.switch, 'on')
73 | % rvm_ = copy(obj);
74 | % obj.crossValidation.accuracy = RvmOptimization.crossvalFunc(rvm_);
75 | % end
76 |
77 | obj.runningTime = toc(tStart);
78 | % display
79 | if strcmp(obj.display, 'on')
80 | RvmOption.displayTrain(obj);
81 | end
82 | % output
83 | if nargout == 1
84 | varargout{1} = obj;
85 | end
86 | end
87 |
88 | function getModel(obj)
89 | switch obj.type
90 | case 'RVC'
91 | likelihoodFunc = 'Bernoulli';
92 | case 'RVR'
93 | likelihoodFunc = 'Gaussian';
94 | end
95 |
96 | switch obj.kernelType
97 | case 'single'
98 | K = obj.kernelFunc.computeMatrix(obj.data, obj.data);
99 | case 'hybrid'
100 | K = 0;
101 | for i = 1:obj.numKernel
102 | K = K+obj.kernelWeight(i)*obj.kernelFunc(i).computeMatrix(obj.data, obj.data);
103 | end
104 | end
105 |
106 | switch obj.freeBasis
107 | case 'off'
108 | % no bias
109 | BASIS = K;
110 | options_ = SB2_UserOptions('iterations', obj.maxIter);
111 | case 'on'
112 | BASIS = [K, ones(obj.numSamples, 1)];
113 | options_ = SB2_UserOptions('freeBasis', obj.numSamples+1, 'iterations', obj.maxIter);
114 | end
115 |
116 | % model
117 | [PARAMETER, HYPERPARAMETER, DIAGNOSTIC] =...
118 | SparseBayes(likelihoodFunc, BASIS, obj.label_, options_);
119 |
120 | % store the model
121 | obj.relevanceVectorIndices = PARAMETER.Relevant; % index of relevance vectors
122 |
123 | switch obj.freeBasis
124 | case 'off'
125 | obj.bias = 0; % value of bias
126 | case 'on'
127 | obj.bias = PARAMETER.Value(end); % value of bias
128 | obj.relevanceVectorIndices(obj.relevanceVectorIndices == obj.numSamples+1) = [];
129 | end
130 |
131 | obj.weight = PARAMETER.Value; % vector of weight values
132 | obj.beta = HYPERPARAMETER.beta; % noise precision
133 | obj.alpha = HYPERPARAMETER.Alpha; % vector of weight precision values
134 | obj.PHI = BASIS(:, PARAMETER.Relevant); % Used BASIS
135 | obj.numIterations = DIAGNOSTIC.iterations; % iteration
136 | obj.numRelevanceVectors = size(obj.relevanceVectorIndices, 1); % number of relevance vectors
137 | obj.rationRelevanceVectors = obj.numRelevanceVectors/obj.numSamples;
138 | obj.relevanceVectors = obj.data(obj.relevanceVectorIndices, :); % relevance vectors
139 |
140 | switch obj.type
141 | case 'RVC'
142 | obj.predictedLabel_ = zeros(obj.numSamples, 1);
143 | index_ = SB2_Sigmoid(obj.PHI*obj.weight)>0.5;
144 | obj.predictedLabel_(index_) = 1;
145 | obj.predictedLabel = RvmOption.restoreLabel(obj.classNames, obj.predictedLabel_);
146 | case 'RVR'
147 | obj.sigma = inv(diag(obj.alpha)+obj.beta*(obj.PHI'*obj.PHI));
148 | obj.predictedLabel = obj.PHI*obj.weight;
149 | end
150 |
151 | % model evaluation
152 | display_ = obj.display;
153 | evaluationMode_ = obj.evaluationMode;
154 | obj.display = 'off';
155 | obj.evaluationMode = 'train';
156 | results_ = test(obj, obj.data, obj.label_);
157 | obj.display = display_;
158 | obj.evaluationMode = evaluationMode_;
159 | obj.performance = evaluateModel(obj, results_);
160 | end
161 |
162 | function results = test(obj, data, label)
163 | tStart = tic;
164 | results.type = obj.type;
165 | results.label = label;
166 | results.label_ = label;
167 | results.data = data;
168 | results.classNames = unique(label);
169 | if strcmp(obj.type, 'RVC') && strcmp(obj.evaluationMode,'test')
170 | results.label_ = RvmOption.checkLabel(results, label);
171 | end
172 | results.numSamples = size(results.data, 1);
173 |
174 | switch obj.kernelType
175 | case 'single'
176 | K = obj.kernelFunc.computeMatrix(results.data, obj.relevanceVectors);
177 | case 'hybrid'
178 | K = 0;
179 | for i = 1:obj.numKernel
180 | K = K+obj.kernelWeight(i)*...
181 | obj.kernelFunc(i).computeMatrix(results.data, obj.relevanceVectors);
182 | end
183 | end
184 |
185 | switch obj.freeBasis
186 | case 'off'
187 | BASIS = K;
188 | case 'on'
189 | BASIS = [K, ones(size(results.data, 1), 1)];
190 | end
191 |
192 | switch obj.type
193 | case 'RVC'
194 | results.predictedLabel_ = zeros(size(results.data, 1), 1);
195 | index_ = SB2_Sigmoid(BASIS*obj.weight)>0.5;
196 | results.predictedLabel_(index_) = 1;
197 | results.predictedLabel = RvmOption.restoreLabel...
198 | (results.classNames, results.predictedLabel_);
199 | case 'RVR'
200 | results.predictedLabel = BASIS*obj.weight;
201 | results.predictedVariance = obj.beta^-1+diag(BASIS*obj.sigma*BASIS');
202 | end
203 | results.performance = obj.evaluateModel(results);
204 | results.runningTime = toc(tStart);
205 |
206 | % display
207 | if strcmp(obj.display, 'on')
208 | RvmOption.displayTest(results);
209 | end
210 | end
211 |
212 | function performance = evaluateModel(obj, results)
213 | switch obj.type
214 | case 'RVC'
215 | performance.accuracy = sum(results.predictedLabel_ == results.label_)/results.numSamples;
216 | [performance.confusionMatrix, performance.classOrder] = confusionmat...
217 | (results.label, results.predictedLabel);
218 | case 'RVR'
219 | performance.RMSE= sqrt(sum((results.label-results.predictedLabel).^2)/results.numSamples);
220 | SSR = sum((results.predictedLabel-mean(results.predictedLabel)).^2);
221 | SSE = sum((results.label-results.predictedLabel).^2);
222 | SST = SSR+SSE;
223 | performance.R2 = 1-SSE./SST;
224 | performance.MAE = sum(abs(results.label-results.predictedLabel))/results.numSamples;
225 | end
226 | end
227 |
228 | function draw(obj, results)
229 | switch obj.type
230 | case 'RVR'
231 | RvmOption.drawRVR(results);
232 | case 'RVC'
233 | RvmOption.drawRVC(results);
234 | end
235 | end
236 |
237 | function numSamples = get.numSamples(obj)
238 | numSamples= size(obj.data, 1);
239 | end
240 |
241 | function numFeatures = get.numFeatures(obj)
242 | numFeatures= size(obj.data, 2);
243 | end
244 | end
245 | end
--------------------------------------------------------------------------------
/RvmModel/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
--------------------------------------------------------------------------------
/RvmModel/RvmOptimization.m:
--------------------------------------------------------------------------------
1 | classdef RvmOptimization < handle
2 | %{
3 | CLASS DESCRIPTION
4 |
5 | Parameter optimization for RVM model.
6 |
7 | --------------------------------------------------------------
8 | %}
9 |
10 | methods (Static)
11 | function getModel(rvm)
12 | RvmOptimizationOption.checkParameter(rvm);
13 | objFun = @(parameter) RvmOptimization.getObjValue(parameter, rvm);
14 | RvmOptimizationOption.constructParamTable(rvm);
15 | switch rvm.optimization.method
16 | case 'bayes'
17 | RvmOptimization.bayesopt(rvm, objFun);
18 | case 'ga'
19 | RvmOptimization.gaopt(rvm, objFun);
20 | case 'pso'
21 | RvmOptimization.psoopt(rvm, objFun);
22 | end
23 | RvmOptimizationOption.setParameter(rvm);
24 | rvm.getModel;
25 | end
26 |
27 | function bayesopt(rvm, objFun)
28 | %{
29 | Optimize the parameters using Bayesian optimization.
30 | For detailed introduction of the algorithm and parameter
31 | setting, please enter 'help bayesopt' in the command line.
32 | %}
33 | parameter = RvmOptimizationOption.setParameterForBayes(rvm);
34 | switch rvm.optimization.display
35 | case 'on'
36 | plotFcn_ = {@plotObjectiveModel, @plotMinObjective};
37 | case 'off'
38 | plotFcn_ = [];
39 | end
40 | results = bayesopt(objFun, parameter, 'Verbose', 1,...
41 | 'MaxObjectiveEvaluations', rvm.optimization.iteration,...
42 | 'NumSeedPoints', rvm.optimization.point, 'PlotFcn', plotFcn_);
43 | % optimization results
44 | [rvm.optimization.bestParam, ~, ~] = bestPoint(results);
45 | end
46 |
47 | function gaopt(rvm, objFun)
48 | %{
49 | Optimize the parameters using Genetic Algorithm (GA)
50 | For detailed introduction of the algorithm and parameter
51 | setting, please enter 'help ga' in the command line.
52 | %}
53 | seedSize = 10*rvm.optimization.numParameter;
54 | [lowerBound_, upperBound_] = RvmOptimizationOption.setParameterForPsoAndGa(rvm);
55 | try
56 | switch rvm.optimization.display
57 | case 'on'
58 | plotFcn_ = 'gaplotbestf';
59 | case 'off'
60 | plotFcn_ = [];
61 | end
62 | options = optimoptions('ga', 'PopulationSize', seedSize,...
63 | 'MaxGenerations', rvm.optimization.iteration,...
64 | 'Display', 'iter', 'PlotFcn', plotFcn_);
65 | bestParam_ = ga(objFun, rvm.optimization.numParameter, [], [], [], [],...
66 | lowerBound_, upperBound_, [], [], options);
67 | catch % older vision
68 | switch rvm.optimization.display
69 | case 'on'
70 | plotFcn_ = @gaplotbestf;
71 | case 'off'
72 | plotFcn_ = [];
73 | end
74 | options = optimoptions('ga', 'PopulationSize', seedSize,...
75 | 'MaxGenerations', rvm.optimization.iteration,...
76 | 'Display', 'iter', 'PlotFcn', plotFcn_);
77 | bestParam_ = ga(objFun, rvm.optimization.numParameter, [], [], [], [],...
78 | lowerBound_, upperBound_, [], [], options);
79 | end
80 | % optimization results
81 | rvm.optimization.bestParam.Variables = bestParam_;
82 | end
83 |
84 | function psoopt(rvm, objFun)
85 | %{
86 | Optimize the parameters using Particle Swarm Optimization (PSO)
87 | For detailed introduction of the algorithm and parameter
88 | setting, please enter 'help particleswarm' in the command line.
89 | %}
90 | seedSize = 10*rvm.optimization.numParameter;
91 | switch rvm.optimization.display
92 | case 'on'
93 | plotFcn_ = 'pswplotbestf';
94 | case 'off'
95 | plotFcn_ = [];
96 | end
97 | options = optimoptions('particleswarm', 'SwarmSize', seedSize,...
98 | 'MaxIterations', rvm.optimization.iteration,...
99 | 'Display', 'iter', 'PlotFcn', plotFcn_);
100 | [lowerBound_, upperBound_] = RvmOptimizationOption.setParameterForPsoAndGa(rvm);
101 | bestParam_ = particleswarm(objFun, rvm.optimization.numParameter,...
102 | lowerBound_, upperBound_, options);
103 | % optimization results
104 | rvm.optimization.bestParam.Variables = bestParam_;
105 | end
106 |
107 | function objValue = getObjValue(parameter, rvm)
108 | %{
109 | Compute the value of objective function
110 | %}
111 | rvm_ = copy(rvm);
112 | rvm_.display = 'off';
113 | switch class(parameter)
114 | case 'table' % bayes
115 | rvm_.optimization.bestParam = parameter;
116 | case 'double' % ga, pso
117 | rvm_.optimization.bestParam.Variables = parameter;
118 | end
119 | % parameter setting
120 | RvmOptimizationOption.setParameter(rvm_);
121 | % cross validation
122 | if strcmp(rvm_.crossValidation.switch, 'on')
123 | objValue = RvmOptimization.crossvalFunc(rvm_);
124 | else
125 | % train with all samples
126 | rvm_.getModel;
127 | evaluationMode_ = rvm_.evaluationMode;
128 | rvm_.evaluationMode = 'train';
129 | results_ = test(rvm_, rvm_.data, rvm_.label_);
130 | rvm_.performance = rvm_.evaluateModel(results_);
131 | switch rvm_.type
132 | case 'RVR'
133 | objValue = rvm_.performance.RMSE;
134 | case 'RVC'
135 | objValue = 1-rvm_.performance.accuracy;
136 | end
137 | rvm_.evaluationMode = evaluationMode_;
138 | end
139 | end
140 |
141 | function accuracy = crossvalFunc(rvm)
142 | %{
143 | Compute the cross validation accuracy
144 | %}
145 | rng('default')
146 | rvm_ = copy(rvm);
147 | data_ = rvm_.data;
148 | label_ = rvm_.label_;
149 | rvm_.display = 'off';
150 | rvm_.evaluationMode = 'train';
151 | cvIndices = crossvalind(rvm.crossValidation.method, ...
152 | rvm_.numSamples, rvm.crossValidation.param);
153 | switch rvm.crossValidation.method
154 | case 'KFold'
155 | accuracy_ = Inf(rvm.crossValidation.param, 1);
156 | for i = 1:rvm.crossValidation.param
157 | testIndices = (cvIndices == i);
158 | testData = data_(testIndices, :);
159 | testLabel = label_(testIndices, :);
160 | rvm_.data = data_(~testIndices, :);
161 | rvm_.label_ = label_(~testIndices, :);
162 | try
163 | rvm_.getModel;
164 | catch
165 | continue
166 | end
167 | results = rvm_.test(testData, testLabel);
168 | switch rvm_.type
169 | case 'RVR'
170 | accuracy_(i, 1) = results.performance.RMSE;
171 | case 'RVC'
172 | accuracy_(i, 1) = 1-results.performance.accuracy;
173 | end
174 |
175 | end
176 | accuracy_(accuracy_ == Inf) = [];
177 | accuracy = mean(accuracy_);
178 | case 'HoldOut'
179 | testIndices = (cvIndices == 0);
180 | testData = data_(testIndices, :);
181 | testLabel = label_(testIndices, :);
182 | rvm_.data = data_(~testIndices, :);
183 | rvm_.label_ = label_(~testIndices, :);
184 | rvm_.getModel;
185 | results = rvm_.test(testData, testLabel);
186 | switch rvm_.type
187 | case 'RVR'
188 | accuracy = results.performance.RMSE;
189 | case 'RVC'
190 | accuracy = 1-results.performance.accuracy;
191 | end
192 | end
193 | end
194 | end
195 | end
--------------------------------------------------------------------------------
/RvmModel/RvmOptimizationOption.m:
--------------------------------------------------------------------------------
1 | classdef RvmOptimizationOption < handle
2 | %{
3 | CLASS DESCRIPTION
4 |
5 | Parameter optimization option for RVM model.
6 |
7 | ---------------------------------------------------------------
8 |
9 | %}
10 |
11 | methods (Static)
12 | function checkParameter(rvm)
13 | backup_ = rvm.optimization;
14 | % gaussian
15 | gaussian.parameterName = {'gamma'};
16 | gaussian.parameterType = {'real'};
17 | gaussian.lowerBound = 2^-6;
18 | gaussian.upperBound = 2^6;
19 |
20 | % laplacian
21 | laplacian.parameterName = {'gamma'};
22 | laplacian.parameterType = {'real'};
23 | laplacian.lowerBound = 2^-6;
24 | laplacian.upperBound = 2^6;
25 |
26 | % polynomial
27 | polynomial.parameterName = {'gamma'; 'offset'; 'degree'};
28 | polynomial.parameterType = {'real'; 'real'; 'integer'};
29 | polynomial.lowerBound = [2^-6; 2^-6; 1];
30 | polynomial.upperBound = [2^6; 2^6; 7];
31 |
32 | % sigmoid
33 | sigmoid.parameterName = {'gamma'; 'offset'};
34 | sigmoid.parameterType = {'real'; 'real'};
35 | sigmoid.lowerBound = [2^-6; 2^-6];
36 | sigmoid.upperBound = [2^6; 2^6];
37 |
38 | % others
39 | rvm.optimization.method = 'bayes'; % bayes, ga, pso
40 | rvm.optimization.display = 'on';
41 | rvm.optimization.iteration = 30;
42 | rvm.optimization.point = 10;
43 |
44 | % parameter initialization
45 | name_ = fieldnames(backup_);
46 | count_ = 0;
47 | %
48 | for i = 1:rvm.numKernel
49 | if ~isfield(backup_, rvm.kernelFuncName{i})
50 | rvm.optimization.(rvm.kernelFuncName{i}) = eval(rvm.kernelFuncName{i});
51 | else
52 | tmp_ = rvm.optimization.(rvm.kernelFuncName{i});
53 | rvm.optimization.(name_{i}) = RvmOptimizationOption.setKernelFuncParam(rvm.kernelFuncName{i}, gaussian, tmp_);
54 | end
55 | count_ = count_+length(rvm.optimization.(rvm.kernelFuncName{i}).parameterName);
56 | end
57 |
58 | for i = 1:size(name_, 1)
59 | if isfield(rvm.optimization, name_{i})
60 | rvm.optimization.(name_{i}) = backup_.(name_{i});
61 | end
62 | end
63 |
64 | if strcmp(rvm.kernelType, 'hybrid')
65 | tmp_ = cell(rvm.numKernel, 1);
66 | tmp_(:) = {'real'};
67 | rvm.optimization.weight.parameterType = tmp_(:);
68 | rvm.optimization.weight.lowerBound = zeros(rvm.numKernel, 1);
69 | rvm.optimization.weight.upperBound = ones(rvm.numKernel, 1);
70 | count_ = count_+rvm.numKernel;
71 | end
72 |
73 | %
74 | rvm.optimization.numParameter = count_;
75 | if isfield(rvm.optimization, 'polynomial')
76 | if ~strcmp(rvm.optimization.method, 'bayes')
77 | errorText = strcat(...
78 | 'The parameter of the polynomial kernel funcion should be optimized by Bayesian optimization.\n',...
79 | 'For examples, the optimization method should be set as follows:\n',...
80 | '--------------------------------------------\n',...
81 | 'opt.method = ''bayes''\n');
82 | error(sprintf(errorText))
83 | end
84 | end
85 | end
86 |
87 | function constructParamTable(rvm)
88 | name_ = cell(1, rvm.optimization.numParameter);
89 | count_ = 0;
90 | for i = 1:rvm.numKernel
91 | for j = 1:length(rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterName)
92 | count_ = count_+1;
93 | name_{count_} = [rvm.kernelFuncName{i, 1},'_', rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterName{j}];
94 | end
95 | end
96 |
97 | if strcmp(rvm.kernelType, 'hybrid')
98 | for i = 1:rvm.numKernel
99 | count_ = count_+1;
100 | name_{count_} = [rvm.kernelFuncName{i, 1}, '_', 'weight'];
101 | end
102 | end
103 | rvm.optimization.bestParam = array2table(zeros(1, rvm.optimization.numParameter));
104 | rvm.optimization.bestParam.Properties.VariableNames = name_;
105 | end
106 |
107 | function setParameter(rvm)
108 | for i = 1:rvm.numKernel
109 | switch rvm.kernelFuncName{i}
110 | case 'gaussian'
111 | rvm.kernelFunc(i).gamma = rvm.optimization.bestParam.gaussian_gamma;
112 | case 'laplacian'
113 | rvm.kernelFunc(i).gamma = rvm.optimization.bestParam.laplacian_gamma;
114 | case 'polynomial'
115 | rvm.kernelFunc(i).gamma = rvm.optimization.bestParam.polynomial_gamma;
116 | rvm.kernelFunc(i).offset = rvm.optimization.bestParam.polynomial_offset;
117 | rvm.kernelFunc(i).degree = rvm.optimization.bestParam.polynomial_degree;
118 | case 'sigmoid'
119 | rvm.kernelFunc(i).gamma = rvm.optimization.bestParam.sigmoid_gamma;
120 | rvm.kernelFunc(i).offset = rvm.optimization.bestParam.sigmoid_offset;
121 | end
122 | end
123 |
124 | if strcmp(rvm.kernelType, 'hybrid')
125 | for i = 1:rvm.numKernel
126 | rvm.kernelWeight(i) = rvm.optimization.bestParam.([rvm.kernelFuncName{i}, '_weight']);
127 | end
128 | rvm.kernelWeight = rvm.kernelWeight/sum(rvm.kernelWeight);
129 | for i = 1:rvm.numKernel
130 | rvm.optimization.bestParam.([rvm.kernelFuncName{i}, '_weight']) = rvm.kernelWeight(i);
131 | end
132 | end
133 | end
134 |
135 | function parameter = setParameterForBayes(rvm)
136 | parameter = [];
137 | for i = 1:rvm.numKernel
138 | for j = 1: length(rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterName)
139 | name_ = [rvm.kernelFuncName{i, 1},'_', rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterName{j}];
140 | tmp_ = optimizableVariable(name_,...
141 | [rvm.optimization.(rvm.kernelFuncName{i, 1}).lowerBound(j)...
142 | rvm.optimization.(rvm.kernelFuncName{i, 1}).upperBound(j)],...
143 | 'Type', rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterType{j});
144 | parameter = [parameter; tmp_];
145 | end
146 | end
147 | if strcmp(rvm.kernelType, 'hybrid')
148 | for i = 1:rvm.numKernel
149 | name_ = [rvm.kernelFuncName{i, 1}, '_', 'weight'];
150 | tmp_ = optimizableVariable(name_,...
151 | [rvm.optimization.weight.lowerBound(i)...
152 | rvm.optimization.weight.upperBound(i)],...
153 | 'Type', rvm.optimization.weight.parameterType{i});
154 | parameter = [parameter; tmp_];
155 | end
156 | end
157 | end
158 |
159 | function [lowerBound_, upperBound_] = setParameterForPsoAndGa(rvm)
160 | lowerBound_ = [];
161 | for i = 1:rvm.numKernel
162 | for j = 1: length(rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterName)
163 | tmp_ = rvm.optimization.(rvm.kernelFuncName{i, 1}).lowerBound(j);
164 | lowerBound_ = [lowerBound_; tmp_];
165 | end
166 | end
167 | if strcmp(rvm.kernelType, 'hybrid')
168 | for i = 1:rvm.numKernel
169 | tmp_ = rvm.optimization.weight.lowerBound(i);
170 | lowerBound_ = [lowerBound_; tmp_];
171 | end
172 | end
173 |
174 | upperBound_ = [];
175 | for i = 1:rvm.numKernel
176 | for j = 1: length(rvm.optimization.(rvm.kernelFuncName{i, 1}).parameterName)
177 | tmp_ = rvm.optimization.(rvm.kernelFuncName{i, 1}).upperBound(j);
178 | upperBound_ = [upperBound_; tmp_];
179 | end
180 | end
181 | if strcmp(rvm.kernelType, 'hybrid')
182 | for i = 1:rvm.numKernel
183 | tmp_ = rvm.optimization.weight.upperBound(i);
184 | upperBound_ = [upperBound_; tmp_];
185 | end
186 | end
187 | end
188 |
189 | function defaultParam = setKernelFuncParam(paramType, defaultParam, inputParam)
190 | names = fieldnames(inputParam);
191 | lab = isfield(defaultParam, names);
192 | if ~all(lab(:) == 1)
193 | RvmOptimizationOption.showErrorText(paramType);
194 | end
195 | for i = 1:numel(names)
196 | defaultParam.(names{i}) = inputParam.(names{i});
197 | end
198 | end
199 |
200 | function showErrorText(paramType)
201 | switch paramType
202 | case 'gaussian'
203 | errorText = strcat(...
204 | 'Incorrected optimization parameters of the gaussian kernel function.\n',...
205 | 'For examples, the parameters should be set as follows:\n',...
206 | '--------------------------------------------\n',...
207 | 'opt.gaussian.parameterName = {''gamma''}\n',...
208 | 'opt.gaussian.parameterType = {''real''}\n',...
209 | 'opt.gaussian.lowerBound = 2^-6\n',...
210 | 'opt.gaussian.upperBound = 2^6\n');
211 |
212 | case 'laplacian'
213 | errorText = strcat(...
214 | 'Incorrected optimization parameters of the laplacian kernel function.\n',...
215 | 'For examples, the parameters should be set as follows:\n',...
216 | '--------------------------------------------\n',...
217 | 'opt.laplacian.parameterName = {''gamma''}\n',...
218 | 'opt.laplacian.parameterType = {''real''}\n',...
219 | 'opt.laplacian.lowerBound = 2^-6\n',...
220 | 'opt.laplacian.upperBound = 2^6\n');
221 |
222 | case 'polynomial'
223 | errorText = strcat(...
224 | 'Incorrected optimization parameters of the polynomial kernel function.\n',...
225 | 'For examples, the parameters should be set as follows:\n',...
226 | '--------------------------------------------\n',...
227 | 'opt.polynomial.parameterName = {''gamma''; ''offset''; ''degree''}\n',...
228 | 'opt.polynomial.parameterType = {''real''; ''real''; ''integer''}\n',...
229 | 'opt.polynomial.lowerBound = [2^-6; 2^-6; 1]\n',...
230 | 'opt.polynomial.upperBound = [2^6; 2^6; 7]\n');
231 |
232 | case 'sigmoid'
233 | errorText = strcat(...
234 | 'Incorrected optimization parameters of the sigmoid kernel function.\n',...
235 | 'For examples, the parameters should be set as follows:\n',...
236 | '--------------------------------------------\n',...
237 | 'opt.sigmoid.parameterName = {''gamma''; ''offset''}\n',...
238 | 'opt.sigmoid.parameterType = {''real''; ''real''}\n',...
239 | 'opt.sigmoid.lowerBound = [2^-6; 2^-6]\n',...
240 | 'opt.sigmoid.upperBound = [2^6; 2^6]\n');
241 | end
242 | error(sprintf(errorText))
243 | end
244 | end
245 | end
--------------------------------------------------------------------------------
/RvmModel/RvmOption.m:
--------------------------------------------------------------------------------
1 | classdef RvmOption < handle
2 | %{
3 | CLASS DESCRIPTION
4 |
5 | Option of RVM model
6 |
7 | -----------------------------------------------------------------
8 | %}
9 |
10 | methods(Static)
11 | function label_ = checkLabel(obj, label)
12 | label_ = zeros(length(label), 1);
13 | if length(obj.classNames) ~= 2
14 | error('RVM is only supported for binary classification.')
15 | end
16 | switch class(obj.classNames)
17 | case 'double'
18 | label_(label==obj.classNames(1)) = 0;
19 | label_(label==obj.classNames(2)) = 1;
20 | case 'cell'
21 | label_(strcmp(label, obj.classNames{1})) = 0;
22 | label_(strcmp(label, obj.classNames{2})) = 1;
23 | end
24 | end
25 |
26 | function label_ = restoreLabel(names, label)
27 | numLabel = length(label);
28 | switch class(names)
29 | case 'double'
30 | label_ = zeros(numLabel, 1);
31 | label_(label==0) = names(1);
32 | label_(label==1) = names(2);
33 | case 'cell'
34 | label_ = cell(numLabel, 1);
35 | for i = 1:numLabel
36 | if label(i) == 0
37 | label_{i} = names{1};
38 | else
39 | label_{i} = names{2};
40 | end
41 | end
42 | end
43 | end
44 |
45 | function drawRVR(results)
46 | figure
47 | set(gcf, 'position', [300 150 600 400])
48 | hold on
49 | grid on
50 | % boundary
51 | index = (1:size(results.label, 1))';
52 | area_color = [229, 235, 245]/255;
53 | area = [results.predictedLabel(:,1)+2*sqrt(results.predictedVariance(:, 1));...
54 | flip(results.predictedLabel(:, 1)-2*sqrt(results.predictedVariance(:, 1)), 1)];
55 | fill([index; flip(index, 1)], area, area_color, 'EdgeColor', area_color)
56 | plot(index, results.label,...
57 | '-', 'LineWidth', 2, 'color', [254, 67, 101]/255)
58 | plot(index, results.predictedLabel,...
59 | '-','LineWidth', 2,'color', [0, 114, 189]/255)
60 | legend('3�� boundary', 'Real value', 'Predicted value')
61 | xlabel('Observations');
62 | ylabel('Predictions');
63 | end
64 |
65 | function drawRVC(results)
66 | titleStr = ['Classification based on RVM (Accuracy: ',...
67 | sprintf('%.2f', 100*results.performance.accuracy), '%)'];
68 | figure
69 | set(gcf, 'position', [300 150 600 400])
70 | hold on
71 | grid on
72 | index = (1:length(results.label))';
73 | plot(index, results.label_,...
74 | 'LineWidth', 2, 'Color', [0, 114, 189]/255,...
75 | 'LineStyle', '-')
76 | plot(index, results.predictedLabel_,...
77 | 'LineWidth', 2, 'Color', [162, 20, 47]/255,...
78 | 'LineStyle', ':')
79 | legend('Real class', 'Predicted class')
80 | xlabel('Observations');
81 | ylabel('Predictions');
82 | ylim([-0.5, 1.5])
83 | yticks([0 1])
84 | yticklabels(results.classNames)
85 | title(titleStr)
86 |
87 | try
88 | figure
89 | set(gcf, 'position', [300 150 600 400])
90 | confusionchart(results.performance.confusionMatrix, results.performance.classOrder,...
91 | 'Title', titleStr,...
92 | 'RowSummary','row-normalized', ...
93 | 'ColumnSummary','column-normalized');
94 | catch
95 | fprintf('Confusion matrix chart is not effective with the versions lower than R2018b.')
96 | close
97 | end
98 |
99 | end
100 |
101 | function setParameter(obj, parameter)
102 | version_ = version('-release');
103 | year_ = str2double(version_(1:4));
104 | if year_ < 2016 || (year_ == 2016 && version_(5) == 'a')
105 | error('RVM V2.1 is not compatible with the versions lower than R2016b.')
106 | end
107 | %
108 | obj.crossValidation.switch = 'off';
109 | obj.optimization.switch = 'off';
110 | name_ = fieldnames(parameter);
111 | for i = 1:size(name_, 1)
112 | switch name_{i}
113 | case {'HoldOut', 'KFold'}
114 | obj.crossValidation.switch = 'on';
115 | obj.crossValidation.method = name_{i, 1};
116 | obj.crossValidation.param = parameter.(name_{i, 1});
117 | case {'optimization'}
118 | obj.(name_{i}) = parameter.(name_{i, 1});
119 | obj.(name_{i}).switch = 'on';
120 | otherwise
121 | obj.(name_{i, 1}) = parameter.(name_{i, 1});
122 | end
123 | end
124 | obj.numKernel = numel(obj.kernelFunc);
125 | for i = 1:obj.numKernel
126 | obj.kernelFuncName{i, 1} = obj.kernelFunc(i).type;
127 | end
128 | if obj.numKernel == 1
129 | obj.kernelType = 'single';
130 | obj.kernelFuncName{1} = obj.kernelFunc.type;
131 | else
132 | obj.kernelType = 'hybrid';
133 | for i = 1:obj.numKernel
134 | obj.kernelFuncName{i, 1} = obj.kernelFunc(i).type;
135 | end
136 | end
137 | if isempty(obj.kernelWeight)
138 | obj.kernelWeight = 1/obj.numKernel*ones(obj.numKernel, 1);
139 | end
140 | end
141 |
142 | function displayTrain(obj)
143 | fprintf('\n')
144 | switch obj.type
145 | case 'RVR'
146 | fprintf('*** RVM model (regression) train finished ***\n')
147 | case 'RVC'
148 | fprintf('*** RVM model (classification) train finished ***\n')
149 | end
150 | fprintf('running time = %.4f seconds\n', obj.runningTime)
151 | fprintf('iterations = %d \n', obj.numIterations)
152 | fprintf('number of samples = %d \n', obj.numSamples)
153 | fprintf('number of RVs = %d \n', obj.numRelevanceVectors)
154 | fprintf('ratio of RVs = %.4f%% \n', 100*obj.numRelevanceVectors/obj.numSamples)
155 | switch obj.type
156 | case 'RVR'
157 | fprintf('RMSE = %.4f\n', obj.performance.RMSE)
158 | fprintf('R2 = %.4f\n', obj.performance.R2)
159 | fprintf('MAE = %.4f\n', obj.performance.MAE)
160 | case 'RVC'
161 | fprintf('accuracy = %.4f%%\n', 100*obj.performance.accuracy)
162 | end
163 | if strcmp(obj.optimization.switch, 'on')
164 | fprintf('Optimized parameter')
165 | display(obj.optimization.bestParam)
166 | fprintf('\n')
167 | end
168 | if obj.numRelevanceVectors/obj.numSamples > 0.5
169 | warning('The trained RVM model may be overfitting.')
170 | end
171 | fprintf('\n')
172 | end
173 |
174 | function displayTest(obj)
175 | fprintf('\n')
176 | switch obj.type
177 | case 'RVR'
178 | fprintf('*** RVM model (regression) test finished ***\n')
179 | case 'RVC'
180 | fprintf('*** RVM model (classification) test finished ***\n')
181 | end
182 | fprintf('running time = %.4f seconds\n', obj.runningTime)
183 | fprintf('number of samples = %d \n', obj.numSamples)
184 | switch obj.type
185 | case 'RVR'
186 | fprintf('RMSE = %.4f\n', obj.performance.RMSE)
187 | fprintf('R2 = %.4f\n', obj.performance.R2)
188 | fprintf('MAE = %.4f\n', obj.performance.MAE)
189 | case 'RVC'
190 | fprintf('accuracy = %.4f%%\n', 100*obj.performance.accuracy)
191 | end
192 | fprintf('\n')
193 | end
194 | end
195 | end
--------------------------------------------------------------------------------
/SB2_Release_200/Readme.txt:
--------------------------------------------------------------------------------
1 | SPARSEBAYES Matlab Toolbox
2 |
3 | Version 2.00
4 |
5 | -- INTRODUCTION --
6 |
7 | "SparseBayes" is a package of Matlab functions designed to implement
8 | an efficient learning algorithm for "Sparse Bayesian" models.
9 |
10 | The "Version 2" package is an expanded implementation of the algorithm
11 | detailed in:
12 |
13 | Tipping, M. E. and A. C. Faul (2003). "Fast marginal likelihood
14 | maximisation for sparse Bayesian models." In C. M. Bishop and
15 | B. J. Frey (Eds.), Proceedings of the Ninth International Workshop
16 | on Artificial Intelligence and Statistics, Key West, FL, Jan 3-6.
17 |
18 | This paper, the accompanying code, and further information regarding
19 | Sparse Bayesian models and the related "relevance vector machine" may
20 | be obtained from:
21 |
22 | http://www.relevancevector.com
23 |
24 |
25 | -- CODE STATUS --
26 |
27 | March 2009: The code is currently at Version 2.0, and has seen limited
28 | testing under Matlab Version 7.4.
29 |
30 |
31 | -- GETTING STARTED --
32 |
33 | The SparseBayes distribution comes with a basic user manual: see
34 | SB2_Manual.pdf.
35 |
36 | In summary, there are a number of files supplied in the SB2
37 | distribution, but the user should only need to call a subset of them.
38 |
39 | To set up options and parameters:
40 |
41 | SB2_UserOptions.m
42 | SB2_ParameterSettings.m
43 |
44 | The main algorithm
45 |
46 | SparseBayes.m
47 |
48 | Type "help SparseBayes" etc. at the Matlab prompt for further details
49 | on how to use these functions.
50 |
51 | There is a also a simple demonstration program, showing how
52 | SparseBayes may be used, in:
53 |
54 | SparseBayesDemo.m
55 |
56 |
57 | -- LICENCE --
58 |
59 | Note that the "SparseBayes" software is supplied subject to version 2
60 | of the "GNU General Public License" (detailed in the file "licence.txt").
61 |
62 | SPARSEBAYES is free software; you can redistribute it and/or modify it
63 | under the terms of the GNU General Public License as published by the
64 | Free Software Foundation; either version 2 of the License, or (at your
65 | option) any later version.
66 |
67 | SPARSEBAYES is distributed in the hope that it will be useful, but
68 | WITHOUT ANY WARRANTY; without even the implied warranty of
69 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
70 | General Public License for more details.
71 |
72 | You should have received a copy of the GNU General Public License
73 | along with SPARSEBAYES in the accompanying file "licence.txt"; if not,
74 | write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
75 | Floor, Boston, MA 02110-1301 USA
76 |
77 |
78 | -- ACKNOWLEDGEMENTS --
79 |
80 | The author would like to thank Mark Hatton, Anita Faul, Ian Nabney,
81 | Arnulf Graf and Gavin Cawley for their assistance in producing this
82 | code.
83 |
84 |
85 | --
86 |
87 | Mike Tipping
88 | www.relevancevector.com
89 | m a i l [at] m i k e t i p p i n g . c o m
90 |
91 | This README file (Readme.txt) was created on 13-Mar-2009 at 8:58 AM.
92 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_ControlSettings.m:
--------------------------------------------------------------------------------
1 | % SB2_CONTROLSETTINGS Set parameters to control the SPARSEBAYES algorithm
2 | %
3 | % CONTROLS = SB2_CONTROLSETTINGS
4 | %
5 | % OUTPUT ARGUMENTS:
6 | %
7 | % CONTROLS A structure whose fields control various aspects of the
8 | % running of the SPARSEBAYES algorithm.
9 | %
10 | % .ZeroFactor Small number equivalent to zero for Q^2-S
11 | % .MinDeltaLogAlpha Termination criterion for changes in log-alpha
12 | % .MinDeltaLogBeta Termination criterion for changes in log-beta
13 | %
14 | % .PriorityAddition Prefer "addition" operations
15 | % .PriorityDeletion Prefer "deletion" operations
16 | %
17 | % .BetaUpdateStart How many "fast start" beta updates
18 | % .BetaUpdateFrequency
19 | % How regularly to update beta after the above
20 | % .BetaMaxFactor Minimum value control for noise estimate
21 | %
22 | % .PosteriorModeFrequency
23 | % How regularly to re-find the posterior mode
24 | %
25 | % .BasisAlignmentTest Test for redundant basis vectors?
26 | % .AlignmentMax Basis redundancy criterion
27 | %
28 | % NOTES:
29 | %
30 | % The various definitions in the file are effectively "fixed" and not
31 | % modified elsewhere.
32 | %
33 | % The interested user may wish to experiment with the operation of the
34 | % SPARSEBAYES algorithm by modifying the values the file directly. See the
35 | % inline comments for hints on the various control settings.
36 | %
37 |
38 | %
39 | % Copyright 2009, Vector Anomaly Ltd
40 | %
41 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
42 | %
43 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
44 | % under the terms of the GNU General Public License as published by the Free
45 | % Software Foundation; either version 2 of the License, or (at your option)
46 | % any later version.
47 | %
48 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
49 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
50 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
51 | % more details.
52 | %
53 | % You should have received a copy of the GNU General Public License along
54 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
55 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
56 | % MA 02110-1301 USA
57 | %
58 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
59 | %
60 | function CONTROLS = SB2_ControlSettings
61 |
62 | %% Define parameters which influence the underlying operation of the
63 | %% SparseBayes inference algorithm
64 |
65 | % TOLERANCES
66 | %
67 | % Any Q^2-S "relevance factor" less than this is considered to be zero
68 | %
69 | CONTROLS.ZeroFactor = 1e-12;
70 | %
71 | % If the change in log-alpha for the best re-estimation is less than this,
72 | % we consider termination
73 | %
74 | CONTROLS.MinDeltaLogAlpha = 1e-3;
75 | %
76 | % In the Gaussian case, we also require a beta update to change the value
77 | % of log-beta (inverse noise variance) less than this to terminate
78 | %
79 | CONTROLS.MinDeltaLogBeta = 1e-6;
80 |
81 | % ADD/DELETE
82 | %
83 | % - preferring addition where possible will probably make the algorithm a
84 | % little slower and perhaps less "greedy"
85 | %
86 | % - preferring deletion may make the model a little more sparse and the
87 | % algorithm may run slightly quicker
88 | %
89 | % Note: both these can be set to 'true' at the same time, in which case
90 | % both take equal priority over re-estimation.
91 | %
92 | CONTROLS.PriorityAddition = false;
93 | CONTROLS.PriorityDeletion = true;
94 |
95 | % (GAUSSIAN) NOISE
96 | %
97 | % When to update the noise estimate
98 | %
99 | % The number of iterations from the start for which we update it every
100 | % iteration (to get in the right ball-park to begin with)
101 | %
102 | CONTROLS.BetaUpdateStart = 10;
103 | %
104 | % After the above, we only regularly update it after
105 | % a given number of iterations
106 | %
107 | CONTROLS.BetaUpdateFrequency = 5;
108 | %
109 | % Prevent zero-noise estimate (perfect fit) problem
110 | % - effectively says the noise variance estimate is clamped to be no
111 | % lower than variance-of-targets / BetaMaxFactor.
112 | %
113 | CONTROLS.BetaMaxFactor = 1e6;
114 |
115 | % POSTERIORMODE
116 | %
117 | % How many alpha updates to do in between each full posterior mode
118 | % computation in the non-Gaussian case
119 | %
120 | % In principle, this should be set to one (to update the posterior every
121 | % iteration) but it may be more efficient to do several alpha updates before
122 | % re-finding the posterior mode.
123 | %
124 | CONTROLS.PosteriorModeFrequency = 1;
125 |
126 | % REDUNDANT BASIS
127 | %
128 | % Check for basis vector alignment/correlation redundancy
129 | %
130 | CONTROLS.BasisAlignmentTest = true;
131 | %
132 | ALIGNMENT_ZERO = 1e-3;
133 | %
134 | % If BasisAlignmentTest is true, any basis vector with inner product more
135 | % than MAX_ALIGNMENT with any existing model vector will not be added
136 | %
137 | CONTROLS.AlignmentMax = 1 - ALIGNMENT_ZERO;
138 |
139 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_Diagnostic.m:
--------------------------------------------------------------------------------
1 | % SB2_DIAGNOSTIC Helper function to output diagnostics in SPARSEBAYES
2 | %
3 | % USAGE (1):
4 | %
5 | % OPTIONS = SB2_DIAGNOSTIC(OPTIONS, ACTION)
6 | %
7 | % OUTPUT ARGUMENTS:
8 | %
9 | % OPTIONS Returned OPTIONS settings, possibly updated
10 | % (e.g. with file handle after 'open' action)
11 | %
12 | % INPUT ARGUMENTS:
13 | %
14 | % OPTIONS Options settings (SB2_USEROPTIONS)
15 | %
16 | % ACTION One of:
17 | % 'OPEN' or 'START' Begin diagnostics
18 | % 'CLOSE', 'END' or 'FINISH' End diagnostics
19 | %
20 | %
21 | % USAGE (2):
22 | %
23 | % SB2_DIAGNOSTIC(OPTIONS, LEVEL, MESSAGE, VARARGIN ...)
24 | %
25 | % OUTPUT ARGUMENTS: None
26 | %
27 | % INPUT ARGUMENTS:
28 | %
29 | % OPTIONS Options settings (SB2_USEROPTIONS)
30 | %
31 | % LEVEL Importance level of message (0 to 4)
32 | % NB: LOWER is MORE IMPORTANT
33 | %
34 | % MESSAGE Message string
35 | %
36 | % VARARGIN Optional arguments to fill in string placeholders
37 | %
38 | % EXAMPLE:
39 | %
40 | % OPTIONS = SB2_Diagnostic(OPTIONS, 'start');
41 | % SB2_Diagnostic(OPTIONS,2,'Total of %d likelihood updates\n', 25);
42 | % OPTIONS = SB2_Diagnostic(OPTIONS, 'end');
43 | %
44 | % NOTES:
45 | %
46 | % This function offers a convenient way to output diagnostic information,
47 | % either to screen or to a file.
48 | %
49 | % It will "filter" the incoming messages and only display/write them if
50 | % their LEVEL is less than or equal to the DIAGNOSTICLEVEL as set by
51 | % SB2_USEROPTIONS.
52 | %
53 | % e.g. after
54 | %
55 | % OPTIONS = SB2_UserOptions('diagnosticLevel',2)
56 | %
57 | % This message won't display:
58 | %
59 | % SB2_Diagnostic(OPTIONS, 3, 'Pointless message')
60 | %
61 | % But this will:
62 | %
63 | % SB2_Diagnostic(OPTIONS, 1, 'Really important message')
64 | %
65 | % Messages of level 0 are therefore most "important", and those of level
66 | % 4 least important. Level-0 messages are always displayed, since the
67 | % DIAGNOSTICLEVEL will always be at least zero.
68 | %
69 |
70 | %
71 | % Copyright 2009, Vector Anomaly Ltd
72 | %
73 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
74 | %
75 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
76 | % under the terms of the GNU General Public License as published by the Free
77 | % Software Foundation; either version 2 of the License, or (at your option)
78 | % any later version.
79 | %
80 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
81 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
82 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
83 | % more details.
84 | %
85 | % You should have received a copy of the GNU General Public License along
86 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
87 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
88 | % MA 02110-1301 USA
89 | %
90 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
91 | %
92 | function OPTIONS = SB2_Diagnostic(OPTIONS, level, message_, varargin)
93 |
94 | %
95 | % Check if "level" is actually a string (this is a control call)
96 | %
97 | if ischar(level)
98 | switch upper(level)
99 | case {'OPEN','START'},
100 | if OPTIONS.diagnosticFID~=1
101 | %
102 | % If diagnosticFID is not 1, we must have previously set a
103 | % diagnostics file via SB2_USEROPTIONS
104 | %
105 | OPTIONS.diagnosticFID = fopen(OPTIONS.diagnosticFile_,'w');
106 | % Check for file open error
107 | if OPTIONS.diagnosticFID==-1
108 | error('Could not open diagnostic file ''%s''', value);
109 | end
110 | end
111 | %
112 | case {'CLOSE','END','FINISH'},
113 | if OPTIONS.diagnosticFID~=1
114 | % diagnosticFID refers to a file opened above
115 | fclose(OPTIONS.diagnosticFID);
116 | end
117 | end
118 | else
119 | %
120 | % Its a message call.
121 | %
122 | % Only output diagnostic messages if the "diagnostic level",
123 | % set via SB2_USEROPTIONS, is equal to or greater than the level of
124 | % the message to be displayed.
125 | %
126 | if level<=OPTIONS.diagnosticLevel
127 | fprintf(OPTIONS.diagnosticFID, repmat(' ', [1 level]));
128 | fprintf(OPTIONS.diagnosticFID, message_, varargin{:});
129 | end
130 | end
131 |
132 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_FormatTime.m:
--------------------------------------------------------------------------------
1 | % SB2_FORMATTIME Pretty output of diagnostic SPARSEBAYES time information
2 | %
3 | % STRING = SB2_FORMATTIME(ELAPSEDTIMEINSECONDS)
4 | %
5 |
6 | %
7 | % Copyright 2009, Vector Anomaly Ltd
8 | %
9 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
10 | %
11 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
12 | % under the terms of the GNU General Public License as published by the Free
13 | % Software Foundation; either version 2 of the License, or (at your option)
14 | % any later version.
15 | %
16 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
17 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 | % more details.
20 | %
21 | % You should have received a copy of the GNU General Public License along
22 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
23 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
24 | % MA 02110-1301 USA
25 | %
26 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
27 | %
28 | function timeString_ = SB2_FormatTime(elapsedTime)
29 |
30 | if elapsedTime>=3600
31 | % More than an hour...
32 | %
33 | h = floor(elapsedTime/3600);
34 | m = floor(rem(elapsedTime,3600)/60);
35 | s = floor(rem(elapsedTime,60));
36 | %
37 | timeString_ = sprintf('%dh %dm %ds', h,m,s);
38 | elseif elapsedTime>=60
39 | % More than one minute (but less than an hour)
40 | m = floor(elapsedTime/60);
41 | s = floor(rem(elapsedTime,60));
42 | %
43 | timeString_ = sprintf('%dm %ds', m,s);
44 | else
45 | % Under a minute
46 | s = elapsedTime;
47 | %
48 | timeString_ = sprintf('%2.2f secs', s);
49 | end
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_FullStatistics.m:
--------------------------------------------------------------------------------
1 | % SB2_FULLSTATISTICS Compute all relevant statistics in full for SPARSEBAYES
2 | %
3 | % [SIGMA,MU,S_IN,Q_IN,S_OUT,Q_OUT,FACTOR,LOGML,GAMMA,...
4 | % BETABASIS_PHI,BETA] = ...
5 | % SB2_FULLSTATISTICS(LIKELIHOOD,BASIS,PHI,TARGETS,USED,ALPHA,BETA,...
6 | % MU,BASIS_PHI,BASIS_TARGETS,OPTIONS)
7 | %
8 | % OUTPUT ARGUMENTS:
9 | %
10 | % SIGMA Posterior covariance matrix for relevant bases
11 | % MU Posterior mean
12 | % S_IN S-factors for in-model (relevant) basis vectors
13 | % Q_IN Q-factors for in-model (relevant) basis vectors
14 | % S_OUT S-factors for all basis vectors
15 | % Q_OUT Q-factors for all basis vectors
16 | % FACTOR Q^2-S relevance factors
17 | % LOGML Log-marginal-likelihood
18 | % GAMMA "Well-determinedness" factors
19 | % BETABASIS_PHI Cached value of BASIS'*B*PHI matrix
20 | % BETA Inverse noise variance (vector of beta
21 | % approximations in non-Gaussian case)
22 | %
23 | % INPUT ARGUMENTS:
24 | %
25 | % LIKELIHOOD LIKELIHOOD structure
26 | % BASIS Full NxM basis matrix
27 | % PHI Current relevant basis matrix
28 | % TARGETS N-vector with target output values
29 | % USED Relevant basis vector indices
30 | % ALPHA Hyperparameters
31 | % BETA Inverse noise variance (ignored in non-Gauss case)
32 | % MU Current posterior mean (for non-Gauss)
33 | % BASIS_PHI Cached value of BASIS'*PHI (for Gauss only)
34 | % BASIS_TARGETS Cached value of BASIS'*TARGETS (for Gauss only)
35 | % OPTIONS Standard OPTIONS structure (see SB2_USEROPTIONS)
36 | %
37 | % NOTES:
38 | %
39 | % This function computes the posterior, and other, statistics for the
40 | % SPARSEBAYES algorithm in "long-hand" fashion. i.e. it does not use the
41 | % more effecient "iterative" updates.
42 | %
43 | % It is required on every iteration (unless approximating) in the
44 | % non-Gaussian case, and on iterations in the Gaussian case where the noise
45 | % estimate (BETA) is updated.
46 | %
47 | % This function is intended for internal use by SPARSEBAYES only.
48 | %
49 |
50 |
51 | %
52 | % Copyright 2009, Vector Anomaly Ltd
53 | %
54 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
55 | %
56 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
57 | % under the terms of the GNU General Public License as published by the Free
58 | % Software Foundation; either version 2 of the License, or (at your option)
59 | % any later version.
60 | %
61 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
62 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
63 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
64 | % more details.
65 | %
66 | % You should have received a copy of the GNU General Public License along
67 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
68 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
69 | % MA 02110-1301 USA
70 | %
71 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
72 | %
73 | function [SIGMA,Mu,S_in,Q_in,S_out,Q_out,Factor,logML,Gamma,...
74 | betaBASIS_PHI,beta] = ...
75 | SB2_FullStatistics(LIKELIHOOD,BASIS,PHI,Targets,Used,Alpha,beta,...
76 | Mu,BASIS_PHI,BASIS_Targets,OPTIONS)
77 |
78 | % Mu is only required for non-Gauss
79 | % BASIS_PHI and BASIS_Targets are only required for Gauss
80 |
81 | % beta (a vector for non-Gauss) is returned only for non-Gauss
82 |
83 | MAX_POSTMODE_ITS = 25; % More than enough
84 |
85 | [N M_full] = size(BASIS);
86 | M = size(PHI,2);
87 |
88 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 |
90 | % COMPUTE POSTERIOR
91 |
92 | %
93 | % Compute full relevant statistics
94 | %
95 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
96 | %
97 | % Posterior is analytic: use linear algebra here
98 | %
99 | % Compute posterior covariance SIGMA (inverse Hessian)
100 | % via Cholesky decomposition
101 | %
102 | U = chol(PHI'*PHI*beta + diag(Alpha));
103 | Ui = inv(U);
104 | SIGMA = Ui * Ui';
105 | % Posterior mean Mu
106 | Mu = (SIGMA * (PHI'*Targets)) * beta;
107 | % Data error and likelihood
108 | y = PHI * Mu;
109 | e = (Targets - y);
110 | ED = e'*e;
111 | %
112 | dataLikely = (N*log(beta) - beta*ED)/2;
113 | %
114 | else
115 | %
116 | % Posterior must be approximated: find the posterior mode as the basis
117 | % for the Laplace approximation
118 | %
119 | [Mu U beta dataLikely] = ...
120 | SB2_PosteriorMode(LIKELIHOOD,PHI,Targets,Alpha,Mu, ...
121 | MAX_POSTMODE_ITS,OPTIONS);
122 | % Compute covariance approximation
123 | %
124 | Ui = inv(U);
125 | SIGMA = Ui * Ui';
126 | % Compute posterior-mean-based outputs and errors
127 | %
128 | switch LIKELIHOOD.InUse
129 | case LIKELIHOOD.Bernoulli,
130 | y = SB2_Sigmoid(PHI * Mu);
131 | case LIKELIHOOD.Poisson
132 | y = exp(PHI*Mu);
133 | end
134 | e = (Targets-y);
135 | end
136 |
137 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138 |
139 | % COMPUTE LOG MARGINAL LIKELIHOOD
140 |
141 | logdetHOver2 = sum(log(diag(U)));
142 | logML = dataLikely - (Mu.^2)'*Alpha/2 + ...
143 | sum(log(Alpha))/2 - logdetHOver2;
144 | % Well-determinedness factors
145 | DiagC = sum(Ui.^2,2);
146 | Gamma = 1 - Alpha.*DiagC;
147 |
148 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 |
150 | % COMPUTE THE Q & S VALUES
151 |
152 | %
153 | % Q: "quality" factor - related to how well the basis function contributes
154 | % to reducing the error
155 | %
156 | % S: "sparsity factor" - related to how orthogonal a given basis function
157 | % is to the currently used set of basis functions
158 | %
159 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
160 | %
161 | % Gaussian case simple: beta a scalar
162 | %
163 | betaBASIS_PHI = beta*BASIS_PHI;
164 | %
165 | % The S_in calculation exploits the fact that BASIS is normalised,
166 | % i.e. sum(BASIS.^2,1)==ones(1,M_full)
167 | %
168 | S_in = beta - sum((betaBASIS_PHI*Ui).^2,2);
169 | Q_in = beta*(BASIS_Targets - BASIS_PHI*Mu);
170 | else
171 | %
172 | % Non-Gaussian case: beta an N-vector
173 | %
174 | betaBASIS_PHI = BASIS'*(PHI.* (beta*ones(1,M)));
175 | S_in = (beta'*(BASIS.^2))' - sum((betaBASIS_PHI*Ui).^2,2);
176 | Q_in = BASIS'*e;
177 | end
178 | %
179 | S_out = S_in;
180 | Q_out = Q_in;
181 | %
182 | % S,Q with that basis excluded: equations (23)
183 | %
184 | S_out(Used) = (Alpha .* S_in(Used)) ./ (Alpha - S_in(Used));
185 | Q_out(Used) = (Alpha .* Q_in(Used)) ./ (Alpha - S_in(Used));
186 | %
187 | % Pre-compute the "relevance factor" for ongoing convenience
188 | %
189 | Factor = (Q_out.*Q_out - S_out);
190 |
191 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_Initialisation.m:
--------------------------------------------------------------------------------
1 | % SB2_INITIALISATION Initialise everything for the SPARSEBAYES algorithm
2 | %
3 | % [LIKELIHOOD, BASIS, BASISSCALES, ALPHA, BETA, MU, PHI, USED] = ...
4 | % SB2_INITIALISATION(TYPE, BASIS, TARGETS, SETTINGS, OPTIONS)
5 | %
6 | % OUTPUT ARGUMENTS:
7 | %
8 | % LIKELIHOOD Likelihood structure (from SB2_LIKELIHOODS)
9 | % BASIS Pre-processed full basis matrix
10 | % BASISSCALES Scaling factors from full basis pre-processing
11 | % ALPHA Initial hyperparameter alpha values
12 | % BETA Initial noise level (Gaussian)
13 | % MU Initial weight values
14 | % PHI Initial "relevant" basis
15 | % USED Indices of initial basis vectors (columns of BASIS)
16 | %
17 | % INPUT ARGUMENTS:
18 | %
19 | % TYPE Likelihood: one of 'Gaussian', 'Bernoulli', 'Poisson'
20 | %
21 | % BASIS NxM matrix of all possible basis vectors
22 | % (one column per basis function)
23 | %
24 | % TARGETS N-vector with target output values
25 | %
26 | % SETTINGS Initialisation structure for main parameter values via
27 | % SB2_PARAMETERSETTINGS
28 | %
29 | % OPTIONS User options structure from SB2_USEROPTIONS
30 | %
31 | % NOTES:
32 | %
33 | % This function initialises all necessary model parameters appropriately
34 | % before entering the main loop of the SPARSEBAYES inference algorithm.
35 | %
36 | % This function is intended for internal use by SPARSEBAYES only.
37 | %
38 |
39 | %
40 | % Copyright 2009, Vector Anomaly Ltd
41 | %
42 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
43 | %
44 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
45 | % under the terms of the GNU General Public License as published by the Free
46 | % Software Foundation; either version 2 of the License, or (at your option)
47 | % any later version.
48 | %
49 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
50 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
51 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
52 | % more details.
53 | %
54 | % You should have received a copy of the GNU General Public License along
55 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
56 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
57 | % MA 02110-1301 USA
58 | %
59 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
60 | %
61 | function [LIKELIHOOD, BASIS, BasisScales, Alpha, beta, Mu, PHI, Used] = ...
62 | SB2_Initialisation(likelihood_, BASIS, Targets, SETTINGS, OPTIONS)
63 |
64 | % A "reasonable" initial value for the noise in the Gaussian case
65 | GAUSSIAN_SNR_INIT = 0.1;
66 |
67 | % "Reasonable" initial alpha bounds
68 | INIT_ALPHA_MAX = 1e3;
69 | INIT_ALPHA_MIN = 1e-3;
70 |
71 | %
72 | % BASIS PREPROCESSING:
73 | %
74 | % Scale basis vectors to unit norm. This eases some calculations and
75 | % will improve numerical robustness later.
76 | %
77 | [BASIS, BasisScales] = SB2_PreProcessBasis(BASIS);
78 |
79 | %% Noise model considerations
80 |
81 | % Validate the likelihood model specification
82 | %
83 | LIKELIHOOD = SB2_Likelihoods(likelihood_);
84 |
85 | % Default beta value for non-Gaussian likelihood
86 | beta = [];
87 | %
88 | % In the Gaussian case, initialise "sensibly" to a proportion of the
89 | % signal level (e.g. 10 percent)
90 | %
91 | if LIKELIHOOD.InUse == LIKELIHOOD.Gaussian
92 | % Noise initialisation
93 | if ~isempty(SETTINGS.beta)
94 | % The user set "beta"
95 | beta = SETTINGS.beta;
96 | elseif ~isempty(SETTINGS.noiseStdDev) && SETTINGS.noiseStdDev>0
97 | % The user set the "noise std dev"
98 | beta = 1/SETTINGS.noiseStdDev^2;
99 | else
100 | % catch the pathological case where all outputs are zero
101 | % (although we're probably doomed anyway if that's true)
102 | stdt = max([1e-6 std(Targets)]);
103 | % Initialise automatically approximately according to "SNR"
104 | beta = 1/(stdt*GAUSSIAN_SNR_INIT)^2;
105 | end
106 | end
107 |
108 |
109 | % Initialise basis (PHI), mu and alpha
110 | %
111 | % Either as specified by the SETTINGS structure, or heuristically
112 | %
113 |
114 | % First, compute 'linearised' output for use in heuristic initialisation
115 | %
116 | TargetsPseudoLinear = Targets; % standard linear case
117 | switch LIKELIHOOD.InUse
118 | case LIKELIHOOD.Bernoulli,
119 | TargetsPseudoLinear = (2*Targets-1);
120 | case LIKELIHOOD.Poisson,
121 | TargetsPseudoLinear = log(Targets+1e-3);
122 | end
123 |
124 |
125 | % 1) the starting basis, PHI
126 |
127 | % Take account of "free basis": it needs to be included from the outset
128 | %
129 | Extra = setdiff(OPTIONS.freeBasis, SETTINGS.Relevant);
130 | %
131 | % [TODO:] More elegant to put "Extra" first in Used list?
132 | %
133 | Used = [SETTINGS.Relevant Extra];
134 | %
135 | % At this point Used will contain both pre-specified relevant basis
136 | % functions (SB2_PARAMETERSETTINGS) and any "free" ones (SB2_USEROPTIONS).
137 | % If neither are active, Used will be empty.
138 | %
139 | if isempty(Used)
140 | % Set initial basis to be the largest projection with the targets
141 | proj = (BASIS'*TargetsPseudoLinear);
142 | [foo Used] = max(abs(proj));
143 | SB2_Diagnostic(OPTIONS, 2, ...
144 | ['Initialising with maximally aligned ' ...
145 | 'basis vector (%d)\n'], Used);
146 | else
147 | SB2_Diagnostic(OPTIONS, 2, ...
148 | 'Initialising with supplied basis of size M = %d\n', ...
149 | length(Used));
150 | end
151 | PHI = BASIS(:,Used);
152 | M = length(Used);
153 |
154 | % 2) the most probable weights
155 |
156 | if isempty(SETTINGS.Mu)
157 | switch LIKELIHOOD.InUse
158 | %
159 | case LIKELIHOOD.Gaussian,
160 | % Mu will be analytically calculated later in the Gaussian case
161 | %
162 | Mu = [];
163 | case LIKELIHOOD.Bernoulli,
164 | % Heuristic initialisation based on log-odds
165 | LogOut = (TargetsPseudoLinear*0.9+1)/2;
166 | Mu = PHI \ (log(LogOut./(1-LogOut)));
167 | %
168 | case LIKELIHOOD.Poisson
169 | % Heuristic initialisation based on log
170 | Mu = PHI \ TargetsPseudoLinear;
171 | %
172 | end
173 | else
174 | if length(SETTINGS.Mu)~=length(SETTINGS.Relevant)
175 | error('Basis length (%d) should equal weight vector length (%d)', ...
176 | length(SETTINGS.Relevant), length(SETTINGS.Mu))
177 | end
178 | SB2_Diagnostic(OPTIONS, 2,'Initialising with supplied weights\n');
179 | % We may have extra "freebasis" functions without supplied weights
180 | % - set those to zero for now
181 | Mu = [Mu ; zeros(length(Extra),1)];
182 | end
183 |
184 | % 3) the hyperparameters
185 |
186 | if isempty(SETTINGS.Alpha)
187 | switch LIKELIHOOD.InUse
188 | %
189 | case LIKELIHOOD.Gaussian,
190 | % Exact for single basis function case (diag irrelevant),
191 | % heuristic in the multiple case
192 | p = diag(PHI'*PHI)*beta;
193 | q = (PHI'*Targets)*beta;
194 | Alpha = p.^2./(q.^2-p); %
195 | % Its possible that one of the basis functions could already be
196 | % irrelevant (alpha<0), so trap that
197 | if all(Alpha<0)
198 | SB2_Diagnostic(OPTIONS, 1,...
199 | ['WARNING: no relevant basis function at' ...
200 | ' initialisation\n']);
201 | end
202 | % The main algorithm will handle these automatically shortly
203 | % (i.e. prune them)
204 | Alpha(Alpha<0) = INIT_ALPHA_MAX;
205 | %
206 | case {LIKELIHOOD.Bernoulli, LIKELIHOOD.Poisson}
207 | % Heuristic initialisation, trapping Mu==0
208 | Alpha = 1./(Mu+(Mu==0)).^2;
209 | % Limit max/min alpha
210 | Alpha(AlphaINIT_ALPHA_MAX) = INIT_ALPHA_MAX;
212 | %
213 | end
214 | if M==1
215 | SB2_Diagnostic(OPTIONS,2,'Initial alpha = %g\n', Alpha);
216 | end
217 | else
218 | if length(SETTINGS.Alpha)~=length(SETTINGS.Relevant)
219 | error('Basis length (%d) should equal alpha vector length (%d)', ...
220 | length(SETTINGS.Relevant), length(SETTINGS.Alpha))
221 | end
222 | % We may have extra "freebasis" functions without supplied alpha
223 | % - set those to zero for now
224 | Alpha = [SETTINGS.Alpha ; zeros(length(Extra),1)];
225 | end
226 |
227 | % Finally - set Alphas to zero for free basis
228 | ALPHA_ZERO = eps;
229 | Alpha(ismember(Used,OPTIONS.freeBasis)) = ALPHA_ZERO;
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_Likelihoods.m:
--------------------------------------------------------------------------------
1 | % SB2_LIKELIHOODS Convenience function to encapsulate likelihood types
2 | %
3 | % LIKELIHOOD = SB2_LIKELIHOODS(TYPE)
4 | %
5 | % OUTPUT ARGUMENTS:
6 | %
7 | % LIKELIHOOD A structure containing an enumeration of possible
8 | % likelihood types, and specification of the type in use.
9 | %
10 | % INPUT ARGUMENTS:
11 | %
12 | % TYPE A string. One of:
13 | % 'Gaussian'
14 | % 'Bernoulli'
15 | % 'Poisson'
16 | %
17 | % EXAMPLE:
18 | %
19 | % LIKELIHOOD = SB2_Likelihoods('Gaussian');
20 | %
21 | % NOTES:
22 | %
23 | % This function is "for internal use only" and exists purely for convenience
24 | % of coding and to facilitate potential future expansion.
25 | %
26 |
27 | %
28 | % Copyright 2009, Vector Anomaly Ltd
29 | %
30 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
31 | %
32 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
33 | % under the terms of the GNU General Public License as published by the Free
34 | % Software Foundation; either version 2 of the License, or (at your option)
35 | % any later version.
36 | %
37 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
38 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
40 | % more details.
41 | %
42 | % You should have received a copy of the GNU General Public License along
43 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
44 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
45 | % MA 02110-1301 USA
46 | %
47 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
48 | %
49 | function LIKELIHOOD = SB2_Likelihoods(likelihood_)
50 |
51 | % For convenience in code, and later expandability,
52 | % we "enum" the available likelihood models
53 | %
54 | % Continuous
55 | LIKELIHOOD.Gaussian = 1;
56 | % Discrete
57 | LIKELIHOOD.Bernoulli = 2;
58 | LIKELIHOOD.Poisson = 3;
59 |
60 | % Feel free to add your own ... and work out the maths :-)
61 |
62 | % Determine the likelihood to be used
63 | %
64 | LIKELIHOOD.InUse = 0;
65 | %
66 | switch upper(likelihood_(1:5))
67 | case 'GAUSS',
68 | LIKELIHOOD.InUse = LIKELIHOOD.Gaussian;
69 | %
70 | case 'BERNO',
71 | LIKELIHOOD.InUse = LIKELIHOOD.Bernoulli;
72 | %
73 | case 'POISS',
74 | LIKELIHOOD.InUse = LIKELIHOOD.Poisson;
75 | %
76 | otherwise
77 | error('Unknown likelihood ''%s'' specified\n', likelihood_)
78 | end
79 |
80 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_Manual.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iqiukp/RVM-MATLAB/fd70db51f1333525ad4603a35b0c97ccf6ba68ba/SB2_Release_200/SB2_Manual.pdf
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_ParameterSettings.m:
--------------------------------------------------------------------------------
1 | % SB2_PARAMETERSETTINGS User parameter initialisation for SPARSEBAYES
2 | %
3 | % SETTINGS = SB2_PARAMETERSETTINGS(parameter1, value1, parameter2, value2,...)
4 | %
5 | % OUTPUT ARGUMENTS:
6 | %
7 | % SETTINGS An initialisation structure to pass to SPARSEBAYES
8 | %
9 | % INPUT ARGUMENTS:
10 | %
11 | % Optional number of parameter-value pairs to specify some, all, or
12 | % none of the following:
13 | %
14 | % BETA (Gaussian) noise precision (inverse variance)
15 | % NOISESTD (Gaussian) noise standard deviation
16 | % RELEVANT Indices of columns of basis matrix to use at start-up
17 | % WEIGHTS Corresponding vector of weights to RELEVANT
18 | % ALPHA Corresponding vector of hyperparameter values to RELEVANT
19 | %
20 | % EXAMPLE:
21 | %
22 | % SETTINGS = SB2_ParameterSettings('NoiseStd',0.1);
23 | %
24 | % NOTES:
25 | %
26 | % 1. If no input arguments are supplied, defaults (effectively an
27 | % empty structure) will be returned.
28 | %
29 | % 2. If both BETA and NOISESTD are specified, BETA will take
30 | % precedence.
31 | %
32 | % 3. RELEVANT may be specified without WEIGHTS or ALPHA (these will be
33 | % sensibly initialised later).
34 | %
35 | % 4. If RELEVANT is specified, WEIGHTS may be specified also without ALPHA.
36 | %
37 |
38 | %
39 | % Copyright 2009, Vector Anomaly Ltd
40 | %
41 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
42 | %
43 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
44 | % under the terms of the GNU General Public License as published by the Free
45 | % Software Foundation; either version 2 of the License, or (at your option)
46 | % any later version.
47 | %
48 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
49 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
50 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
51 | % more details.
52 | %
53 | % You should have received a copy of the GNU General Public License along
54 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
55 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
56 | % MA 02110-1301 USA
57 | %
58 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
59 | %
60 | function SETTINGS = SB2_ParameterSettings(varargin)
61 |
62 | % Ensure arguments are supplied in pairs
63 | %
64 | if rem(nargin,2)
65 | error('Arguments to SB2_ParameterSettings should be (property, value) pairs')
66 | end
67 | % Any settings specified?
68 | numSettings = nargin/2;
69 |
70 | %% Defaults - over-ridden later if requested
71 |
72 | % Two options for setting noise level (purely for convenience)
73 | % - if 'beta' set, 'noiseStdDev' will be over-ridden
74 | %
75 | SETTINGS.beta = [];
76 | SETTINGS.noiseStdDev = [];
77 | %
78 | SETTINGS.Relevant = [];
79 | SETTINGS.Mu = [];
80 | SETTINGS.Alpha = [];
81 |
82 | %% Requested overrides
83 |
84 | % Parse string/variable pairs
85 | for n=1:numSettings
86 | property_ = varargin{(n-1)*2+1};
87 | value = varargin{(n-1)*2+2};
88 | switch upper(property_)
89 | case 'BETA',
90 | SETTINGS.beta = value;
91 | case 'NOISESTD',
92 | SETTINGS.noiseStdDev = value;
93 | case 'ALPHA',
94 | SETTINGS.Alpha = value;
95 | case 'WEIGHTS',
96 | SETTINGS.Mu = value;
97 | case 'RELEVANT',
98 | SETTINGS.Relevant = value;
99 | otherwise,
100 | error('Unrecognised initialisation property: ''%s''\n', property_)
101 | end
102 | end
103 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_PosteriorMode.m:
--------------------------------------------------------------------------------
1 | % SB2_POSTERIORMODE Posterior mode-finder for the SPARSEBAYES algorithm
2 | %
3 | % [MU, U, BETA, LIKELIHOODMODE, BADHESS] = ...
4 | % SB2_POSTERIORMODE(LIKELIHOOD,BASIS,TARGETS,ALPHA,MU,ITSMAX,OPTIONS)
5 | %
6 | % OUTPUT ARGUMENTS:
7 | %
8 | % MU Parameter values at the mode
9 | % U Cholesky factor of the covariance at the mode
10 | % BETA Vector of pseudo-noise variances at the mode
11 | % LIKELIHOODMODE Data likelihood at the mode
12 | % BADHESS Returns true if Hessian is "bad" (becomes
13 | % non-positive-definite during maximisation)
14 | %
15 | % INPUT ARGUMENTS:
16 | %
17 | % LIKELIHOOD LIKELIHOOD structure
18 | % BASIS Current relevant basis matrix
19 | % TARGETS N-vector with target output values
20 | % ALPHA Current hyperparameters
21 | % MU Current weights
22 | % ITSMAX Maximum number of iterations to run
23 | % OPTIONS Standard OPTIONS structure (only affects diagnostics)
24 | %
25 | % NOTES:
26 | %
27 | % SB2_POSTERIORMODE finds the posterior mode (with respect to the
28 | % weights) of the likelihood function in the non-Gaussian case to
29 | % facilitate subsequent Laplace approximation.
30 | %
31 | % This function is intended for internal use by SPARSEBAYES only (within
32 | % SB2_FullStatistics).
33 | %
34 |
35 | %
36 | % Copyright 2009, Vector Anomaly Ltd
37 | %
38 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
39 | %
40 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
41 | % under the terms of the GNU General Public License as published by the Free
42 | % Software Foundation; either version 2 of the License, or (at your option)
43 | % any later version.
44 | %
45 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
46 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
47 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
48 | % more details.
49 | %
50 | % You should have received a copy of the GNU General Public License along
51 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
52 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
53 | % MA 02110-1301 USA
54 | %
55 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
56 | %
57 | function [Mu, U, Beta, likelihoodMode, badHess] = ...
58 | SB2_PosteriorMode(LIKELIHOOD,BASIS,Targets,Alpha,Mu,itsMax,OPTIONS)
59 |
60 | % TOLERANCES
61 | %
62 | % Termination criterion for each gradient dimension
63 | %
64 | GRADIENT_MIN = 1e-6;
65 | %
66 | % Minimum fraction of the full Newton step considered
67 | %
68 | STEP_MIN = 1/(2^8);
69 | %
70 |
71 | [N, M] = size(BASIS);
72 | A = diag(Alpha);
73 |
74 | % NB: for historical reasons, we work in term of error here (negative
75 | % log-liklihood) and minimise
76 |
77 | %
78 | % Get current model output and data error
79 | %
80 | BASIS_Mu = BASIS*Mu; % Linear output
81 | [dataError, y] = SB2_DataError(LIKELIHOOD, BASIS_Mu, Targets);
82 | %
83 | % Add on the weight penalty
84 | %
85 | regulariser = (Alpha'*(Mu.^2))/2;
86 | newTotalError = dataError + regulariser;
87 | %
88 | badHess = false;
89 | errorLog = zeros(itsMax,1);
90 | %
91 | for iteration=1:itsMax
92 | %
93 | % Log the error value each iteration
94 | %
95 | errorLog(iteration) = newTotalError;
96 | SB2_Diagnostic(OPTIONS,4,'PM cycle: %2d\t error: %.6f\n', ...
97 | iteration, errorLog(iteration));
98 |
99 | % Construct the gradient
100 | %
101 | e = (Targets-y);
102 | g = BASIS'*e - Alpha.*Mu;
103 | %
104 | % Compute the likelihood-dependent analogue of the noise precision.
105 | % NB: Beta now a vector.
106 | %
107 | switch LIKELIHOOD.InUse
108 | case LIKELIHOOD.Bernoulli
109 | Beta = y.*(1-y);
110 | case LIKELIHOOD.Poisson
111 | Beta = y;
112 | end
113 | % Compute the Hessian
114 | BASIS_B = BASIS .* (Beta * ones(1,M));
115 | H = (BASIS_B'*BASIS + A);
116 | % Invert Hessian via Cholesky, watching out for ill-conditioning
117 | [U, pdErr] = chol(H);
118 | % Make sure its positive definite
119 | if pdErr
120 | % If you see this, it's *probably* the result of a bad choice of
121 | % basis. e.g. a kernel with too large a "width"
122 | %
123 | SB2_Diagnostic(OPTIONS, 1,...
124 | '** Warning ** Ill-conditioned Hessian (%g)\n', ...
125 | rcond(H));
126 | badHess = true;
127 | U = [];
128 | Beta = [];
129 | likelihoodMode = [];
130 | return
131 | end
132 | %
133 | % Before progressing, check for termination based on the gradient norm
134 | %
135 | if all(abs(g)STEP_MIN
149 | % Follow gradient to get new value of parameters
150 | %
151 | Mu_new = Mu + step*DeltaMu;
152 | BASIS_Mu = BASIS*Mu_new;
153 | %
154 | % Compute outputs and error at new point
155 | %
156 | [dataError,y] = SB2_DataError(LIKELIHOOD,BASIS_Mu,Targets);
157 | regulariser = (Alpha'*(Mu_new.^2))/2;
158 | newTotalError = dataError + regulariser;
159 | %
160 | % Test that we haven't made things worse
161 | %
162 | if newTotalError>=errorLog(iteration)
163 | % If so, back off!
164 | %
165 | step = step/2;
166 | SB2_Diagnostic(OPTIONS, 4,['PM error increase! Backing off to l=' ...
167 | '%.3f\n'], step);
168 | else
169 | Mu = Mu_new;
170 | step = 0; % this will force exit from the "while" loop
171 | end
172 | end
173 | %
174 | % If we get here with non-zero "step", it means that the smallest
175 | % offset from the current point along the "downhill" direction did not
176 | % lead to a decrease in error. In other words, we must be
177 | % infinitesimally close to a minimum (which is OK).
178 | %
179 | if step
180 | SB2_Diagnostic(OPTIONS, 4, ...
181 | 'PM stopping due to back-off limit (|g| = %g)\n', ...
182 | max(abs(g)));
183 | break;
184 | end
185 | end
186 | %
187 | % Simple computation of return value of log likelihood at mode
188 | %
189 | likelihoodMode = -dataError;
190 |
191 | %%%%%%%%%%%%%%%%%%%%%%
192 | %
193 | % Support function
194 | %
195 | %%%%%%%%%%%%%%%%%%%%%%
196 |
197 | function [e,y] = SB2_DataError(LIKELIHOOD,BASIS_Mu,Targets)
198 |
199 | switch LIKELIHOOD.InUse
200 | case LIKELIHOOD.Bernoulli
201 | y = SB2_Sigmoid(BASIS_Mu);
202 | % Handle probability zero cases
203 | y0 = (y==0);
204 | y1 = (y==1);
205 | if any(y0(Targets>0)) || any(y1(Targets<1))
206 | % Error infinite when model gives zero probability in
207 | % contradiction to data
208 | e = inf;
209 | else
210 | % Any y=0 or y=1 cases must now be accompanied by appropriate
211 | % output=0 or output=1 values, so can be excluded.
212 | e = -(Targets(~y0)'*log(y(~y0)) + (1-Targets(~y1))'*log(1-y(~y1)));
213 | end
214 | %
215 | case LIKELIHOOD.Poisson
216 | y = exp(BASIS_Mu);
217 | e = -sum(Targets.*BASIS_Mu - y);
218 | %
219 | end
220 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_PreProcessBasis.m:
--------------------------------------------------------------------------------
1 | % SB2_PREPROCESSBASIS Do basis matrix pre-processing for SPARSEBAYES
2 | %
3 | % [BASIS, SCALES] = SB2_PREPROCESSBASIS(BASIS)
4 | %
5 | % OUTPUT ARGUMENTS:
6 | %
7 | % BASIS NxM matrix of basis vectors appropriately pre-processed
8 | % (scaled to unit length per column)
9 | %
10 | % SCALES Vector of scaling factors applied
11 | %
12 | %
13 | % INPUT ARGUMENTS:
14 | %
15 | % BASIS NxM matrix of basis vectors (one column per basis function)
16 | %
17 | % NOTES:
18 | %
19 | % Simply normalises the basis vectors to unit length, returning the
20 | % original lengths so that the weights can be rescaled appropriately
21 | % before returning to the user.
22 | %
23 |
24 | %
25 | % Copyright 2009, Vector Anomaly Ltd
26 | %
27 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
28 | %
29 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
30 | % under the terms of the GNU General Public License as published by the Free
31 | % Software Foundation; either version 2 of the License, or (at your option)
32 | % any later version.
33 | %
34 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
35 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
36 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
37 | % more details.
38 | %
39 | % You should have received a copy of the GNU General Public License along
40 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
41 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
42 | % MA 02110-1301 USA
43 | %
44 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
45 | %
46 | function [BASIS, Scales] = SB2_PreProcessBasis(BASIS)
47 |
48 | %
49 | [N,M] = size(BASIS);
50 | %
51 | % Compute "lengths" of basis vectors (columns of BASIS)
52 | %
53 | Scales = sqrt(sum(BASIS.^2));
54 | %
55 | % Work-around divide-by-zero inconvenience
56 | %
57 | Scales(Scales==0) = 1;
58 | %
59 | % Normalise each basis vector to "unit length"
60 | %
61 | for m=1:M
62 | BASIS(:,m) = BASIS(:,m) / Scales(m);
63 | end
64 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_Sigmoid.m:
--------------------------------------------------------------------------------
1 | % SB2_SIGMOID Generic sigmoid function for SPARSEBAYES computations
2 | %
3 | % Y = SB2_SIGMOID(X)
4 | %
5 | % Implements the function f(x) = 1 / (1+exp(-x))
6 | %
7 |
8 | %
9 | % Copyright 2009, Vector Anomaly Ltd
10 | %
11 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
12 | %
13 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
14 | % under the terms of the GNU General Public License as published by the Free
15 | % Software Foundation; either version 2 of the License, or (at your option)
16 | % any later version.
17 | %
18 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
19 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 | % more details.
22 | %
23 | % You should have received a copy of the GNU General Public License along
24 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
25 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
26 | % MA 02110-1301 USA
27 | %
28 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
29 | %
30 | function y = SB2_Sigmoid(x)
31 |
32 | y = 1 ./ (1+exp(-x));
33 |
--------------------------------------------------------------------------------
/SB2_Release_200/SB2_UserOptions.m:
--------------------------------------------------------------------------------
1 | % SB2_USEROPTIONS User option specification for SPARSEBAYES
2 | %
3 | % OPTIONS = SB2_USEROPTIONS(parameter1, value1, parameter2, value2,...)
4 | %
5 | % OUTPUT ARGUMENTS:
6 | %
7 | % OPTIONS An options structure to pass to SPARSEBAYES
8 | %
9 | % INPUT ARGUMENTS:
10 | %
11 | % Optional number of parameter-value pairs to specify the following:
12 | %
13 | % ITERATIONS Number of interations to run for.
14 | %
15 | % TIME Time limit to run for, expressed as a space-separated
16 | % string. e.g. '1.5 hours', '30 minutes', '1 second'.
17 | %
18 | % DIAGNOSTICLEVEL Integer [0,4] or string to determine the verbosity of
19 | % diagnostic output.
20 | % 0 or 'ZERO' or 'NONE' No output
21 | % 1 or 'LOW' Low level of output
22 | % 2 or 'MEDIUM' etc...
23 | % 3 or 'HIGH'
24 | % 4 or 'ULTRA'
25 | %
26 | % DIAGNOSTICFILE Filename to write diagnostics to file instead of
27 | % the default stdout.
28 | %
29 | % MONITOR Integer number: diagnostic information is output
30 | % every MONITOR iterations.
31 | %
32 | % FIXEDNOISE True/false whether the Gaussian noise is to be fixed
33 | % (default: false.
34 | %
35 | % FREEBASIS Indices of basis vectors considered "free" and not
36 | % constrained by the Bayesian prior (e.g. the "bias").
37 | %
38 | % CALLBACK External function to call each iteration of the algorithm
39 | % (string). Intended to facilitate graphical demos etc.
40 | %
41 | % CALLBACKDATA Arbitrary additional data to pass to the CALLBACK
42 | % function.
43 | %
44 | % EXAMPLE:
45 | %
46 | % OPTIONS = SB2_UserOptions('diagnosticLevel','medium',...
47 | % 'monitor',25,...
48 | % 'diagnosticFile', 'logfile.txt');
49 | %
50 | % NOTES:
51 | %
52 | % Each option (field of OPTIONS) is given a default value in
53 | % SB2_USEROPTIONS. Any supplied property-value pairs over-ride those
54 | % defaults.
55 | %
56 |
57 | %
58 | % Copyright 2009, Vector Anomaly Ltd
59 | %
60 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
61 | %
62 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
63 | % under the terms of the GNU General Public License as published by the Free
64 | % Software Foundation; either version 2 of the License, or (at your option)
65 | % any later version.
66 | %
67 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
68 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
69 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
70 | % more details.
71 | %
72 | % You should have received a copy of the GNU General Public License along
73 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
74 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
75 | % MA 02110-1301 USA
76 | %
77 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
78 | %
79 | function OPTIONS = SB2_UserOptions(varargin)
80 |
81 | % Ensure arguments are supplied in pairs
82 | %
83 | if rem(nargin,2)
84 | error('Arguments to SB2_UserOptions should be (property, value) pairs')
85 | end
86 | % Any options specified?
87 | numSettings = nargin/2;
88 |
89 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 |
91 | % Set defaults
92 | %
93 | % Assume we will infer the noise in the Gaussian case
94 | %
95 | OPTIONS.fixedNoise = false;
96 | %
97 | % Option to allow subset of the basis (e.g. bias) to be unregularised
98 | %
99 | OPTIONS.freeBasis = [];
100 | %
101 | % Option to set max iterations to run for
102 | %
103 | OPTIONS.iterations = 10000;
104 | %
105 | % Option to set max time to run for
106 | %
107 | OPTIONS.time = 10000; % seconds
108 | %
109 | % Set options for monitoring and recording the algorithm's progress
110 | %
111 | OPTIONS.monitor = 0;
112 | OPTIONS.diagnosticLevel = 0;
113 | OPTIONS.diagnosticFID = 1; % stdout
114 | OPTIONS.diagnosticFile_ = [];
115 | %
116 | % Option to call a function during each iteration (to create demos etc)
117 | %
118 | OPTIONS.callback = false;
119 | OPTIONS.callbackFunc = [];
120 | OPTIONS.callbackData = {};
121 |
122 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123 |
124 | % Parse string/variable pairs
125 |
126 | for n=1:numSettings
127 | property_ = varargin{(n-1)*2+1};
128 | value = varargin{(n-1)*2+2};
129 | switch upper(property_)
130 | %
131 | case 'FIXEDNOISE',
132 | OPTIONS.fixedNoise = value;
133 | %
134 | case 'FREEBASIS',
135 | OPTIONS.freeBasis = value;
136 | %
137 | case 'ITERATIONS',
138 | OPTIONS.iterations = value;
139 | %
140 | case 'TIME',
141 | OPTIONS.time = timeInSeconds(value);
142 | %
143 | case 'MONITOR',
144 | OPTIONS.monitor = value;
145 | %
146 | case 'DIAGNOSTICLEVEL',
147 | OPTIONS.diagnosticLevel = value;
148 | MAX_LEVEL = 4;
149 | if ischar(value)
150 | switch upper(value)
151 | case {'ZERO','NONE'},
152 | OPTIONS.diagnosticLevel = 0;
153 | case 'LOW',
154 | OPTIONS.diagnosticLevel = 1;
155 | case 'MEDIUM',
156 | OPTIONS.diagnosticLevel = 2;
157 | case 'HIGH',
158 | OPTIONS.diagnosticLevel = 3;
159 | case 'ULTRA',
160 | OPTIONS.diagnosticLevel = 4;
161 | otherwise,
162 | error('Unrecognised textual diagnostic level: ''%s''\n', value)
163 | end
164 | elseif isnumeric(value)
165 | if value>=0 & value<=MAX_LEVEL
166 | OPTIONS.diagnosticLevel = value;
167 | else
168 | error(['Supplied level should be integer in [0,%d], '...
169 | 'or one of ZERO/LOW/MEDIUM/HIGH/ULTRA'], MAX_LEVEL);
170 |
171 | end
172 | end
173 | %
174 | case 'DIAGNOSTICFILE',
175 | OPTIONS.diagnosticFile_ = value;
176 | OPTIONS.diagnosticFID = -1; % It will be opened later
177 | %
178 | case 'CALLBACK',
179 | OPTIONS.callback = true;
180 | OPTIONS.callbackFunc = value;
181 | if exist(OPTIONS.callbackFunc)~=2
182 | error('Callback function ''%s'' does not appear to exist\n', value)
183 | end
184 | %
185 | case 'CALLBACKDATA',
186 | OPTIONS.callbackData = value;
187 | otherwise,
188 | error('Unrecognised user option: ''%s''\n', property_)
189 | %
190 | end
191 | end
192 |
193 | %%
194 | %% Support function: parse time specification
195 | %%
196 | function s = timeInSeconds(value_)
197 | %
198 | [v_ r_] = strtok(value_,' ');
199 | v = str2num(v_);
200 | r_(isspace(r_)) = [];
201 | switch upper(r_)
202 | case {'SECONDS', 'SECOND'}
203 | s = v;
204 | case {'MINUTES', 'MINUTE'}
205 | s = 60*v;
206 | case {'HOURS', 'HOUR'}
207 | s = 3600*v;
208 | otherwise,
209 | error('Badly formed time string: ''%s''', value_)
210 | end
211 | % Returns time in seconds
212 | %
--------------------------------------------------------------------------------
/SB2_Release_200/SparseBayes.m:
--------------------------------------------------------------------------------
1 | % SPARSEBAYES Sparse Bayesian modelling: main estimation algorithm
2 | %
3 | % [PARAMETER, HYPERPARAMETER, DIAGNOSTIC] = ...
4 | % SPARSEBAYES(LIKELIHOOD, BASIS, TARGETS, OPTIONS, SETTINGS)
5 | %
6 | % OUTPUT ARGUMENTS:
7 | %
8 | % PARAMETER Structure specifying inferred primary parameters:
9 | %
10 | % .Value Vector of weight values
11 | % .Relevant Vector of corresponding indices of relevant
12 | % columns of BASIS matrix (sorted ascending)
13 | %
14 | % HYPERPARAMETER Structure specifying inferred hyperparameters:
15 | %
16 | % .Alpha Vector of weight precision values
17 | % .beta Noise precision (Gaussian likelihood case)
18 | %
19 | % DIAGNOSTIC Structure containing various diagnostics:
20 | %
21 | % .Gamma Vector of "well-determined" factors [0,1] for
22 | % relevant weights
23 | % .Likelihood Vector of evolving log-marginal-likelihood
24 | % .iterations Number of iterations run
25 | % .S_Factor Vector of S ("Sparsity") values for relevant weights
26 | % .Q_Factor Vector of Q ("Quality") values for relevant weights
27 | %
28 | % INPUT ARGUMENTS:
29 | %
30 | % LIKELIHOOD String comprising one of 'Gaussian', 'Bernoulli' or 'Poisson'
31 | %
32 | % BASIS NxM matrix of basis vectors (one column per basis function)
33 | %
34 | % TARGETS N-vector with target output values
35 | %
36 | % OPTIONS User-specified settings via SB2_USEROPTIONS [Optional]
37 | %
38 | % SETTINGS Initialisation of main parameter values via
39 | % SB2_PARAMETERSETTINGS [Optional]
40 | %
41 | % NOTES:
42 | %
43 | % SPARSEBAYES is the implementation of the main algorithm for parameter
44 | % inference in "sparse Bayesian" models.
45 | %
46 | % Given inputs (BASIS), desired outputs (TARGETS) and an appropriate
47 | % LIKELIHOOD function, SPARSEBAYES will optimise the log-marginal-likelihood
48 | % of the corresponding sparse Bayesian model and should return (given
49 | % reasonable choice of basis) a sparse vector of model parameters.
50 | %
51 | % OPTIONS and SETTINGS arguments may be omitted, and if so, will assume
52 | % sensible default values.
53 | %
54 | % SEE ALSO:
55 | %
56 | % SB2_USEROPTIONS: available user-definable options, such as the
57 | % number of iterations to run for, level of diagnostic output etc.
58 | %
59 | % SB2_PARAMETERSETTINGS: facility to change default initial values for
60 | % parameters and hyperparameters.
61 | %
62 | % SB2_CONTROLSETTINGS: hard-wired internal algorithm settings.
63 | %
64 | % The main algorithm is based upon that outlined in "Fast marginal
65 | % likelihood maximisation for sparse Bayesian models", by Tipping & Faul, in
66 | % Proceedings of AISTATS 2003. That paper may be downloaded from
67 | % www.relevancevector.com or via the conference online proceedings site at
68 | % http://research.microsoft.com/conferences/aistats2003/proceedings/.
69 | %
70 |
71 | %
72 | % Copyright 2009, Vector Anomaly Ltd
73 | %
74 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
75 | %
76 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
77 | % under the terms of the GNU General Public License as published by the Free
78 | % Software Foundation; either version 2 of the License, or (at your option)
79 | % any later version.
80 | %
81 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
82 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
83 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
84 | % more details.
85 | %
86 | % You should have received a copy of the GNU General Public License along
87 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
88 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
89 | % MA 02110-1301 USA
90 | %
91 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
92 | %
93 | function [PARAMETER, HYPERPARAMETER, DIAGNOSTIC] = ...
94 | SparseBayes(likelihood_, BASIS, Targets, OPTIONS, SETTINGS)
95 |
96 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 | %%
98 | %% SET UP INITIAL PARAMETERS, USER OPTIONS AND ALGORITHM CONTROL SETTINGS
99 | %%
100 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101 |
102 | %% If no initial parameter setting structure passed, import the defaults
103 | if ~exist('SETTINGS','var')
104 | SETTINGS = SB2_ParameterSettings;
105 | end
106 |
107 | %% If no user options passed, import defaults
108 | if ~exist('OPTIONS','var')
109 | OPTIONS = SB2_UserOptions;
110 | end
111 |
112 | %% Any sanity checks on options and initialisation here
113 |
114 | % Error if fixed noise specified but value not set
115 | %
116 | if OPTIONS.fixedNoise && isempty(SETTINGS.beta) && ...
117 | isempty(SETTINGS.noiseStdDev)
118 | error('Option to fix noise variance is set but value is not supplied.')
119 | end
120 |
121 | %% Get the default algorithm control settings
122 |
123 | CONTROLS = SB2_ControlSettings;
124 |
125 | % Start the clock now for diagnostic purposes (and to allow the algorithm to
126 | % run for a fixed time)
127 | %
128 | t0 = clock;
129 |
130 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131 | %%
132 | %% INITIALISATION
133 | %%
134 | %% Pre-process basis, set up internal parameters according to initial
135 | %% settings and likelihod specification
136 | %%
137 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138 |
139 | % Kick off diagnostics (primarily, open log file if specified)
140 | %
141 | OPTIONS = SB2_Diagnostic(OPTIONS, 'start');
142 |
143 | %
144 | % Initialise everything, based on SETTINGS and OPTIONS
145 | %
146 | [LIKELIHOOD, BASIS, BasisScales, Alpha, beta, Mu, PHI, Used] = ...
147 | SB2_Initialisation(likelihood_, BASIS, Targets, SETTINGS, OPTIONS);
148 | %
149 | % Cache some values for later efficiency
150 | %
151 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
152 | % It will be computationally advantageous to "cache" this quantity
153 | % in the Gaussian case
154 | BASIS_PHI = BASIS'*PHI;
155 | else
156 | BASIS_PHI = [];
157 | end
158 | BASIS_Targets = BASIS'*Targets;
159 |
160 | % FULL COMPUTATION
161 | %
162 | % Initialise with a full explicit computation of the statistics
163 | %
164 | % NOTE: The AISTATS paper uses "S/Q" (upper case) to denote the key
165 | % "sparsity/quality" Factors for "included" basis functions, and "s/q"
166 | % (lower case) for the factors calculated when the relevant basis
167 | % functions are "excluded".
168 | %
169 | % Here, for greater clarity:
170 | %
171 | % All S/Q are denoted by vectors S_in, Q_in
172 | % All s/q are denoted by vectors S_out, Q_out
173 | %
174 | [SIGMA,Mu,S_in,Q_in,S_out,Q_out,Factor,logML,Gamma,BASIS_B_PHI,beta] = ...
175 | SB2_FullStatistics(LIKELIHOOD,BASIS,PHI,Targets,Used, ...
176 | Alpha,beta,Mu,BASIS_PHI,BASIS_Targets,OPTIONS);
177 |
178 | %
179 | % Avoid falling over in pathological case of zero iterations
180 | %
181 | if OPTIONS.iterations==0
182 | PARAMETER = [];
183 | HYPERPARAMETER = [];
184 | DIAGNOSTIC.Likelihood = logML;
185 | return
186 | end
187 |
188 | %
189 | [N M_full] = size(BASIS);
190 | M = size(PHI,2);
191 | %
192 | % Some diagnostics
193 | %
194 | addCount = 0;
195 | deleteCount = 0;
196 | updateCount = 0;
197 | %
198 | % Create storage to record evolution of log marginal likelihood
199 | %
200 | maxLogSize = OPTIONS.iterations + CONTROLS.BetaUpdateStart + ...
201 | ceil(OPTIONS.iterations/CONTROLS.BetaUpdateFrequency);
202 | logMarginalLog = zeros(maxLogSize,1);
203 | count = 0;
204 | %
205 | % If we're doing basis alignment testing, we'll need to maintain lists of
206 | % those functions that are near identical, both in and out of the current
207 | % model.
208 | %
209 | if CONTROLS.BasisAlignmentTest
210 | Aligned_out = [];
211 | Aligned_in = [];
212 | alignDeferCount = 0;
213 | end
214 |
215 | % ACTION CODES
216 | %
217 | % Assign an integer code to the basic action types
218 | %
219 | ACTION_REESTIMATE = 0;
220 | ACTION_ADD = 1;
221 | ACTION_DELETE = -1;
222 | %
223 | % Some extra types
224 | %
225 | ACTION_TERMINATE = 10;
226 | %
227 | ACTION_NOISE_ONLY = 11;
228 | %
229 | ACTION_ALIGNMENT_SKIP = 12;
230 |
231 | %
232 | % Before kicking off the main loop, call the specified "callback" function
233 | % with "ACTION_ADD" to take account of the initialisation of the model
234 | %
235 | if OPTIONS.callback
236 | feval(OPTIONS.callbackFunc, 0, ACTION_ADD,logML/N, ...
237 | Used, Mu./BasisScales(Used)', SIGMA,...
238 | Alpha, beta, Gamma, PHI,...
239 | OPTIONS.callbackData{:})
240 | end
241 |
242 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243 | %%
244 | %% MAIN LOOP
245 | %%
246 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 | %
248 | i = 0; % Iteration number
249 | LAST_ITERATION = false;
250 | %
251 | while (~LAST_ITERATION)
252 | %
253 | i = i+1;
254 | %
255 | % "UpdateIteration": set to true if this is an iteration where fast matrix
256 | % update rules can be used compute the appropriate quantities
257 | %
258 | % This can be done if:
259 | %
260 | % - we are using a Gaussian likelihood
261 | % - we are using other likelihoods and we have not specified a full
262 | % posterior mode computation this cycle
263 | %
264 | UpdateIteration = LIKELIHOOD.InUse==LIKELIHOOD.Gaussian || ...
265 | rem(i,CONTROLS.PosteriorModeFrequency);
266 |
267 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
268 |
269 | %% DECISION PHASE
270 | %%
271 | %% Assess all potential actions
272 | %%
273 |
274 | %
275 | % Compute change in likelihood for all possible updates
276 | %
277 | DeltaML = zeros(M_full,1);
278 | %
279 | Action = ACTION_REESTIMATE*ones(M_full,1); % Default
280 | %
281 | % 'Relevance Factor' (Q^S-S) values for basis functions in model
282 | %
283 | UsedFactor = Factor(Used);
284 |
285 | %
286 | % RE-ESTIMATION: must be a POSITIVE 'factor' and already IN the model
287 | %
288 | iu = UsedFactor>CONTROLS.ZeroFactor;
289 | index = Used(iu);
290 | NewAlpha = S_out(index).^2 ./ Factor(index);
291 | Delta = (1./NewAlpha - 1./Alpha(iu)); % Temp vector
292 | %
293 | % Quick computation of change in log-likelihood given all re-estimations
294 | %
295 | DeltaML(index) = (Delta.*(Q_in(index).^2) ./ ...
296 | (Delta.*S_in(index) + 1) - ...
297 | log(1 + S_in(index).*Delta))/2;
298 |
299 | %
300 | % DELETION: if NEGATIVE factor and IN model
301 | %
302 | % But don't delete:
303 | % - any "free" basis functions (e.g. the "bias")
304 | % - if there is only one basis function (M=1)
305 | %
306 | % (In practice, this latter event ought only to happen with the Gaussian
307 | % likelihood when initial noise is too high. In that case, a later beta
308 | % update should 'cure' this.)
309 | %
310 | iu = ~iu; % iu = UsedFactor <= CONTROLS.ZeroFactor
311 | index = Used(iu);
312 | anyToDelete = ~isempty(setdiff(index,OPTIONS.freeBasis)) && M>1;
313 | %
314 | if anyToDelete
315 | %
316 | % Quick computation of change in log-likelihood given all deletions
317 | %
318 | DeltaML(index) = -(Q_out(index).^2 ./ (S_out(index) + Alpha(iu)) - ...
319 | log(1 + S_out(index) ./ Alpha(iu)))/2;
320 | Action(index) = ACTION_DELETE;
321 | % Note: if M==1, DeltaML will be left as zero, which is fine
322 | end
323 |
324 | %
325 | % ADDITION: must be a POSITIVE factor and OUT of the model
326 | %
327 | % Find ALL good factors ...
328 | GoodFactor = Factor>CONTROLS.ZeroFactor;
329 | % ... then mask out those already in model
330 | GoodFactor(Used) = 0;
331 | % ... and then mask out any that are aligned with those in the model
332 | if CONTROLS.BasisAlignmentTest
333 | GoodFactor(Aligned_out) = 0;
334 | end
335 | %
336 | index = find(GoodFactor);
337 | anyToAdd = ~isempty(index);
338 | if anyToAdd
339 | %
340 | % Quick computation of change in log-likelihood given all additions
341 | %
342 | quot = Q_in(index).^2 ./ S_in(index);
343 | DeltaML(index) = (quot - 1 - log(quot))/2;
344 | Action(index) = ACTION_ADD;
345 | end
346 |
347 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
348 |
349 | % Post-process action results to take account of preferences
350 |
351 | % Ensure that nothing happens with "free basis" functions
352 | %
353 | DeltaML(OPTIONS.freeBasis) = 0;
354 |
355 | % If we prefer ADD or DELETE actions over RE-ESTIMATION
356 | %
357 | if (anyToAdd && CONTROLS.PriorityAddition) || ...
358 | (anyToDelete && CONTROLS.PriorityDeletion)
359 | % We won't perform re-estimation this iteration, which we achieve by
360 | % zero-ing out the delta
361 | DeltaML(Action==ACTION_REESTIMATE) = 0;
362 | % Furthermore, we should enforce ADD if preferred and DELETE is not
363 | % - and vice-versa
364 | if (anyToAdd && CONTROLS.PriorityAddition && ~CONTROLS.PriorityDeletion)
365 | DeltaML(Action==ACTION_DELETE) = 0;
366 | end
367 | if (anyToDelete && CONTROLS.PriorityDeletion && ~CONTROLS.PriorityAddition)
368 | DeltaML(Action==ACTION_ADD) = 0;
369 | end
370 | end
371 |
372 | % Finally...we choose the action that results
373 | % in the greatest change in likelihood
374 | %
375 | [deltaLogMarginal nu] = max(DeltaML);
376 | selectedAction = Action(nu);
377 | anyWorthwhileAction = deltaLogMarginal>0;
378 | %
379 | % We need to note if basis nu is already in the model, and if so,
380 | % find its interior index, denoted by "j"
381 | %
382 | if selectedAction==ACTION_REESTIMATE || selectedAction==ACTION_DELETE
383 | j = find(Used==nu);
384 | end
385 | %
386 | % Get the individual basis vector for update and compute its optimal alpha,
387 | % according to equation (20): alpha = S_out^2 / (Q_out^2 - S_out)
388 | %
389 | Phi = BASIS(:,nu);
390 | newAlpha = S_out(nu)^2 / Factor(nu);
391 |
392 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393 |
394 | % TERMINATION CONDITIONS
395 | %
396 | % Propose to terminate if:
397 | %
398 | % 1. there is no worthwhile (likelihood-increasing) action, OR
399 | %
400 | % 2a. the best action is an ACTION_REESTIMATE but this would only lead to
401 | % an infinitesimal alpha change, AND
402 | % 2b. at the same time there are no potential awaiting deletions
403 | %
404 | if ~anyWorthwhileAction || ...
405 | (selectedAction==ACTION_REESTIMATE && ...
406 | abs(log(newAlpha) - log(Alpha(j)))CONTROLS.AlignmentMax);
432 | numAligned = length(findAligned);
433 | if numAligned>0
434 | % The added basis function is effectively indistinguishable from
435 | % one present already
436 | selectedAction = ACTION_ALIGNMENT_SKIP;
437 | act_ = 'alignment-deferred addition';
438 | alignDeferCount = alignDeferCount+1;
439 | % Make a note so we don't try this next time
440 | % May be more than one in the model, which we need to note was
441 | % the cause of function 'nu' being rejected
442 | Aligned_out = [Aligned_out ; nu*ones(numAligned,1)];
443 | Aligned_in = [Aligned_in ; Used(findAligned)];
444 | end
445 | end
446 | %
447 | % Deletion: reinstate any previously deferred basis functions
448 | % resulting from this basis function
449 | %
450 | if selectedAction==ACTION_DELETE
451 | findAligned = find(Aligned_in==nu);
452 | numAligned = length(findAligned);
453 | if numAligned>0
454 | reinstated = Aligned_out(findAligned);
455 | Aligned_in(findAligned) = [];
456 | Aligned_out(findAligned) = [];
457 | %
458 | r_ = sprintf('%d ', reinstated);
459 | SB2_Diagnostic(OPTIONS,3,'Alignment reinstatement of %s\n', r_);
460 | end
461 | end
462 | end
463 |
464 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 |
466 | %% ACTION PHASE
467 | %%
468 | %% Implement above decision
469 | %%
470 |
471 | % We'll want to note if we've made a change which necessitates later
472 | % updating of the statistics
473 | %
474 | UPDATE_REQUIRED = false;
475 | %
476 | switch selectedAction
477 | %
478 | case ACTION_REESTIMATE,
479 | %
480 | % Basis function 'nu' is already in the model,
481 | % and we're re-estimating its corresponding alpha
482 | %
483 | % - should correspond to Appendix A.3
484 | %
485 | oldAlpha = Alpha(j);
486 | Alpha(j) = newAlpha;
487 | s_j = SIGMA(:,j);
488 | deltaInv = 1/(newAlpha - oldAlpha);
489 | kappa = 1/(SIGMA(j,j)+deltaInv);
490 | tmp = kappa*s_j;
491 | SIGMANEW = SIGMA - tmp * s_j';
492 | deltaMu = -Mu(j)*tmp;
493 | Mu = Mu + deltaMu;
494 | %
495 | if UpdateIteration
496 | S_in = S_in + kappa*(BASIS_B_PHI * s_j).^2;
497 | Q_in = Q_in - BASIS_B_PHI*(deltaMu);
498 | end
499 | updateCount = updateCount+1;
500 | act_ = 're-estimation';
501 | %
502 | UPDATE_REQUIRED = true;
503 | %
504 |
505 | case ACTION_ADD,
506 | %
507 | % Basis function nu is not in the model, and we're adding it in
508 | %
509 | % - should correspond to Appendix A.2
510 | %
511 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
512 | BASIS_Phi = BASIS'*Phi;
513 | BASIS_PHI = [BASIS_PHI BASIS_Phi];
514 | B_Phi = beta*Phi;
515 | BASIS_B_Phi = beta*BASIS_Phi;
516 | else
517 | B_Phi = (Phi.*beta);
518 | BASIS_B_phi = BASIS'*B_Phi;
519 | end
520 | tmp = ((B_Phi'*PHI)*SIGMA)';
521 | %
522 | Alpha = [Alpha ; newAlpha];
523 | PHI = [PHI Phi];
524 | %
525 | s_ii = 1/(newAlpha+S_in(nu));
526 | s_i = -s_ii*tmp;
527 | TAU = -s_i*tmp';
528 | SIGMANEW = [SIGMA+TAU s_i ; s_i' s_ii];
529 | mu_i = s_ii*Q_in(nu);
530 | deltaMu = [-mu_i*tmp ; mu_i];
531 | Mu = [Mu ; 0] + deltaMu;
532 | %
533 | if UpdateIteration
534 | mCi = BASIS_B_Phi - BASIS_B_PHI*tmp;
535 | S_in = S_in - s_ii * mCi.^2;
536 | Q_in = Q_in - mu_i * mCi;
537 | end
538 | Used = [Used; nu];
539 | addCount = addCount+1;
540 | act_ = 'addition';
541 | %
542 | UPDATE_REQUIRED = true;
543 |
544 | case ACTION_DELETE,
545 | %
546 | % Basis function nu is in the model, but we're removing it
547 | %
548 | % - should correspond to Appendix A.4
549 | %
550 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
551 | BASIS_PHI(:,j) = [];
552 | end
553 | PHI(:,j) = [];
554 | Alpha(j) = [];
555 | %
556 | s_jj = SIGMA(j,j);
557 | s_j = SIGMA(:,j);
558 | tmp = s_j/s_jj;
559 | SIGMANEW = SIGMA - tmp*s_j';
560 | SIGMANEW(j,:) = [];
561 | SIGMANEW(:,j) = [];
562 | deltaMu = - Mu(j)*tmp;
563 | mu_j = Mu(j);
564 | Mu = Mu +deltaMu;
565 | Mu(j) = [];
566 | %
567 | if UpdateIteration
568 | jPm = (BASIS_B_PHI * s_j);
569 | S_in = S_in + jPm.^2 / s_jj;
570 | Q_in = Q_in + jPm * mu_j / s_jj;
571 | end
572 | Used(j) = [];
573 | deleteCount = deleteCount+1;
574 | act_ = 'deletion';
575 | %
576 | UPDATE_REQUIRED = true;
577 | %
578 | end % of switch over actions
579 | M = length(Used);
580 | %
581 | SB2_Diagnostic(OPTIONS, 3, 'ACTION: %s of %d (%g)\n', ...
582 | act_, nu, deltaLogMarginal);
583 | %
584 |
585 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
586 |
587 | %% UPDATE STATISTICS
588 |
589 | % If we've performed a meaningful action,
590 | % update the relevant variables
591 | %
592 | if UPDATE_REQUIRED
593 | %
594 | % S_in & Q_in values were calculated earlier
595 | %
596 | % Here: update the S_out/Q_out values and relevance factors
597 | %
598 | if UpdateIteration
599 | %
600 | % Previous "update" statisics calculated earlier are valid
601 | %
602 | S_out = S_in;
603 | Q_out = Q_in;
604 | tmp = Alpha ./ (Alpha - S_in(Used));
605 | S_out(Used) = tmp .* S_in(Used);
606 | Q_out(Used) = tmp .* Q_in(Used);
607 | Factor = (Q_out.*Q_out - S_out);
608 | SIGMA = SIGMANEW;
609 | Gamma = 1 - Alpha.*diag(SIGMA);
610 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
611 | BASIS_B_PHI = beta*BASIS_PHI;
612 | else
613 | BASIS_B_PHI = ((PHI.* (beta*ones(1,M)))'*BASIS)';
614 | end
615 | else
616 | %
617 | % Compute all statistics in "full" form (non-Gaussian likelihoods)
618 | %
619 | [SIGMA,Mu,S_in,Q_in,S_out,Q_out,Factor,newLogM,Gamma,...
620 | BASIS_B_PHI,beta] = ...
621 | SB2_FullStatistics(LIKELIHOOD,BASIS,PHI,Targets,Used,...
622 | Alpha,beta,Mu,BASIS_PHI,BASIS_Targets,OPTIONS);
623 | deltaLogMarginal = newLogM - logML;
624 | end
625 | %
626 | if UpdateIteration && deltaLogMarginal<0
627 | SB2_Diagnostic(OPTIONS,1,...
628 | '** Alert ** DECREASE IN LIKELIHOOD !! (%g)\n',...
629 | deltaLogMarginal);
630 | end
631 | %
632 | logML = logML + deltaLogMarginal;
633 | count = count + 1;
634 | logMarginalLog(count) = logML;
635 | end
636 |
637 | % GAUSSIAN NOISE ESTIMATE
638 | %
639 | % For Gaussian likelihood, re-estimate noise variance if:
640 | %
641 | % - not fixed, AND
642 | % - an update is specified this cycle as normal, OR
643 | % - we're considering termination
644 | %
645 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian && ...
646 | ~OPTIONS.fixedNoise && ...
647 | (selectedAction == ACTION_TERMINATE || ...
648 | i<=CONTROLS.BetaUpdateStart || ...
649 | rem(i,CONTROLS.BetaUpdateFrequency)==0)
650 | %
651 | betaZ1 = beta;
652 | y = PHI * Mu;
653 | e = (Targets-y);
654 | beta = (N - sum(Gamma))/(e'*e);
655 | % Work-around zero-noise issue
656 | beta = min([beta CONTROLS.BetaMaxFactor/var(Targets)]);
657 | %
658 | deltaLogBeta = log(beta)-log(betaZ1);
659 | %
660 | if abs(deltaLogBeta) > CONTROLS.MinDeltaLogBeta
661 | %
662 | % Full re-computation of statistics required after beta update
663 | %
664 | [SIGMA,Mu,S_in,Q_in,S_out,Q_out,Factor,logML,Gamma,BASIS_B_PHI] = ...
665 | SB2_FullStatistics(LIKELIHOOD,BASIS,PHI,Targets,Used,Alpha,beta,...
666 | Mu, BASIS_PHI,BASIS_Targets,OPTIONS);
667 | %
668 | count = count + 1;
669 | logMarginalLog(count) = logML;
670 | %
671 | if selectedAction==ACTION_TERMINATE
672 | %
673 | % We considered terminating above as no alpha update seemed
674 | % worthwhile. However, a beta update has made a non-trivial
675 | % increase in the likelihood, so we continue.
676 | %
677 | selectedAction = ACTION_NOISE_ONLY;
678 | SB2_Diagnostic(OPTIONS,3,'Noise update (termination deferred)\n');
679 | end
680 | end
681 | end
682 |
683 | % CALLBACK
684 | %
685 | % Call callback function if specified
686 | % - this can be useful for demos etc where it is desired to display
687 | % graphical information at each iteration
688 | %
689 | if OPTIONS.callback
690 | feval(OPTIONS.callbackFunc, i, selectedAction,...
691 | logMarginalLog(1:count)/N, ...
692 | Used, Mu./BasisScales(Used)', SIGMA,...
693 | Alpha, beta, Gamma, PHI.*(ones(N,1)*BasisScales(Used)),...
694 | OPTIONS.callbackData{:})
695 | end
696 |
697 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698 |
699 | % END OF CYCLE PROCESSING
700 | %
701 | % Check if termination still specified, and output diagnostics
702 | %
703 | if (selectedAction==ACTION_TERMINATE)
704 | %
705 | % If we're here, then no update to the model was considered worthwhile
706 | %
707 | SB2_Diagnostic(OPTIONS,2,...
708 | '** Stopping at iteration %d (Max_delta_ml=%g) **\n', ...
709 | i, deltaLogMarginal);
710 | if LIKELIHOOD.InUse~=LIKELIHOOD.Gaussian
711 | SB2_Diagnostic(OPTIONS,2,'%4d> L = %.6f\t Gamma = %.2f (M = %d)\n',...
712 | i, logML/N, sum(Gamma), M);
713 | else
714 | SB2_Diagnostic(OPTIONS,2,...
715 | '%4d> L = %.6f\t Gamma = %.2f (M = %d)\t s=%.3f\n',...
716 | i, logML/N, sum(Gamma), M, sqrt(1/beta));
717 | end
718 | % Exit the main loop
719 | break;
720 | end
721 | %
722 | % Check for "natural" termination
723 | %
724 | ITERATION_LIMIT = (i==OPTIONS.iterations);
725 | TIME_LIMIT = (etime(clock,t0)>OPTIONS.time);
726 | LAST_ITERATION = ITERATION_LIMIT || TIME_LIMIT;
727 | %
728 | if (OPTIONS.monitor && ~rem(i,OPTIONS.monitor)) || LAST_ITERATION
729 | %
730 | % Output diagnostic progress info if desired
731 | %
732 | if LIKELIHOOD.InUse~=LIKELIHOOD.Gaussian
733 | SB2_Diagnostic(OPTIONS,2,'%5d> L = %.6f\t Gamma = %.2f (M = %d)\n',...
734 | i, logML/N, sum(Gamma), M);
735 | else
736 | SB2_Diagnostic(OPTIONS,2,...
737 | '%5d> L = %.6f\t Gamma = %.2f (M = %d)\t s=%.3f \n',...
738 | i, logML/N, sum(Gamma), M, sqrt(1/beta));
739 | end
740 | end
741 |
742 | end % of MAIN LOOP
743 |
744 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745 |
746 | %%
747 | %% POST-PROCESSING
748 | %%
749 |
750 | %
751 | % Warn if we exited the main loop without terminating automatically
752 | %
753 | if (selectedAction~=ACTION_TERMINATE)
754 | %
755 | if ITERATION_LIMIT
756 | SB2_Diagnostic(OPTIONS,1,...
757 | '** Iteration limit: algorithm did not converge\n');
758 | elseif TIME_LIMIT
759 | SB2_Diagnostic(OPTIONS,1,...
760 | '** Time limit: algorithm did not converge\n');
761 | end
762 | end
763 | %
764 | % Output action summary if desired
765 | %
766 | if OPTIONS.diagnosticLevel>1
767 | % Stop timer
768 | t1 = etime(clock,t0);
769 | total = addCount + deleteCount + updateCount;
770 | if CONTROLS.BasisAlignmentTest
771 | total = total+alignDeferCount;
772 | end
773 | %
774 | SB2_Diagnostic(OPTIONS,2,'Action Summary\n');
775 | SB2_Diagnostic(OPTIONS,2,'==============\n');
776 | SB2_Diagnostic(OPTIONS,2,'Added\t\t%6d (%.0f%%)\n',...
777 | addCount, 100*addCount/total);
778 | SB2_Diagnostic(OPTIONS,2,'Deleted\t\t%6d (%.0f%%)\n',...
779 | deleteCount, 100*deleteCount/total);
780 | SB2_Diagnostic(OPTIONS,2,'Reestimated\t%6d (%.0f%%)\n',...
781 | updateCount, 100*updateCount/total);
782 | %
783 | if CONTROLS.BasisAlignmentTest && alignDeferCount
784 | SB2_Diagnostic(OPTIONS,2,'--------------\n');
785 | SB2_Diagnostic(OPTIONS,2,'Deferred\t%6d (%.0f%%)\n',...
786 | alignDeferCount, 100*alignDeferCount/total);
787 | end
788 | %
789 | SB2_Diagnostic(OPTIONS,2,'==============\n');
790 | SB2_Diagnostic(OPTIONS,2,'Total of %d likelihood updates\n', count);
791 | SB2_Diagnostic(OPTIONS,2,'Time to run: %s\n', SB2_FormatTime(t1));
792 | end
793 |
794 | % Terminate diagnostics
795 | OPTIONS = SB2_Diagnostic(OPTIONS, 'end');
796 |
797 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798 |
799 | %%
800 | %% OUTPUT VARIABLES
801 | %%
802 | if nargout>=1
803 | % We also choose to sort here - it can't hurt and may help later
804 | [PARAMETER.Relevant, index] = sort(Used);
805 | % Not forgetting to correct for normalisation too
806 | PARAMETER.Value = Mu(index) ./ BasisScales(Used(index))';
807 | if nargout>=2
808 | %
809 | HYPERPARAMETER.Alpha = Alpha(index)./(BasisScales(Used(index))'.^2);
810 | HYPERPARAMETER.beta = beta;
811 | if nargout>=3
812 | %
813 | DIAGNOSTIC.Gamma = Gamma(index);
814 | DIAGNOSTIC.Likelihood = logMarginalLog(1:count);
815 | DIAGNOSTIC.iterations = i;
816 | DIAGNOSTIC.S_Factor = S_out;
817 | DIAGNOSTIC.Q_Factor = Q_out;
818 | DIAGNOSTIC.M_full = M_full;
819 | end
820 | end
821 | end
822 |
823 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824 |
--------------------------------------------------------------------------------
/SB2_Release_200/SparseBayesDemo.m:
--------------------------------------------------------------------------------
1 | % SPARSEBAYESDEMO Simple demonstration of the SPARSEBAYES algorithm
2 | %
3 | % SPARSEBAYESDEMO(LIKELIHOOD, DIMENSION, NOISETOSIGNAL)
4 | %
5 | % OUTPUT ARGUMENTS: None
6 | %
7 | % INPUT ARGUMENTS:
8 | %
9 | % LIKELIHOOD Text string, one of 'Gaussian' or 'Bernoulli'
10 | % DIMENSION Integer, 1 or 2
11 | % NOISETOSIGNAL An optional positive number to specify the
12 | % noise-to-signal (standard deviation) fraction.
13 | % (Optional: default value is 0.2).
14 | %
15 | % EXAMPLES:
16 | %
17 | % SPARSEBAYESDEMO("Bernoulli",2)
18 | % SPARSEBAYESDEMO("Gaussian",1,0.5)
19 | %
20 | % NOTES:
21 | %
22 | % This program offers a simple demonstration of how to use the
23 | % SPARSEBAYES (V2) Matlab software.
24 | %
25 | % Synthetic data is generated from an underlying linear model based
26 | % on a set of "Gaussian" basis functions, with the generator being
27 | % "sparse" such that 10% of potential weights are non-zero. Data may be
28 | % generated in an input space of one or two dimensions.
29 | %
30 | % This generator is then used either as the basis for real-valued data with
31 | % additive Gaussian noise (whose level may be varied), or for binary
32 | % class-labelled data based on probabilities given by a sigmoid link
33 | % function.
34 | %
35 | % The SPARSEBAYES algorithm is then run on the data, and results and
36 | % diagnostic information are graphed.
37 | %
38 |
39 | %
40 | % Copyright 2009, Vector Anomaly Ltd
41 | %
42 | % This file is part of the SPARSEBAYES library for Matlab (V2.0).
43 | %
44 | % SPARSEBAYES is free software; you can redistribute it and/or modify it
45 | % under the terms of the GNU General Public License as published by the Free
46 | % Software Foundation; either version 2 of the License, or (at your option)
47 | % any later version.
48 | %
49 | % SPARSEBAYES is distributed in the hope that it will be useful, but WITHOUT
50 | % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
51 | % FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
52 | % more details.
53 | %
54 | % You should have received a copy of the GNU General Public License along
55 | % with SPARSEBAYES in the accompanying file "licence.txt"; if not, write to
56 | % the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
57 | % MA 02110-1301 USA
58 | %
59 | % Contact the author: m a i l [at] m i k e t i p p i n g . c o m
60 | %
61 | function SparseBayesDemo(likelihood_, dimension, noiseToSignal)
62 |
63 | % Fix the random seed for reproducibility of results
64 | %
65 | rseed = 1;
66 | rand('state',rseed);
67 | randn('state',rseed);
68 |
69 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70 | %
71 | % --- VALIDATE INPUT ARGUMENTS ---
72 | %
73 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 | %
75 | % (1) likelihood
76 | %
77 | LIKELIHOOD = SB2_Likelihoods(likelihood_);
78 | %
79 | % (2) dimension
80 | %
81 | if dimension~=1 && dimension~=2
82 | error('Specified dimension should be 1 or 2')
83 | end
84 | %
85 | % Set up default for "noiseToSignal" variable.
86 | % For ease of use, we'll just ignore it in the case of a non-Gaussian
87 | % likelihood model.
88 | %
89 | if ~exist('noiseToSignal','var')
90 | noiseToSignal = 0.2;
91 | end
92 |
93 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 | %
95 | % --- SET UP DEMO PARAMETERS ---
96 | %
97 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98 | %
99 | % Experiment with these values to vary the demo if you wish
100 | %
101 | if dimension==1
102 | N = 100; % Number of points
103 | else
104 | N = 900; % Gives a nice square grid of decent size
105 | end
106 | %
107 | basisWidth = 0.05; % NB: data is in [0,1]
108 | %
109 | % Define probability of a basis function NOT being used by the generative
110 | % model. i.e. if pSparse=0.90, only 10% of basis functions (on average) will
111 | % be used to synthesise the data.
112 | %
113 | pSparse = 0.90;
114 | iterations = 500;
115 | %
116 | % Heuristically adjust basis width to account for
117 | % distance scaling with dimension.
118 | %
119 | basisWidth = basisWidth^(1/dimension);
120 | %
121 |
122 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123 | %
124 | % --- SYNTHETIC DATA GENERATION ---
125 | %
126 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 | %
128 | % First define the input data over a regular grid
129 | %
130 | if dimension==1
131 | X = [0:N-1]'/N;
132 | else
133 | % dimension is 2
134 | sqrtN = floor(sqrt(N));
135 | N = sqrtN*sqrtN;
136 | x = [0:sqrtN-1]'/sqrtN;
137 | [gx gy] = meshgrid(x);
138 | X = [gx(:) gy(:)];
139 | end
140 | %
141 | % Now define the basis
142 | %
143 | % Locate basis functions at data points
144 | %
145 | C = X;
146 | %
147 | % Compute ("Gaussian") basis (design) matrix
148 | %
149 | BASIS = exp(-distSquared(X,C)/(basisWidth^2));
150 | %
151 | %
152 | % Randomise some weights, then make each weight sparse with probability
153 | % pSparse
154 | %
155 | M = size(BASIS,2);
156 | w = randn(M,1)*100 / (M*(1-pSparse));
157 | sparse = rand(M,1)0.5);
261 | end
262 | %
263 |
264 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265 | %
266 | % --- PLOT THE RESULTS ---
267 | %
268 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269 | %
270 | % Likelihood trace (and Gaussian noise info)
271 | %
272 | subplot(fRows,fCols,SP_LIKELY)
273 | lsteps = length(DIAGNOSTIC.Likelihood);
274 | plot(1:lsteps, DIAGNOSTIC.Likelihood, 'g-')
275 | set(gca,'Xlim',[0 lsteps+1])
276 | title('Log marginal likelihood trace','FontSize',TITLE_SIZE)
277 | if LIKELIHOOD.InUse==LIKELIHOOD.Gaussian
278 | ax = axis;
279 | dx = ax(2)-ax(1);
280 | dy = ax(4)-ax(3);
281 | t_ = sprintf('Actual noise: %.5f', noise);
282 | text(ax(1)+0.1*dx,ax(3)+0.6*dy,t_,'FontName','Courier')
283 | t_ = sprintf('Inferred noise: %.5f', 1/sqrt(HYPERPARAMETER.beta));
284 | text(ax(1)+0.1*dx,ax(3)+0.5*dy,t_,'FontName','Courier')
285 | end
286 | %
287 | % Compare the generative and predictive linear models
288 | %
289 | subplot(fRows,fCols,SP_LINEAR)
290 | if dimension==1
291 | plot(X,z,'w-','linewidth',4);
292 | hold on
293 | plot(X,y,'r-','linewidth',3);
294 | hold off
295 | else
296 | mesh(gx,gy,reshape(z,size(gx)),'edgecolor','w','facecolor','w')
297 | hold on
298 | mesh(gx,gy,reshape(y,size(gx)),'edgecolor','r','facecolor','r')
299 | hold off
300 | light
301 | end
302 | title('Generative function and linear model','FontSize',TITLE_SIZE)
303 | legend('Actual','Model','Location','NorthWest')
304 | legend('boxoff')
305 | %
306 | % Compare the data and the predictive model (post link-function)
307 | %
308 | subplot(fRows,fCols,SP_COMPARE)
309 | if dimension==1
310 | plot(X,Outputs,'w.','linewidth',4);
311 | hold on
312 | plot(X,y_l,'r-','linewidth',3);
313 | plot(X(PARAMETER.Relevant),Outputs(PARAMETER.Relevant),'yo')
314 | hold off
315 | else
316 | plot3(X(:,1),X(:,2),Outputs,'w.')
317 | hold on
318 | mesh(gx,gy,reshape(y_l,size(gx)),'edgecolor','r','facecolor','r')
319 | hold off
320 | light
321 | end
322 | title('Data and predictor','FontSize',TITLE_SIZE)
323 | %
324 | % Show the inferred weights
325 | %
326 | subplot(fRows,fCols,SP_WEIGHTS)
327 | h = stem(w_infer,'filled');
328 | set(h,'Markersize',3,'Color','r')
329 | set(gca,'Xlim',[0 N+1])
330 | t_ = sprintf('Inferred weights (%d)', length(PARAMETER.Relevant));
331 | title(t_,'FontSize',TITLE_SIZE)
332 | %
333 | % Show the "well-determinedness" factors
334 | %
335 | subplot(fRows,fCols,SP_GAMMA)
336 | bar(DIAGNOSTIC.Gamma,'g')
337 | axis([0 length(PARAMETER.Relevant)+1 0 1.1])
338 | title('Well-determinedness (gamma)','FontSize',TITLE_SIZE)
339 |
340 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341 | %
342 | % Support function to compute basis
343 | %
344 | function D2 = distSquared(X,Y)
345 | %
346 | nx = size(X,1);
347 | ny = size(Y,1);
348 | %
349 | D2 = (sum((X.^2), 2) * ones(1,ny)) + (ones(nx, 1) * sum((Y.^2),2)') - ...
350 | 2*X*Y';
351 |
352 |
--------------------------------------------------------------------------------
/SB2_Release_200/licence.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
--------------------------------------------------------------------------------
/custom_option.m:
--------------------------------------------------------------------------------
1 | %% custom optimization option
2 | %{
3 | opt.method = 'bayes'; % bayes, ga, pso
4 | opt.display = 'on';
5 | opt.iteration = 20;
6 | opt.point = 10;
7 |
8 | % gaussian kernel function
9 | opt.gaussian.parameterName = {'gamma'};
10 | opt.gaussian.parameterType = {'real'};
11 | opt.gaussian.lowerBound = 2^-6;
12 | opt.gaussian.upperBound = 2^6;
13 |
14 | % laplacian kernel function
15 | opt.laplacian.parameterName = {'gamma'};
16 | opt.laplacian.parameterType = {'real'};
17 | opt.laplacian.lowerBound = 2^-6;
18 | opt.laplacian.upperBound = 2^6;
19 |
20 | % polynomial kernel function
21 | opt.polynomial.parameterName = {'gamma'; 'offset'; 'degree'};
22 | opt.polynomial.parameterType = {'real'; 'real'; 'integer'};
23 | opt.polynomial.lowerBound = [2^-6; 2^-6; 1];
24 | opt.polynomial.upperBound = [2^6; 2^6; 7];
25 |
26 | % sigmoid kernel function
27 | opt.sigmoid.parameterName = {'gamma'; 'offset'};
28 | opt.sigmoid.parameterType = {'real'; 'real'};
29 | opt.sigmoid.lowerBound = [2^-6; 2^-6];
30 | opt.sigmoid.upperBound = [2^6; 2^6];
31 | %}
32 |
33 | %% RVM model parameter
34 | %{
35 | 'display' : 'on', 'off'
36 | 'type' : 'RVR', 'RVC'
37 | 'kernelFunc' : kernel function
38 | 'KFolds' : cross validation, for example, 5
39 | 'HoldOut' : cross validation, for example, 0.3
40 | 'freeBasis' : 'on', 'off'
41 | 'maxIter' : max iteration, for example, 1000
42 | %}
43 |
--------------------------------------------------------------------------------
/data/UCI_data.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iqiukp/RVM-MATLAB/fd70db51f1333525ad4603a35b0c97ccf6ba68ba/data/UCI_data.mat
--------------------------------------------------------------------------------
/data/sinc_data.mat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iqiukp/RVM-MATLAB/fd70db51f1333525ad4603a35b0c97ccf6ba68ba/data/sinc_data.mat
--------------------------------------------------------------------------------
/demo_RVC.m:
--------------------------------------------------------------------------------
1 | %{
2 | A demo for classification using RVM
3 | %}
4 |
5 | clc
6 | clear all
7 | close all
8 | addpath(genpath(pwd))
9 |
10 | % use fisheriris dataset
11 | load fisheriris
12 | inds = ~strcmp(species, 'setosa');
13 | data_ = meas(inds, 3:4);
14 | label_ = species(inds);
15 | cvIndices = crossvalind('HoldOut', length(data_), 0.3);
16 | trainData = data_(cvIndices, :);
17 | trainLabel = label_(cvIndices, :);
18 | testData = data_(~cvIndices, :);
19 | testLabel = label_(~cvIndices, :);
20 |
21 | % kernel function
22 | kernel = Kernel('type', 'gaussian', 'gamma', 0.2);
23 |
24 | % parameter
25 | parameter = struct( 'display', 'on',...
26 | 'type', 'RVC',...
27 | 'kernelFunc', kernel);
28 | rvm = BaseRVM(parameter);
29 |
30 | % RVM model training, testing, and visualization
31 | rvm.train(trainData, trainLabel);
32 | results = rvm.test(testData, testLabel);
33 | rvm.draw(results)
--------------------------------------------------------------------------------
/demo_RVR.m:
--------------------------------------------------------------------------------
1 | %{
2 | A demo for regression using RVM
3 | %}
4 |
5 | clc
6 | clear all
7 | close all
8 | addpath(genpath(pwd))
9 |
10 | % sinc funciton
11 | load sinc_data
12 | trainData = x;
13 | trainLabel = y;
14 | testData = xt;
15 | testLabel = yt;
16 |
17 | % kernel function
18 | kernel = Kernel('type', 'gaussian', 'gamma', 0.1);
19 |
20 | % parameter
21 | parameter = struct( 'display', 'on',...
22 | 'type', 'RVR',...
23 | 'kernelFunc', kernel);
24 | rvm = BaseRVM(parameter);
25 |
26 | % RVM model training, testing, and visualization
27 | rvm.train(trainData, trainLabel);
28 | results = rvm.test(testData, testLabel);
29 | rvm.draw(results)
30 |
31 |
32 |
--------------------------------------------------------------------------------
/demo_hybrid_kernel.m:
--------------------------------------------------------------------------------
1 | %{
2 | A demo for regression using RVM with hybrid_kernel
3 | %}
4 |
5 | clc
6 | clear all
7 | close all
8 | addpath(genpath(pwd))
9 |
10 | % sinc funciton
11 | load sinc_data
12 | trainData = x;
13 | trainLabel = y;
14 | testData = xt;
15 | testLabel = yt;
16 |
17 | % kernel function
18 | kernel_1 = Kernel('type', 'gaussian', 'gamma', 0.3);
19 | kernel_2 = Kernel('type', 'polynomial', 'degree', 2);
20 | kernelWeight = [0.5, 0.5];
21 | % parameter
22 | parameter = struct( 'display', 'on',...
23 | 'type', 'RVR',...
24 | 'kernelFunc', [kernel_1, kernel_2],...
25 | 'kernelWeight', kernelWeight);
26 | rvm = BaseRVM(parameter);
27 |
28 | % RVM model training, testing, and visualization
29 | rvm.train(trainData, trainLabel);
30 | results = rvm.test(testData, testLabel);
31 | rvm.draw(results)
32 |
33 |
34 |
--------------------------------------------------------------------------------
/demo_hybrid_kernel_optimization.m:
--------------------------------------------------------------------------------
1 | %{
2 | A demo for hybrid-kernel RVM model with Parameter Optimization
3 | %}
4 |
5 |
6 | clc
7 | clear all
8 | close all
9 | addpath(genpath(pwd))
10 |
11 | % data
12 | load UCI_data
13 | trainData = x;
14 | trainLabel = y;
15 | testData = xt;
16 | testLabel = yt;
17 |
18 | % kernel function
19 | kernel_1 = Kernel('type', 'gaussian', 'gamma', 0.5);
20 | kernel_2 = Kernel('type', 'polynomial', 'degree', 2);
21 |
22 | % parameter optimization
23 | opt.method = 'bayes'; % bayes, ga, pso
24 | opt.display = 'on';
25 | opt.iteration = 30;
26 |
27 | % parameter
28 | parameter = struct( 'display', 'on',...
29 | 'type', 'RVR',...
30 | 'kernelFunc', [kernel_1, kernel_2],...
31 | 'optimization', opt);
32 | rvm = BaseRVM(parameter);
33 |
34 | % RVM model training, testing, and visualization
35 | rvm.train(trainData, trainLabel);
36 | results = rvm.test(testData, testLabel);
37 | rvm.draw(results)
--------------------------------------------------------------------------------
/demo_kernel_function.m:
--------------------------------------------------------------------------------
1 | %{
2 | Demonstration of computing the kernel function matrix.
3 | %}
4 |
5 | clc
6 | clear all
7 | close all
8 | addpath(genpath(pwd))
9 |
10 | % data
11 | X = rand(33, 2);
12 | Y = rand(23, 2);
13 |
14 | % kernel setting
15 | kernel1 = Kernel('type', 'gaussian', 'gamma', 0.01);
16 | kernel2 = Kernel('type', 'polynomial', 'degree', 3);
17 | kernel3 = Kernel('type', 'linear');
18 | kernel4 = Kernel('type', 'sigmoid', 'gamma', 0.01);
19 | kernel5 = Kernel('type', 'laplacian', 'gamma', 0.1);
20 |
21 | % compute the kernel function matrix
22 | matrix{1} = kernel1.computeMatrix(X, Y);
23 | matrix{2} = kernel2.computeMatrix(X, Y);
24 | matrix{3} = kernel3.computeMatrix(X, Y);
25 | matrix{4} = kernel4.computeMatrix(X, Y);
26 | matrix{5} = kernel5.computeMatrix(X, Y);
--------------------------------------------------------------------------------
/demo_optimization.m:
--------------------------------------------------------------------------------
1 | %{
2 | A demo for RVM model with Parameter Optimization
3 | %}
4 |
5 | clc
6 | clear all
7 | close all
8 | addpath(genpath(pwd))
9 |
10 | % use fisheriris dataset
11 | load fisheriris
12 | inds = ~strcmp(species, 'setosa');
13 | data_ = meas(inds, 3:4);
14 | label_ = species(inds);
15 | cvIndices = crossvalind('HoldOut', length(data_), 0.3);
16 | trainData = data_(cvIndices, :);
17 | trainLabel = label_(cvIndices, :);
18 | testData = data_(~cvIndices, :);
19 | testLabel = label_(~cvIndices, :);
20 |
21 | % kernel function
22 | kernel = Kernel('type', 'gaussian', 'gamma', 5);
23 |
24 | % parameter optimization
25 | opt.method = 'bayes'; % bayes, ga, pso
26 | opt.display = 'on';
27 | opt.iteration = 20;
28 |
29 | % parameter
30 | parameter = struct( 'display', 'on',...
31 | 'type', 'RVC',...
32 | 'kernelFunc', kernel,...
33 | 'optimization', opt);
34 | rvm = BaseRVM(parameter);
35 |
36 | % RVM model training, testing, and visualization
37 | rvm.train(trainData, trainLabel);
38 | results = rvm.test(testData, testLabel);
39 | rvm.draw(results)
40 |
41 |
42 |
--------------------------------------------------------------------------------