faif
Svm.hpp
Go to the documentation of this file.
1 /**
2  * \file Svm.hpp
3  * \brief The Support Vector Machine Classifier
4  */
5 
6 #ifndef FAIF_SVM_HPP_
7 #define FAIF_SVM_HPP_
8 
9 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
10 //msvc14.0 warnings for Boost.Serialization
11 #pragma warning(disable:4100)
12 #pragma warning(disable:4512)
13 #endif
14 
15 
16 #include <boost/numeric/ublas/vector.hpp>
17 #include <boost/numeric/ublas/io.hpp>
18 #include <boost/random/mersenne_twister.hpp>
19 #include <boost/random/uniform_int_distribution.hpp>
20 #include <vector>
21 #include <utility>
22 #include <math.h>
23 #include <ctime>
24 
25 #include <boost/serialization/nvp.hpp>
26 #include <boost/serialization/split_member.hpp>
27 #include <boost/serialization/vector.hpp>
28 #include <boost/serialization/utility.hpp>
29 
30 #include "../Value.hpp"
31 #include "../Point.hpp"
32 #include "../utils/Random.hpp"
33 #include "Belief.hpp"
34 
35 namespace faif {
36  namespace ml {
37 
38  /** Support vector machine classifier class */
39  template <typename Val, typename DomainVal>
40  class SvmClassifier {
41 
42  public:
43 
44  /** \brief serialization using boost::serialization */
46 
47  template<class Archive>
48  void serialize( Archive &ar, const unsigned int file_version ){
49  boost::serialization::split_member(ar, *this, file_version);
50  }
51 
52  template<class Archive>
53  void save(Archive & ar, const unsigned int /* file_version */) const {
54  ar & boost::serialization::make_nvp("Dimension", dimension );
55  ar & boost::serialization::make_nvp("TrainingExamples", trainingExamples );
56  ar & boost::serialization::make_nvp("Smo", smo );
57  ar & boost::serialization::make_nvp("Categories", categories);
58 
59  }
60 
61  template<class Archive>
62  void load(Archive & ar, const unsigned int /* file_version */) {
63  ar >> boost::serialization::make_nvp("Dimension", dimension );
64  ar >> boost::serialization::make_nvp("TrainingExamples", trainingExamples );
65  ar >> boost::serialization::make_nvp("Smo", smo );
66  ar >> boost::serialization::make_nvp("Categories", categories);
67  }
68 
69  /** Classify example is n-dimensional vector */
70  typedef typename std::vector<Val> ClassifyExample;
71 
72  /** The attribute domain for learning */
73  typedef typename DomainVal::DomainType AttrDomain;
74 
75  /** Attribute value representation in learning */
76  typedef typename DomainVal::Value AttrValue;
77 
78  /** Collection of pair (AttrIdd, Probability) */
79  typedef typename Belief<DomainVal>::Beliefs Beliefs;
80 
81  private:
82 
83  /** Dimension is the number of attributes used at classification, i.e. dimension of train/classify vectors */
84  size_t dimension;
85 
86  /** Sequential Minimal Optimization algorithm */
87  class SmoAlgorithm {
88 
89  public:
90 
91  /** Classify example is n-dimensional real vector */
92  typedef typename boost::numeric::ublas::vector<Val> ClassifyExampleSmo;
93 
94  /** Train example is pair of category (1 or -1) and n-dimensional vector */
95  typedef typename std::pair < int, ClassifyExampleSmo > TrainExampleSmo;
96 
97  enum Kernel_type{
98  default_type,
99  gauss_type,
100  linear_type,
101  polynomial_type,
102  hyperbolic_tangent_type
103  };
104 
105  friend class SvmClassifier<Val, DomainVal>;
106 
107  private:
108 
109  /** \brief serialization using boost::serialization */
110  friend class boost::serialization::access;
111 
112  /** Serialize function */
113  template<class Archive>
114  void serialize( Archive &ar, const unsigned int file_version ){
115  boost::serialization::split_member(ar, *this, file_version);
116  }
117 
118  /** Serialize function */
119  template<class Archive>
120  void save(Archive & ar, const unsigned int /* file_version */) const {
121  ar & boost::serialization::make_nvp("C", C );
122  ar & boost::serialization::make_nvp("margin", margin );
123  ar & boost::serialization::make_nvp("epsilon", epsilon );
124  ar & boost::serialization::make_nvp("b", b );
125  ar & boost::serialization::make_nvp("delta_b", delta_b );
126  ar & boost::serialization::make_nvp("gaussParameter", gaussParameter );
127  ar & boost::serialization::make_nvp("polynomialInhomogeneousParameter", polynomialInhomogeneousParameter );
128  ar & boost::serialization::make_nvp("polynomialDegree", polynomialDegree );
129  ar & boost::serialization::make_nvp("tangentFrequency", tangentFrequency );
130  ar & boost::serialization::make_nvp("tangentShift", tangentShift );
131  ar & boost::serialization::make_nvp("sigmoidScaleFactor", sigmoidScaleFactor );
132  ar & boost::serialization::make_nvp("finiteStopCondition", finiteStopCondition );
133  ar & boost::serialization::make_nvp("stepsStopCondition", stepsStopCondition );
134  ar & boost::serialization::make_nvp("alpha", alpha );
135  ar & boost::serialization::make_nvp("error_cache", error_cache );
136  ar & boost::serialization::make_nvp("trainingExamples", trainingExamples );
137  ar & boost::serialization::make_nvp("kernelType", kernel_type );
138  }
139 
140  /** Serialize function */
141  template<class Archive>
142  void load(Archive & ar, const unsigned int /* file_version */) {
143  ar >> boost::serialization::make_nvp("C", C );
144  ar >> boost::serialization::make_nvp("margin", margin );
145  ar >> boost::serialization::make_nvp("epsilon", epsilon );
146  ar >> boost::serialization::make_nvp("b", b );
147  ar >> boost::serialization::make_nvp("delta_b", delta_b );
148  ar >> boost::serialization::make_nvp("gaussParameter", gaussParameter );
149  ar >> boost::serialization::make_nvp("polynomialInhomogeneousParameter", polynomialInhomogeneousParameter );
150  ar >> boost::serialization::make_nvp("polynomialDegree", polynomialDegree );
151  ar >> boost::serialization::make_nvp("tangentFrequency", tangentFrequency );
152  ar >> boost::serialization::make_nvp("tangentShift", tangentShift );
153  ar >> boost::serialization::make_nvp("sigmoidScaleFactor", sigmoidScaleFactor );
154  ar >> boost::serialization::make_nvp("finiteStopCondition", finiteStopCondition );
155  ar >> boost::serialization::make_nvp("stepsStopCondition", stepsStopCondition );
156  ar >> boost::serialization::make_nvp("alpha", alpha );
157  ar >> boost::serialization::make_nvp("error_cache", error_cache );
158  ar >> boost::serialization::make_nvp("trainingExamples", trainingExamples );
159  ar >> boost::serialization::make_nvp("kernelType", kernel_type );
160  setKernel(kernel_type);
161  }
162 
163  /** Gauss kernel function */
164  Val kernel_gauss(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2);
165 
166  /** Linear kernel function */
167  Val kernel_linear(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2);
168 
169  /** Polynomial kernel function */
170  Val kernel_polynomial(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2);
171 
172  /** Tanh(x) kernel function */
173  Val kernel_hyperbolic_tangent(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2);
174 
175  /** Pointer to kernel function */
176  Val (faif::ml::SvmClassifier<Val, DomainVal>::SmoAlgorithm::*kernel) (const ClassifyExampleSmo&, const ClassifyExampleSmo&);
177 
178  /** Enum representing which kernel is used for SMO algorithm */
179  Kernel_type kernel_type;
180 
181  /** Setting kernel type */
182  void setKernel(Kernel_type);
183 
184  /** Sigmoid function, used for scaling classification result from ]-inf;inf[ to [0;1] */
185  Val sigmoid_function(Val);
186 
187  /** Lagrangian multipliers */
188  std::vector<Val> alpha;
189 
190  /** SVM threshold (hyperplane) */
191  Val b;
192 
193  /** b error */
194  Val delta_b;
195 
196  /** SVM parameter, additional constraint for soft margin */
197  Val C;
198 
199  /** Parameter used for stop condition (check if violates KKT condition)*/
200  Val margin;
201 
202  /** Parameter used for stop condition (check if violates KKT condition)*/
203  Val epsilon;
204 
205  /** Parameter of gaussian kernel function: exp(-gaussParameter*||x1-x2||)*/
206  Val gaussParameter;
207 
208  /** Parameter 'c' of polynomial kernel function: (x1.x2 + c)^d */
209  Val polynomialInhomogeneousParameter;
210 
211  /** Parameter 'd' of polynomial kernel function: (x1.x2 + c)^d */
212  Val polynomialDegree;
213 
214  /** Parameter 'w' of hyperbolic tangent kernel function: tanh(w*x1.x2 - c) */
215  Val tangentFrequency;
216 
217  /** Parameter 'c' of hyperbolic tangent kernel function: tanh(w*x1.x2 - c) */
218  Val tangentShift;
219 
220  /** If finiteStopConditon is true SMO algorithm stops after stepsStopCondition*numExamples steps */
221  bool finiteStopCondition;
222  double stepsStopCondition;
223 
224  /** Parameter 'p' of sigmoid function 1 / ( 1 + exp(-px) ), should be > 0*/
225  Val sigmoidScaleFactor;
226 
227  /** Cache error vector */
228  std::vector<Val> error_cache;
229 
230  /** Vector of all training examples */
231  std::vector< TrainExampleSmo > trainingExamples;
232 
233  /** Calculate current classification function for i-th training vector */
234  Val learned_func(size_t i);
235 
236  /** Optimizes Lagrangian multipliers i1 and i2, return 1 if succes, else return 0 */
237  size_t optimizeTwoAlphas(size_t i1, size_t i2);
238 
239  /** Checks KKT condition is violated by more than margin for alpha[i1]. If so, looks for 2nd multiplier and jointly optimize multipliers using optimizeTwoAlphas function */
240  size_t examineKKTViolation(size_t i1);
241 
242  /** Restrict copy */
243  SmoAlgorithm(const SmoAlgorithm& smo);
244  const SmoAlgorithm& operator=(const SmoAlgorithm& smo);
245 
246  /**C-tor, initialize parameters*/
247  SmoAlgorithm(Kernel_type _kernel_type = default_type) : kernel_type(_kernel_type), b(0.0), C(2.0), margin(0.001), epsilon(0.000000000001), gaussParameter(1.0), polynomialInhomogeneousParameter(0.0), polynomialDegree(2.0), tangentFrequency(1.0), tangentShift(0.0), finiteStopCondition(true), stepsStopCondition(1.0), sigmoidScaleFactor(0.1) {
248  // Initialize kernel function
249  setKernel(kernel_type);
250  }
251 
252  /** Use train example to train svm classifier */
253  void train(std::vector<typename SmoAlgorithm::TrainExampleSmo> _trainingExamples);
254 
255  /** Classify example (return positive or negative number) based on what have been trained */
256  Val classify(const ClassifyExampleSmo& vec);
257 
258  /** Erase all the added train examples added to svm classifier */
259  void reset();
260 
261  /** Set parameter C, should be >0 */
262  void setC(Val C);
263 
264  /** Set parameter margin, should be >0 */
265  void setMargin(Val margin);
266 
267  /** Set parameter epsilon, should be >0 */
268  void setEpsilon(Val epsilon);
269 
270  /** Set gaussian parameter t : exp(-t*||x1-x2||), should be >0 */
271  void setGaussParameter(Val gaussParameter);
272 
273  /** Set parameter 'c' of hyperbolic tangent kernel function: tanh(w*x1.x2 - c) */
274  void setPolynomialInhomogeneousParameter(Val polynomialInhomogeneousParameter);
275 
276  /** Set parameter 'd' of polynomial kernel function: (x1.x2 + c)^d */
277  void setPolynomialDegree(Val polynomialDegree);
278 
279  /** Set parameter 'w' of hyperbolic tangent kernel function: tanh(w*x1.x2 - c) , should be > 0 */
280  void setTangentFrequency(Val tangentFrequency);
281 
282  /** Set parameter 'c' of polynomial kernel function: (x1.x2 + c)^d , should be >= 0 */
283  void setTangentShift(Val tangentShift);
284 
285  /** Set stop condition for SMO algorithm: when N training examples, SMO stops after stop*N steps. stop should be >0 */
286  void setFiniteStepsStopCondition(double stop);
287 
288  /** Unset stop condition for SMO algorithm */
290 
291  /** Set parameter 'p' of sigmoid function: 1 / ( 1 + exp(-px) ) */
292  void setSigmoidScaleFactor(Val sigmoidScaleFactor);
293 
294  };//class SmoAlgorithm
295 
296  /** Categories used for examples labeling (svm assumption: two categories only)*/
297  AttrDomain categories;
298 
299  /** Instance of smo algorithm */
300  SmoAlgorithm smo;
301 
302  /** Vector of training vectors with corresponding category used for classifier training*/
303  std::vector< typename SmoAlgorithm::TrainExampleSmo > trainingExamples;
304 
305  /** Restrict copy */
306  SvmClassifier(const SvmClassifier& svm);
307  const SvmClassifier& operator=(const SvmClassifier& svm);
308 
309  /** Classify example (return positive or negative number) based on what have been trained */
310  Val classify(const ClassifyExample& vec);
311 
312  /** Convert std::vector with train/classify example to boost::numeric::ublas vector to achieve better time performance */
313  typename SmoAlgorithm::ClassifyExampleSmo convertVectorToUblas(const ClassifyExample&);
314 
315  public:
316 
317  /** Empty c-tor */
318  SvmClassifier() : dimension(0) {} ;
319 
320  /** C-tor creates svm classifier for given dimensionality of a problem */
321  SvmClassifier(size_t dimension_, const AttrDomain& category_domain) : dimension(dimension_) {
322  //Svm classifier works for two class problems only
323  if(category_domain.getSize()==2)
324  categories = category_domain;
325  else
326  throw std::domain_error("For SVM classifier category domain should have exactly 2 classes");
327  };
328 
329  /** Add train example with known category to svm classifier */
330  void addExample(const ClassifyExample& example, const AttrValue& category);
331 
332  /** Classify and return all classes (svm assumption: two classes) with belief that the example is from given class */
333  Beliefs getCategories(const ClassifyExample& vec);
334 
335  /** Classify and return the belief of the most probable class */
336  Belief<DomainVal> getCategory(const ClassifyExample& vec);
337 
338  /** Use train example to train svm classifier */
339  void train();
340 
341  /** Return dimensionality of a svm classifier */
342  size_t getDimension();
343 
344  /** Return the number of train examples added to svm classifier */
345  size_t countTrainExamples();
346 
347  /** Erase all the added train examples added to svm classifier */
348  void reset();
349 
350  /** Erase all the added train examples added to svm classifier and change dimension of classifier*/
351  void resetAndChangeDimension(size_t);
352 
353  /** Set parameter C, should be >0 */
354  void setC(Val C);
355 
356  /** Set parameter margin, should be >0 */
357  void setMargin(Val margin);
358 
359  /** Set parameter epsilon, should be >0 */
360  void setEpsilon(Val epsilon);
361 
362  /** Set gaussian parameter t : exp(-t*||x1-x2||), should be >0 */
363  void setGaussParameter(Val gaussParameter);
364 
365  /** Set parameter 'c' of hyperbolic tangent kernel function: tanh(w*x1.x2 - c) */
366  void setPolynomialInhomogeneousParameter(Val polynomialInhomogeneousParameter);
367 
368  /** Set parameter 'd' of polynomial kernel function: (x1.x2 + c)^d */
369  void setPolynomialDegree(Val polynomialDegree);
370 
371  /** Set parameter 'w' of hyperbolic tangent kernel function: tanh(w*x1.x2 - c) , should be > 0 */
372  void setTangentFrequency(Val tangentFrequency);
373 
374  /** Set parameter 'c' of polynomial kernel function: (x1.x2 + c)^d , should be >= 0 */
375  void setTangentShift(Val tangentShift);
376 
377  /** Set stop condition for SMO algorithm: when N training examples, SMO stops after stop*N steps. stop should be >0 */
378  void setFiniteStepsStopCondition(double stop);
379 
380  /** Unset stop condition for SMO algorithm */
382 
383  /** Set parameter 'p' of sigmoid function: 1 / ( 1 + exp(-px) ), should be > 0 */
384  void setSigmoidScaleFactor(Val sigmoidScaleFactor);
385 
386  /** Set linear kernel for SMO algorithm */
387  void setLinearKernel();
388 
389  /** Set gaussian kernel for SMO algorithm */
390  void setGaussKernel();
391 
392  /** Set polynomial kernel for SMO algorithm */
393  void setPolynomialKernel();
394 
395  /** Set hyperbolic tangent kernel for SMO algorithm */
397 
398  }; //class SvmClassifier
399 
400 
401 
402 //////////////////////////////////////////
403 /* SvmClassifier Implementation */
404 //////////////////////////////////////////
405 
406  template <typename Val, typename DomainVal>
407  typename SvmClassifier<Val, DomainVal>::SmoAlgorithm::ClassifyExampleSmo SvmClassifier<Val, DomainVal>::convertVectorToUblas(const ClassifyExample& vec) {
408  typename SmoAlgorithm::ClassifyExampleSmo toRet(vec.size());
409  for(size_t i=0; i<vec.size(); ++i)
410  toRet(i) = vec[i];
411  return toRet;
412  }
413 
414  template <typename Val, typename DomainVal>
416  //Convert example to ublas::vector and classify
417  return smo.classify( convertVectorToUblas( vec ) );
418  }
419 
420  template <typename Val, typename DomainVal>
422  try{
423  // Find category in categories, if not found then NotFoundException
424  categories.find(category);
425  if(example.size() == this->dimension){ /* Size of train example and dimensionality of svm must be equal */
426  // Svm assumption: two category classification
427  // Another assumption: first category correspond to svm class y_i = +1, second category to -1
428  // Convert example to ublas::vector when add to train examples
429  if(category==categories.begin()->get())
430  this->trainingExamples.push_back(std::make_pair(1, convertVectorToUblas(example) ));
431  else
432  this->trainingExamples.push_back(std::make_pair(-1, convertVectorToUblas(example) ));
433  }
434  }
435  catch(NotFoundException ex){//If example's category is unknown, the example is not added to train examples
436  throw std::domain_error("Train example's category: "+category+" is not in classifier's domain.");
437  }
438  }
439 
440  template <typename Val, typename DomainVal>
442  // Svm assumption: two category classification
443 
444  // Classify example, the classify_value is probability of first category
445  auto classify_value = this->classify(vec);
446 
447  Beliefs toRet;
448 
449  // Push the pair of the first category and classification result (probability) to returned Beliefs
450  auto it = categories.begin();
451  toRet.push_back(typename Beliefs::value_type(it->getDomain()->getValueId(it), classify_value));
452 
453  // Push the pair of the second category and 1.0-probability to returned Beliefs
454  ++it;
455  toRet.push_back(typename Beliefs::value_type(it->getDomain()->getValueId(it), 1.0-classify_value));
456 
457  // Returned sorted Beliefs
458  std::sort( toRet.begin(), toRet.end() );
459  return toRet;
460  }
461 
462  template <typename Val, typename DomainVal>
464  return getCategories(vec)[0];
465  }
466 
467  template <typename Val, typename DomainVal>
468  void SvmClassifier<Val, DomainVal>::train(){ smo.train(this->trainingExamples);}
469 
470  template <typename Val, typename DomainVal>
471  size_t SvmClassifier<Val, DomainVal>::getDimension(){ return this->dimension;}
472 
473  template <typename Val, typename DomainVal>
474  size_t SvmClassifier<Val, DomainVal>::countTrainExamples(){ return trainingExamples.size();}
475 
476  template <typename Val, typename DomainVal>
477  void SvmClassifier<Val, DomainVal>::reset() { trainingExamples.clear(); smo.reset();}
478 
479  template <typename Val, typename DomainVal>
480  void SvmClassifier<Val, DomainVal>::resetAndChangeDimension(size_t _dimension) { trainingExamples.clear(); smo.reset(); this->dimension = _dimension;}
481 
482  template <typename Val, typename DomainVal>
483  void SvmClassifier<Val, DomainVal>::setC(Val C){ smo.setC(C);}
484 
485  template <typename Val, typename DomainVal>
486  void SvmClassifier<Val, DomainVal>::setMargin(Val margin){ smo.setMargin(margin);}
487 
488  template <typename Val, typename DomainVal>
489  void SvmClassifier<Val, DomainVal>::setEpsilon(Val epsilon){ smo.setEpsilon(epsilon);}
490 
491  template <typename Val, typename DomainVal>
492  void SvmClassifier<Val, DomainVal>::setGaussParameter(Val gaussParameter){ smo.setGaussParameter(gaussParameter);}
493 
494  template <typename Val, typename DomainVal>
495  void SvmClassifier<Val, DomainVal>::setPolynomialInhomogeneousParameter(Val p){ smo.setPolynomialInhomogeneousParameter(p);}
496 
497  template <typename Val, typename DomainVal>
498  void SvmClassifier<Val, DomainVal>::setPolynomialDegree(Val polynomialDegree){smo.setPolynomialDegree(polynomialDegree);}
499 
500  template <typename Val, typename DomainVal>
501  void SvmClassifier<Val, DomainVal>::setTangentFrequency(Val f){ smo.setTangentFrequency(f);}
502 
503  template <typename Val, typename DomainVal>
504  void SvmClassifier<Val, DomainVal>::setTangentShift(Val tangentShift){ smo.setTangentShift(tangentShift);}
505 
506  template <typename Val, typename DomainVal>
507  void SvmClassifier<Val, DomainVal>::setFiniteStepsStopCondition(double stop){ smo.setFiniteStepsStopCondition(stop);}
508 
509  template <typename Val, typename DomainVal>
510  void SvmClassifier<Val, DomainVal>::unsetFiniteStepsStopCondition(){ smo.unsetFiniteStepsStopCondition();}
511 
512  template <typename Val, typename DomainVal>
513  void SvmClassifier<Val, DomainVal>::setSigmoidScaleFactor(Val sigmoidScaleFactor){ smo.setSigmoidScaleFactor(sigmoidScaleFactor);}
514 
515  template <typename Val, typename DomainVal>
516  void SvmClassifier<Val, DomainVal>::setLinearKernel(){ smo.setKernel(SmoAlgorithm::Kernel_type::linear_type);}
517 
518  template <typename Val, typename DomainVal>
520  smo.setKernel(SmoAlgorithm::Kernel_type::gauss_type);
521  }
522 
523  template <typename Val, typename DomainVal>
525  smo.setKernel(SmoAlgorithm::Kernel_type::polynomial_type);
526  }
527 
528  template <typename Val, typename DomainVal>
530  smo.setKernel(SmoAlgorithm::Kernel_type::hyperbolic_tangent_type);
531  }
532 
533 
534 //////////////////////////////////////////
535 /* SmoAlgorithm Implementation */
536 //////////////////////////////////////////
537 
538 
539  template <typename Val, typename DomainVal>
540  Val SvmClassifier<Val, DomainVal>::SmoAlgorithm::kernel_gauss(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2){
541  assert(vec1.size() == vec2.size());
542  Val squared_euclidean_distance = boost::numeric::ublas::sum(boost::numeric::ublas::element_prod(vec1-vec2, vec1-vec2));
543  return exp(-gaussParameter*squared_euclidean_distance);
544  }
545 
546  template <typename Val, typename DomainVal>
547  Val SvmClassifier<Val, DomainVal>::SmoAlgorithm::kernel_linear(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2){
548  assert(vec1.size() == vec2.size());
549  return boost::numeric::ublas::sum(boost::numeric::ublas::element_prod(vec1, vec2));
550  }
551 
552  template <typename Val, typename DomainVal>
553  Val SvmClassifier<Val, DomainVal>::SmoAlgorithm::kernel_polynomial(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2){
554  assert(vec1.size() == vec2.size());
555  return pow(kernel_linear(vec1, vec2) + this->polynomialInhomogeneousParameter, this->polynomialDegree);
556  }
557 
558  template <typename Val, typename DomainVal>
559  Val SvmClassifier<Val, DomainVal>::SmoAlgorithm::kernel_hyperbolic_tangent(const ClassifyExampleSmo& vec1, const ClassifyExampleSmo& vec2){
560  assert(vec1.size() == vec2.size());
561  return tanh(this->tangentFrequency * kernel_linear(vec1, vec2) - this->tangentShift);
562  }
563 
564  template <typename Val, typename DomainVal>
566  return 1.0 / (1.0 + exp( (-this->sigmoidScaleFactor) * x) );
567  }
568 
569  template <typename Val, typename DomainVal>
571  Val result = 0.0;
572  size_t numExamples = trainingExamples.size();
573  for(size_t j = 0; j<numExamples; ++j)
574  if(alpha[i]>0)
575  result += alpha[i]*trainingExamples[i].first*((this->*kernel)(trainingExamples[i].second, trainingExamples[j].second));
576  result -= b;
577  return result;
578  }
579 
580  template <typename Val, typename DomainVal>
581  void SvmClassifier<Val, DomainVal>::SmoAlgorithm::setKernel(Kernel_type kernel_type_in){
582  this->kernel_type = kernel_type_in;
583  switch(kernel_type){
584  case gauss_type:
586  break;
587  case linear_type:
589  break;
590  case polynomial_type:
592  break;
593  case hyperbolic_tangent_type:
595  break;
596  default:
598  break;
599  }
600  }
601 
602  template <typename Val, typename DomainVal>
604  if(i1 == i2)
605  return 0;
606 
607  //Compute E1, E2, y1, y2, alpha1_old, alpha2_old
608  Val alpha1_old = alpha[i1];
609  int y1 = this->trainingExamples[i1].first;
610  Val E1;
611  if(alpha1_old > 0 && alpha1_old < this->C)
612  E1 = this->error_cache[i1];
613  else
614  E1 = learned_func(i1) - y1;
615 
616  Val alpha2_old = this->alpha[i2];
617  int y2 = this->trainingExamples[i2].first;
618  Val E2;
619  if(alpha2_old > 0 && alpha2_old < this->C)
620  E2 = this->error_cache[i2];
621  else
622  E2 = learned_func(i2) - y2;
623 
624  int s = y1 * y2;
625 
626  //Compute L, H
627  Val L, H;
628  if(y1 == y2){
629  Val gamma = alpha1_old + alpha2_old;
630  if(gamma > this->C){
631  L = gamma - this->C;
632  H = this->C;
633  }
634  else{
635  L = 0;
636  H = gamma;
637  }
638  }
639  else{
640  Val gamma = alpha1_old - alpha2_old;
641  if(gamma > 0){
642  L = 0;
643  H = this->C-gamma;
644  }
645  else{
646  L = -gamma;
647  H = this->C;
648  }
649  }
650  //Some software use L>=H condition
651  if(L == H)
652  return 0;
653 
654  //Compute eta
655  Val k11, k22;
656  if(kernel_type==gauss_type || kernel_type==default_type){
657  k11 = k22 = 1.0;
658  }
659  else{
660  k11 = (this->*kernel)(this->trainingExamples[i1].second, this->trainingExamples[i1].second);
661  k22 = (this->*kernel)(this->trainingExamples[i2].second, this->trainingExamples[i2].second);
662  }
663  Val k12 = (this->*kernel)(this->trainingExamples[i1].second, this->trainingExamples[i2].second);
664  Val eta = 2*k12 - k11 - k22;
665  Val alpha2_new;
666  Val Lobj, Hobj;
667  if(eta < 0){
668  alpha2_new = alpha2_old + y2 * (E2 - E1) / eta;
669  if(alpha2_new < L)
670  alpha2_new = L;
671  else if(alpha2_new > H)
672  alpha2_new = H;
673  }
674  else{
675  //Compute Lobj, Hobj at alpha2_new = L, alpha2_new = H
676  Lobj = (eta/2) * L * L + L * ( y2 * (E1-E2) - eta * alpha2_old);
677  Hobj = (eta/2) * H * H + H * ( y2 * (E1-E2) - eta * alpha2_old);
678 
679  if(Lobj > Hobj + epsilon)
680  alpha2_new = L;
681  else if(Lobj < Hobj - epsilon)
682  alpha2_new = H;
683  else
684  alpha2_new = alpha2_old;
685  }
686 
687  if( ( (alpha2_new > alpha2_old) ? (alpha2_new - alpha2_old) : ( alpha2_old - alpha2_new ) ) < epsilon * (epsilon + alpha2_new + alpha2_old) )
688  return 0;
689 
690  Val alpha1_new = alpha1_old -s * (alpha2_new - alpha2_old);
691  if(alpha1_new < 0){
692  alpha2_new += s * alpha1_new;
693  alpha1_new = 0;
694  }
695  else if(alpha1_new > this->C){
696  alpha2_new += s * (alpha1_new - this->C);
697  alpha1_new = this->C;
698  }
699 
700  //Update threshold to reflect change in Lagrange multipliers
701  {
702  Val bnew;
703  if(alpha1_new > 0 && alpha1_new < this->C)
704  bnew = b + E1 + y1 * (alpha1_new - alpha1_old) * k11 + y2 * (alpha2_new - alpha2_old) * k12;
705  else{
706  if(alpha2_new > 0 && alpha2_new < this->C)
707  bnew = b + E2 + y1 * (alpha1_new - alpha1_old) * k12 + y2 * (alpha2_new - alpha2_old) * k22;
708  else{
709  Val b1 = b + E1 + y1 * (alpha1_new - alpha1_old) * k11 + y2 * (alpha2_new - alpha2_old) * k12;
710  Val b2 = b + E2 + y1 * (alpha1_new - alpha1_old) * k12 + y2 * (alpha2_new - alpha2_old) * k22;
711  bnew = (b1 + b2)/2;
712  }
713  }
714  this->delta_b = bnew - this->b;
715  this->b = bnew;
716  }
717 
718  //Update error cache with new multipliers
719  {
720  Val t1 = y1 * (alpha1_new - alpha1_old);
721  Val t2 = y2 * (alpha2_new - alpha2_old);
722  size_t numExamples = trainingExamples.size();
723  for(size_t i=0; i<numExamples; ++i)
724  if( alpha[i] > 0 && alpha[i] < this->C)
725  this->error_cache[i] += t1 * (this->*kernel)(trainingExamples[i1].second, trainingExamples[i].second) + t2 * (this->*kernel)(trainingExamples[i2].second, trainingExamples[i].second) - this->delta_b;
726  this->error_cache[i1] = 0.0;
727  this->error_cache[i2] = 0.0;
728  }
729 
730  alpha[i1] = alpha1_new;
731  alpha[i2] = alpha2_new;
732  return 1;
733  }
734 
735  template <typename Val, typename DomainVal>
737  Val y1 = this->trainingExamples[i1].first;
738  Val alpha1 = alpha[i1];
739  Val E1 = (alpha1 > 0 && alpha1 < this->C) ? (this->error_cache[i1]) : (learned_func(i1) - y1);
740  Val r1 = y1 * E1;
741  size_t numExamples = trainingExamples.size();
742  if((r1 < -(this->margin) && alpha1 < (this->C)) || (r1 > this->margin && alpha1 > 0)){
743  //Try argmax E1-E2
744  {
745  Val tmax = 0.0;
746  size_t i2 = 0;
747  for(size_t k = 0; k < numExamples; ++k)
748  if(alpha[k] > 0 && alpha[k] < C){
749  Val E2 = error_cache[k];
750  Val temp = (E1>E2) ? (E1-E2) : (E2-E1);
751  if(temp > tmax){
752  tmax = temp;
753  i2 = k;
754  }
755  }
756  if(tmax>0.0)
757  if(optimizeTwoAlphas(i1, i2))
758  return 1;
759  }
760  {
761  RandomInt rnd(0, static_cast<int>(numExamples)-1);
762 
763  // First try iterate through the non-bound examples
764  size_t j0 = static_cast<size_t>(rnd());
765  for(size_t j=j0; j < numExamples+j0; ++j){
766  size_t i2 = j%numExamples;
767  if(alpha[i2] > 0 && alpha[i2] < this->C)
768  if(optimizeTwoAlphas(i1, i2))
769  return 1;
770  }
771 
772  // After try iterate through the entire training set
773  j0 = static_cast<size_t>(rnd());
774  for(size_t j=j0; j < numExamples+j0; ++j){
775  size_t i2 = j%numExamples;
776  if(optimizeTwoAlphas(i1, i2))
777  return 1;
778  }
779  }
780  }
781  return 0;
782  }
783 
784  template <typename Val, typename DomainVal>
785  void SvmClassifier<Val, DomainVal>::SmoAlgorithm::train(std::vector<typename SmoAlgorithm::TrainExampleSmo > _trainingExamples){
786  for(const auto& example : _trainingExamples)
787  trainingExamples.push_back(example);
788 
789  size_t numExamples = trainingExamples.size();
790  if (numExamples < 1)
791  return;
792 
793  size_t alphasImproved = 0;
794  bool checkAllExamples = true;
795  alpha.resize(numExamples, 0.);
796  error_cache.resize(numExamples, 0.);
797 
798  size_t numSteps = static_cast<size_t>(numExamples*this->stepsStopCondition);
799  //Stop after numSteps while finiteStopCondition is true
800  while( (alphasImproved > 0 || checkAllExamples) && ( !(this->finiteStopCondition) || ((numSteps--) > 0 ) ) ){
801  alphasImproved = 0;
802  if(checkAllExamples){
803  for(size_t i=0; i<numExamples; ++i)
804  alphasImproved += examineKKTViolation(i);
805  }
806  else{
807  for(size_t i=0; i<numExamples; ++i)
808  if((alpha[i] > C+margin || alpha[i] < C-margin) && (alpha[i] > margin || alpha[i] < -(margin) ))
809  alphasImproved += examineKKTViolation(i);
810  }
811  if(checkAllExamples)
812  checkAllExamples = false;
813  else if(alphasImproved == 0)
814  checkAllExamples = true;
815  }//while
816 
817  // At the end of training delete all non-supporting vectors
818  std::vector<size_t> index_to_delete;
819  for(size_t i = 0; i < alpha.size(); ++i)
820  // The vector is non-supporting if alpha==0
821  if(!(alpha[i]>0))
822  index_to_delete.push_back(i-index_to_delete.size());
823 
824  for(const auto& i : index_to_delete) {
825  alpha.erase(alpha.begin() + i);
826  error_cache.erase(error_cache.begin() + i);
827  trainingExamples.erase(trainingExamples.begin() + i);
828  }
829  }
830 
831  template <typename Val, typename DomainVal>
832  Val SvmClassifier<Val, DomainVal>::SmoAlgorithm::classify(const ClassifyExampleSmo& vec){
833  // Default classification value: 0.5
834  if(alpha.size() == 0)
835  return 0.5;
836  Val result = 0.0;
837  size_t numExamples = trainingExamples.size();
838  for(size_t i=0; i<numExamples; ++i)
839  result += alpha[i] * trainingExamples[i].first * ( (this->*kernel)(trainingExamples[i].second, vec) );
840  return sigmoid_function(result-b);
841  }
842 
843  template <typename Val, typename DomainVal>
845  trainingExamples.clear();
846  alpha.clear();
847  error_cache.clear();
848  b = 0.0;
849  delta_b = 0.0;
850  }
851 
852  template <typename Val, typename DomainVal>
854  if( C_in > 0 )
855  this->C = C_in;
856  else throw std::invalid_argument("SVM's parameter 'C' should be > 0");
857  }
858 
859  template <typename Val, typename DomainVal>
861  if( margin_in > 0 )
862  this->margin = margin_in;
863  else throw std::invalid_argument("SVM's parameter 'margin' should be > 0");
864  }
865 
866  template <typename Val, typename DomainVal>
868  if( epsilon_in > 0 )
869  this->epsilon = epsilon_in;
870  else throw std::invalid_argument("SVM's parameter 'epsilon' should be > 0");
871  }
872 
873  template <typename Val, typename DomainVal>
875  if( gaussParameter_in > 0 )
876  this->gaussParameter = gaussParameter_in;
877  else throw std::invalid_argument("SVM's parameter 'gaussParameter' should be > 0");
878  }
879 
880  template <typename Val, typename DomainVal>
881  void SvmClassifier<Val, DomainVal>::SmoAlgorithm::setPolynomialInhomogeneousParameter(Val polynomialInhomogeneousParameter_in){
882  this->polynomialInhomogeneousParameter = polynomialInhomogeneousParameter_in;
883  }
884 
885  template <typename Val, typename DomainVal>
887  this->polynomialDegree = polynomialDegree_in;
888  }
889 
890  template <typename Val, typename DomainVal>
892  if( tangentFrequency_in > 0 )
893  this->tangentFrequency = tangentFrequency_in;
894  else throw std::invalid_argument("SVM's parameter 'tangentFrequency' should be > 0");
895  }
896 
897  template <typename Val, typename DomainVal>
899  if( tangentShift_in >= 0 )
900  this->tangentShift = tangentShift_in;
901  else throw std::invalid_argument("SVM's parameter 'tangentShift' should be >= 0");
902  }
903 
904  template <typename Val, typename DomainVal>
906  if( stop > 0 ){
907  this->stepsStopCondition = stop;
908  this->finiteStopCondition = true;
909  }
910  else throw std::invalid_argument("SVM's parameter 'stepsStopCondition' should be > 0");
911  }
912 
913  template <typename Val, typename DomainVal>
915  this->finiteStopCondition = false;
916  }
917 
918  template <typename Val, typename DomainVal>
920  if( sigmoidScaleFactor_in > 0 )
921  this->sigmoidScaleFactor = sigmoidScaleFactor_in;
922  else throw std::invalid_argument("SVM's parameter 'sigmoidScaleFactor' should be > 0");
923  }
924  } //namespace ml
925 } //namespace faif
926 
927 #endif //FAIF_SVM_HPP_
DomainVal::DomainType AttrDomain
Definition: Svm.hpp:73
SvmClassifier()
Definition: Svm.hpp:318
void setPolynomialInhomogeneousParameter(Val polynomialInhomogeneousParameter)
Definition: Svm.hpp:495
Definition: Chain.h:17
void setTangentShift(Val tangentShift)
Definition: Svm.hpp:504
Beliefs getCategories(const ClassifyExample &vec)
Definition: Svm.hpp:441
size_t countTrainExamples()
Definition: Svm.hpp:474
void setSigmoidScaleFactor(Val sigmoidScaleFactor)
Definition: Svm.hpp:513
void unsetFiniteStepsStopCondition()
Definition: Svm.hpp:510
Belief< DomainVal > getCategory(const ClassifyExample &vec)
Definition: Svm.hpp:463
Definition: Svm.hpp:40
void setHyperbolicTangentKernel()
Definition: Svm.hpp:529
void setC(Val C)
Definition: Svm.hpp:483
void setEpsilon(Val epsilon)
Definition: Svm.hpp:489
void setMargin(Val margin)
Definition: Svm.hpp:486
void setFiniteStepsStopCondition(double stop)
Definition: Svm.hpp:507
void setTangentFrequency(Val tangentFrequency)
Definition: Svm.hpp:501
void setGaussParameter(Val gaussParameter)
Definition: Svm.hpp:492
std::vector< Val > ClassifyExample
Definition: Svm.hpp:70
void resetAndChangeDimension(size_t)
Definition: Svm.hpp:480
void setPolynomialDegree(Val polynomialDegree)
Definition: Svm.hpp:498
the uniform distribution for int, in range <min,max>, uses RandomSingleton
Definition: Random.hpp:107
SvmClassifier(size_t dimension_, const AttrDomain &category_domain)
Definition: Svm.hpp:321
void setPolynomialKernel()
Definition: Svm.hpp:524
void addExample(const ClassifyExample &example, const AttrValue &category)
Definition: Svm.hpp:421
DomainVal::Value AttrValue
Definition: Svm.hpp:76
void setLinearKernel()
Definition: Svm.hpp:516
void setGaussKernel()
Definition: Svm.hpp:519
Belief< DomainVal >::Beliefs Beliefs
Definition: Svm.hpp:79
friend class boost::serialization::access
serialization using boost::serialization
Definition: Svm.hpp:45
void reset()
Definition: Svm.hpp:477
belief is value id with probability
Definition: Belief.hpp:41
size_t getDimension()
Definition: Svm.hpp:471
void train()
Definition: Svm.hpp:468
the exception thrown when the value for given attribute is not found
Definition: ExceptionsFaif.hpp:32