OneVersusOneClassifier.h
Go to the documentation of this file.
1 //===========================================================================
2 /*!
3  *
4  *
5  * \brief One-versus-one Classifier.
6  *
7  *
8  *
9  * \author T. Glasmachers
10  * \date 2012
11  *
12  *
13  * \par Copyright 1995-2017 Shark Development Team
14  *
15  * <BR><HR>
16  * This file is part of Shark.
17  * <http://shark-ml.org/>
18  *
19  * Shark is free software: you can redistribute it and/or modify
20  * it under the terms of the GNU Lesser General Public License as published
21  * by the Free Software Foundation, either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * Shark is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU Lesser General Public License for more details.
28  *
29  * You should have received a copy of the GNU Lesser General Public License
30  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
31  *
32  */
33 //===========================================================================
34 
35 #ifndef SHARK_MODELS_ONEVERSUSONE_H
36 #define SHARK_MODELS_ONEVERSUSONE_H
37 
38 
40 
41 
42 namespace shark {
43 
44 
45 ///
46 /// \brief One-versus-one Classifier.
47 ///
48 /// \par
49 /// The one-versus-one classifier combines a number of binary
50 /// classifiers to form a multi-class ensemble classifier.
51 /// In the one-versus-one model, there exists one binary
52 /// classifier for each pair of classes. The predictions of
53 /// all binary machines are combined with a simple voting
54 /// scheme.
55 ///
56 /// \par
57 /// The classifier can be extended to handle more classes on
58 /// the fly, without a need for re-training the existing
59 /// binary models.
60 ///
61 template <class InputType, class VectorType = RealVector>
62 class OneVersusOneClassifier : public AbstractModel<InputType, unsigned int, VectorType>
63 {
64 public:
70 
71  /// \brief Constructor
73  : m_classes(1)
74  { }
75 
76  /// \brief From INameable: return the class name.
77  std::string name() const
78  { return "OneVersusOneClassifier"; }
79 
80 
81  /// get internal parameters of the model
82  virtual VectorType parameterVector() const{
83  std::size_t total = numberOfParameters();
84  VectorType ret(total);
85  std::size_t used = 0;
86  for (std::size_t i=0; i<m_binary.size(); i++){
87  std::size_t n = m_binary[i]->numberOfParameters();
88  noalias(subrange(ret, used, used + n)) = m_binary[i]->parameterVector();
89  used += n;
90  }
91  return ret;
92  }
93 
94  /// set internal parameters of the model
95  virtual void setParameterVector(VectorType const& newParameters) {
96  SHARK_RUNTIME_CHECK(numberOfParameters() == newParameters.size(),"Invalid number of parameters");
97  std::size_t used = 0;
98  for (std::size_t i=0; i<m_binary.size(); i++){
99  std::size_t n = m_binary[i]->numberOfParameters();
100  m_binary[i]->setParameterVector(subrange(newParameters, used, used + n));
101  used += n;
102  }
103  }
104 
105  /// return the size of the parameter vector
106  virtual std::size_t numberOfParameters() const{
107  std::size_t ret = 0;
108  for (std::size_t i=0; i<m_binary.size(); i++)
109  ret += m_binary[i]->numberOfParameters();
110  return ret;
111  }
112 
113  /// return number of classes
114  unsigned int numberOfClasses() const
115  { return m_classes; }
116 
117  Shape inputShape() const{
118  return m_binary.empty()? Shape() : m_binary[0]->inputShape();
119  }
121  return Shape();
122  }
123 
124  /// \brief Obtain binary classifier.
125  ///
126  /// \par
127  /// The method returns the binary classifier used to distinguish
128  /// class_one from class_zero. The convention class_one > class_zero
129  /// is used (the inverse classifier can be constructed from this one
130  /// by flipping the labels). The binary classifier outputs a value
131  /// of 1 for class_one and a value of zero for class_zero.
132  binary_classifier_type const& binary(unsigned int class_one, unsigned int class_zero) const{
133  SHARK_ASSERT(class_zero < class_one);
134  SHARK_ASSERT(class_one < m_classes);
135  unsigned int index = class_one * (class_zero - 1) / 2 + class_zero;
136  return *m_binary[index];
137  }
138 
139  /// \brief Add binary classifiers for one more class to the model.
140  ///
141  /// The parameter binmodels holds a vector of n binary classifiers,
142  /// where n is the current number of classes. The i-th model is this
143  /// list is supposed to output a value of 1 for class n and a value
144  /// of 0 for class i when faced with the binary classification problem
145  /// of separating class i from class n. Afterwards the model can
146  /// predict the n+1 classes {0, ..., n}.
147  void addClass(std::vector<binary_classifier_type*> const& binmodels)
148  {
149  SHARK_RUNTIME_CHECK(binmodels.size() == m_classes, "[OneVersusOneClassifier::addClass] wrong number of binary models");
150  m_classes++;
151  m_binary.insert(m_binary.end(), binmodels.begin(), binmodels.end());
152  }
153 
154  boost::shared_ptr<State> createState()const{
155  return boost::shared_ptr<State>(new EmptyState());
156  }
157 
158  using base_type::eval;
159  /// One-versus-one prediction: evaluate all binary classifiers,
160  /// collect their votes, and return the class with most votes.
161  void eval(
162  BatchInputType const & patterns, BatchOutputType& output, State& state
163  )const{
164  std::size_t numPatterns = batchSize(patterns);
165  output.resize(numPatterns);
166  output.clear();
167 
168  //matrix storing the class histogram for all patterns
169  UIntMatrix votes(numPatterns,m_classes);
170  votes.clear();
171 
172  //stores the votes of a classifier distinguishing between classes c and e
173  //for all patterns
174  UIntVector bin(numPatterns);
175  //accumulate histograms
176  for (unsigned int i=0, c=0; c<m_classes; c++)
177  {
178  for (std::size_t e=0; e<c; e++, i++)
179  {
180  m_binary[i]->eval(patterns,bin);
181  for(std::size_t p = 0; p != numPatterns; ++p){
182  if (bin[p] == 0)
183  votes(p,e)++;
184  else
185  votes(p,c)++;
186  }
187 
188  }
189  }
190  //find the maximum class for ever pattern
191  for(std::size_t p = 0; p != numPatterns; ++p){
192  for (unsigned int c=1; c < m_classes; c++){
193  if (votes(p,c) > votes(p,output(p)))
194  output(p) = c;
195  }
196  }
197  }
198 
199  /// from ISerializable, reads a model from an archive
200  void read(InArchive& archive)
201  {
202  archive & m_classes;
203  archive & m_binary;
204  }
205 
206  /// from ISerializable, writes a model to an archive
207  void write(OutArchive& archive) const
208  {
209  archive & m_classes;
210  //TODO: O.K. might be leaking memory!!!
211  archive & m_binary;
212  }
213 
214 protected:
215  unsigned int m_classes; ///< number of classes to be distinguished
216  std::vector<binary_classifier_type*> m_binary; ///< list of binary classifiers
217 };
218 
219 
220 }
221 #endif