LooErrorCSvm.h
Go to the documentation of this file.
1 /*!
2  *
3  *
4  * \brief Leave-one-out error for C-SVMs
5  *
6  *
7  *
8  * \author T.Glasmachers
9  * \date 2011
10  *
11  *
12  * \par Copyright 1995-2017 Shark Development Team
13  *
14  * <BR><HR>
15  * This file is part of Shark.
16  * <http://shark-ml.org/>
17  *
18  * Shark is free software: you can redistribute it and/or modify
19  * it under the terms of the GNU Lesser General Public License as published
20  * by the Free Software Foundation, either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * Shark is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU Lesser General Public License for more details.
27  *
28  * You should have received a copy of the GNU Lesser General Public License
29  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
30  *
31  */
32 #ifndef SHARK_OBJECTIVEFUNCTIONS_LOOERRORCSVM_H
33 #define SHARK_OBJECTIVEFUNCTIONS_LOOERRORCSVM_H
34 
35 
43 
44 namespace shark {
45 
46 ///
47 /// \brief Leave-one-out error, specifically optimized for C-SVMs.
48 ///
49 template<class InputType, class CacheType = float>
51 {
52 public:
53  typedef CacheType QpFloatType;
56 
57  /// \brief Constructor.
58  LooErrorCSvm(DatasetType const& dataset, KernelType* kernel, bool withOffset)
59  : mep_dataset(&dataset)
60  , mep_kernel(kernel)
61  , m_withOffset(withOffset)
62  {
63  SHARK_RUNTIME_CHECK(kernel != NULL, "kernel is not allowed to be Null");
65  }
66 
67 
68  /// \brief From INameable: return the class name.
69  std::string name() const
70  { return "LooErrorCSvm"; }
71 
72  std::size_t numberOfVariables()const{
73  return mep_kernel->numberOfParameters()+1;
74  }
75 
76  /// Evaluate the leave-one-out error for the given parameters.
77  /// These parameters describe the regularization
78  /// constant and the kernel parameters.
79  double eval(const RealVector& params){
81  return eval(params, stop);
82  }
83  /// Evaluate the leave-one-out error for the given parameters.
84  /// These parameters describe the regularization constant and
85  /// the kernel parameters. Furthermore, the stopping
86  /// conditions for the solver are passed as an argument.
87  double eval(const RealVector& params, QpStoppingCondition &stop){
88  this->m_evaluationCounter++;
89 
90  double C = params.back();
91  mep_kernel->setParameterVector(subrange(params,0,params.size() - 1));
92 
94 
95  // prepare the quadratic program
96  typedef KernelMatrix<InputType, QpFloatType> KernelMatrixType;
97  typedef CachedMatrix< KernelMatrixType > CachedMatrixType;
98  typedef CSVMProblem<CachedMatrixType> SVMProblemType;
99  KernelMatrixType km(*mep_kernel, mep_dataset->inputs());
100  CachedMatrixType matrix(&km);
101  SVMProblemType svmProblem(matrix,mep_dataset->labels(),C);
102  std::size_t ell = km.size();
103 
104  //QpStoppingCondition stop;
105 
106  if (m_withOffset)
107  {
108  // solve the full problem with equality constraint and activated shrinking
109  typedef SvmProblem<SVMProblemType> ProblemType;
110  ProblemType problem(svmProblem);
111  QpSolver< ProblemType > solver(problem);
112  solver.solve(stop);
113  RealVector alphaFull(problem.dimensions());
114  for(std::size_t i = 0; i != problem.dimensions(); ++i){
115  alphaFull(i) = problem.alpha(i);
116  }
117  KernelExpansion<InputType> svm(mep_kernel,mep_dataset->inputs(),true);
118 
119  // leave-one-out
120  //problem.setShrinking(false);
121  double mistakes = 0;
122  for (std::size_t i=0; i<ell; i++)
123  {
124  // use sparseness of the solution:
125  if (alphaFull(i) == 0.0) continue;
126  problem.deactivateVariable(i);
127 
128  // solve the reduced problem
129  solver.solve(stop);
130 
131  // predict using the previously removed example.
132  // we need to take into account that the initial problem is solved
133  // with shrinking and we thus need to get the initial permutation
134  // for the element index and the unpermuted alpha for the svm
135  column(svm.alpha(),0)= problem.getUnpermutedAlpha();
136  svm.offset(0) = computeBias(problem);
137  std::size_t elementIndex = i;//svmProblem.permutation[i];
138  unsigned int target = mep_dataset->element(elementIndex).label;
139  mistakes += loss(target, svm(mep_dataset->element(elementIndex).input));
140 
141  problem.activateVariable(i);
142  }
143  return mistakes / (double)ell;
144  }
145  else
146  {
147  // solve the full problem without equality constraint and activated shrinking
148  typedef BoxConstrainedProblem<SVMProblemType> ProblemType;
149  ProblemType problem(svmProblem);
150  QpSolver< ProblemType > solver(problem);
151  solver.solve(stop);
152  RealVector alphaFull(problem.dimensions());
153  for(std::size_t i = 0; i != problem.dimensions(); ++i){
154  alphaFull(i) = problem.alpha(i);
155  }
156  KernelExpansion<InputType> svm(mep_kernel,mep_dataset->inputs(),false);
157 
158  // leave-one-out
159  //problem.setShrinking(false);
160  double mistakes = 0;
161  for (std::size_t i=0; i<ell; i++)
162  {
163  // use sparseness of the solution:
164  if (alphaFull(i) == 0.0) continue;
165  problem.deactivateVariable(i);
166 
167 
168  // solve the reduced problem
169  solver.solve(stop);
170 
171  // predict using the previously removed example.
172  // we need to take into account that the initial problem is solved
173  // with shrinking and we thus need to get the initial permutation
174  // for the element index and the unpermuted alpha for the svm
175  column(svm.alpha(),0)= problem.getUnpermutedAlpha();
176  std::size_t elementIndex = i;//svmProblem.permutation[i];
177  unsigned int target = mep_dataset->element(elementIndex).label;
178  mistakes += loss(target, svm(mep_dataset->element(elementIndex).input));
179 
180  problem.activateVariable(i);
181  }
182  return mistakes / (double)ell;
183  }
184  }
185 
186 private:
187  /// Compute the SVM offset term (b).
188  template<class Problem>
189  double computeBias(Problem const& problem){
190  double lowerBound = -1e100;
191  double upperBound = 1e100;
192  double sum = 0.0;
193  std::size_t freeVars = 0;
194  std::size_t ell = problem.dimensions();
195  for (std::size_t i=0; i<ell; i++)
196  {
197  double value = problem.gradient(i);
198  if (problem.alpha(i) == problem.boxMin(i))
199  {
200  lowerBound = std::max(value,lowerBound);
201  }
202  else if (problem.alpha(i) == problem.boxMax(i))
203  {
204  upperBound = std::min(value,upperBound);
205  }
206  else
207  {
208  sum += value;
209  freeVars++;
210  }
211  }
212  if (freeVars > 0)
213  return sum / freeVars;
214  else
215  return 0.5 * (lowerBound + upperBound);
216  }
217 
218  const DatasetType* mep_dataset;
219  KernelType* mep_kernel;
220  bool m_withOffset;
221 };
222 
223 
224 }
225 #endif