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 * <https://shark-ml.github.io/Shark/>
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
42namespace shark {
43
44
45/// \brief One-versus-one Classifier.
46///
47/// \par
48/// The one-versus-one classifier combines a number of binary
49/// classifiers to form a multi-class ensemble classifier.
50/// In the one-versus-one model, there exists one binary
51/// classifier for each pair of classes. The predictions of
52/// all binary machines are combined with a simple voting
53/// scheme.
54///
55/// \par
56/// The classifier can be extended to handle more classes on
57/// the fly, without a need for re-training the existing
58/// binary models.
59///
60///
61/// \ingroup models
62template <class InputType, class VectorType = RealVector>
63class OneVersusOneClassifier : public AbstractModel<InputType, unsigned int, VectorType>
64{
65public:
71
72 /// \brief Constructor
76
77 /// \brief From INameable: return the class name.
78 std::string name() const
79 { return "OneVersusOneClassifier"; }
80
81
82 /// get internal parameters of the model
83 virtual VectorType parameterVector() const{
84 std::size_t total = numberOfParameters();
85 VectorType ret(total);
86 std::size_t used = 0;
87 for (std::size_t i=0; i<m_binary.size(); i++){
88 std::size_t n = m_binary[i]->numberOfParameters();
89 noalias(subrange(ret, used, used + n)) = m_binary[i]->parameterVector();
90 used += n;
91 }
92 return ret;
93 }
94
95 /// set internal parameters of the model
96 virtual void setParameterVector(VectorType const& newParameters) {
97 SHARK_RUNTIME_CHECK(numberOfParameters() == newParameters.size(),"Invalid number of parameters");
98 std::size_t used = 0;
99 for (std::size_t i=0; i<m_binary.size(); i++){
100 std::size_t n = m_binary[i]->numberOfParameters();
101 m_binary[i]->setParameterVector(subrange(newParameters, used, used + n));
102 used += n;
103 }
104 }
105
106 /// return the size of the parameter vector
107 virtual std::size_t numberOfParameters() const{
108 std::size_t ret = 0;
109 for (std::size_t i=0; i<m_binary.size(); i++)
110 ret += m_binary[i]->numberOfParameters();
111 return ret;
112 }
113
114 /// return number of classes
115 unsigned int numberOfClasses() const
116 { return m_classes; }
117
119 return m_binary.empty()? Shape() : m_binary[0]->inputShape();
120 }
122 return Shape({m_classes});
123 }
124
125 /// \brief Obtain binary classifier.
126 ///
127 /// \par
128 /// The method returns the binary classifier used to distinguish
129 /// class_one from class_zero. The convention class_one > class_zero
130 /// is used (the inverse classifier can be constructed from this one
131 /// by flipping the labels). The binary classifier outputs a value
132 /// of 1 for class_one and a value of zero for class_zero.
133 binary_classifier_type const& binary(unsigned int class_one, unsigned int class_zero) const{
134 SHARK_ASSERT(class_zero < class_one);
135 SHARK_ASSERT(class_one < m_classes);
136 unsigned int index = class_one * (class_zero - 1) / 2 + class_zero;
137 return *m_binary[index];
138 }
139
140 /// \brief Add binary classifiers for one more class to the model.
141 ///
142 /// The parameter binmodels holds a vector of n binary classifiers,
143 /// where n is the current number of classes. The i-th model is this
144 /// list is supposed to output a value of 1 for class n and a value
145 /// of 0 for class i when faced with the binary classification problem
146 /// of separating class i from class n. Afterwards the model can
147 /// predict the n+1 classes {0, ..., n}.
148 void addClass(std::vector<binary_classifier_type*> const& binmodels)
149 {
150 SHARK_RUNTIME_CHECK(binmodels.size() == m_classes, "[OneVersusOneClassifier::addClass] wrong number of binary models");
151 m_classes++;
152 m_binary.insert(m_binary.end(), binmodels.begin(), binmodels.end());
153 }
154
155 boost::shared_ptr<State> createState()const{
156 return boost::shared_ptr<State>(new EmptyState());
157 }
158
159 using base_type::eval;
160 /// One-versus-one prediction: evaluate all binary classifiers,
161 /// collect their votes, and return the class with most votes.
162 void eval(
163 BatchInputType const & patterns, BatchOutputType& output, State& state
164 )const{
165 std::size_t numPatterns = batchSize(patterns);
166 output.resize(numPatterns);
167 output.clear();
168
169 //matrix storing the class histogram for all patterns
170 UIntMatrix votes(numPatterns,m_classes);
171 votes.clear();
172
173 //stores the votes of a classifier distinguishing between classes c and e
174 //for all patterns
175 UIntVector bin(numPatterns);
176 //accumulate histograms
177 for (unsigned int i=0, c=0; c<m_classes; c++)
178 {
179 for (std::size_t e=0; e<c; e++, i++)
180 {
181 m_binary[i]->eval(patterns,bin);
182 for(std::size_t p = 0; p != numPatterns; ++p){
183 if (bin[p] == 0)
184 votes(p,e)++;
185 else
186 votes(p,c)++;
187 }
188
189 }
190 }
191 //find the maximum class for ever pattern
192 for(std::size_t p = 0; p != numPatterns; ++p){
193 for (unsigned int c=1; c < m_classes; c++){
194 if (votes(p,c) > votes(p,output(p)))
195 output(p) = c;
196 }
197 }
198 }
199
200 /// from ISerializable, reads a model from an archive
201 void read(InArchive& archive)
202 {
203 archive & m_classes;
204 archive & m_binary;
205 }
206
207 /// from ISerializable, writes a model to an archive
208 void write(OutArchive& archive) const
209 {
210 archive & m_classes;
211 //TODO: O.K. might be leaking memory!!!
212 archive & m_binary;
213 }
214
215protected:
216 unsigned int m_classes; ///< number of classes to be distinguished
217 std::vector<binary_classifier_type*> m_binary; ///< list of binary classifiers
218};
219
220
221}
222#endif