NBClassifier.h
Go to the documentation of this file.
1 //===========================================================================
2 /*!
3  *
4  *
5  * \brief Implementation of Naive Bayes classifier
6  *
7  *
8  *
9  *
10  * \author B. Li
11  * \date 2012
12  *
13  *
14  * \par Copyright 1995-2015 Shark Development Team
15  *
16  * <BR><HR>
17  * This file is part of Shark.
18  * <http://image.diku.dk/shark/>
19  *
20  * Shark is free software: you can redistribute it and/or modify
21  * it under the terms of the GNU Lesser General Public License as published
22  * by the Free Software Foundation, either version 3 of the License, or
23  * (at your option) any later version.
24  *
25  * Shark is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28  * GNU Lesser General Public License for more details.
29  *
30  * You should have received a copy of the GNU Lesser General Public License
31  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
32  *
33  */
34 //===========================================================================
35 #ifndef SHARK_MODEL_NB_CLASSIFIER_H
36 #define SHARK_MODEL_NB_CLASSIFIER_H
37 
38 #include "shark/Core/Exception.h"
39 #include "shark/Core/Math.h"
41 
42 #include <boost/foreach.hpp>
43 #include <boost/noncopyable.hpp>
44 #include <boost/smart_ptr/shared_ptr.hpp>
45 #include <boost/static_assert.hpp>
46 
47 #include <boost/type_traits.hpp>
48 
49 #include <limits>
50 #include <utility>
51 namespace shark {
52 
53 /// @brief Naive Bayes classifier
54 ///
55 /// This model summarizes a Naive Bayes classifier, which assumes that the data X is generated by a mixture
56 /// of class-conditional (i.e., dependent on the value of the class variable Y) distributions. Furthermore, the Naive Bayes
57 /// assumption introduces the additional constraint that the attribute values Xi are independent of one another within
58 /// each of these mixture components.
59 template <class InputType = RealVector, class OutputType = unsigned int>
60 class NBClassifier :
61  public AbstractModel<InputType, OutputType>,
62  private boost::noncopyable
63 {
64 private:
65 
67 
68 public:
69 
72 
73  /// Type of class distribution
74  typedef std::vector<double> ClassPriorsType;
75 
76  typedef boost::shared_ptr<AbstractDistribution> AbstractDistPtr;
77 
78  /// Type of features distribution
79  typedef std::vector<std::vector<AbstractDistPtr> > FeatureDistributionsType;
80 
81  /// Size of distribution in format of (number of classes, number of features)
82  typedef std::pair<std::size_t, std::size_t> DistSizeType;
83 
84  /// Ctor
85  /// Will build hypothesis that all features in each class follows Normal distribution
86  /// @param classSize size of class
87  /// @param featureSize size of feature
88  NBClassifier(std::size_t classSize, std::size_t featureSize)
89  {
90  SIZE_CHECK(classSize > 0u);
91  SIZE_CHECK(featureSize > 0u);
92  for (std::size_t i = 0; i < classSize; ++i)
93  {
94  std::vector<AbstractDistPtr> featureDist;
95  for (std::size_t j = 0; j < featureSize; ++j)
96  featureDist.push_back(AbstractDistPtr(new Normal<DefaultRngType>(Rng::globalRng)));
97  m_featureDistributions.push_back(featureDist);
98  }
99  }
100 
101  /// Ctor
102  /// The distributions for each feature in each class are given by @a featureDists
103  /// @param featureDists the distribution of features
104  explicit NBClassifier(FeatureDistributionsType const& featureDists)
105  : m_featureDistributions(featureDists)
106  {
107  SIZE_CHECK(m_featureDistributions.size() > 0u);
108  }
109 
110  /// \brief From INameable: return the class name.
111  std::string name() const
112  { return "NBClassifier"; }
113 
114  /// Get a feature distribution for feature @a featureIndex given class @a classIndex
115  /// @param classIndex index of class
116  /// @param featureIndex index of feature
117  /// @return the distribution for feature @a featureIndex given class @a classIndex
118  AbstractDistribution& getFeatureDist(std::size_t classIndex, std::size_t featureIndex) const
119  {
120  SIZE_CHECK(classIndex < m_featureDistributions.size());
121  SIZE_CHECK(featureIndex < m_featureDistributions[0].size());
122 
123  AbstractDistPtr const& featureDist = m_featureDistributions[classIndex][featureIndex];
124  SHARK_ASSERT(featureDist);
125  return *featureDist;
126  }
127 
128  /// Get the size of distribution in format of (class size, feature size)
129  /// @return the size of distribution
130  DistSizeType getDistSize() const
131  {
132  SIZE_CHECK(m_featureDistributions.size() > 0u);
133  return std::make_pair(m_featureDistributions.size(), m_featureDistributions[0].size());
134  }
135 
136  using base_type::eval;
137 
138  boost::shared_ptr<State> createState()const{
139  return boost::shared_ptr<State>(new EmptyState());
140  }
141 
142  /// see AbstractModel::eval
143  void eval(BatchInputType const& patterns, BatchOutputType& outputs, State& state)const{
145  SIZE_CHECK(m_classPriors.size() > 0u);
146  SIZE_CHECK(size(patterns) > 0);
147 
148  outputs.resize(size(patterns));
149 
150  for(std::size_t p = 0; p != size(patterns); ++p){
151  OutputType bestProbClass = 0; // just initialized to avoid warning
152  double maxLogProb = - std::numeric_limits<double>::max(); // initialized as smallest negative value
153 
154  // For each of possible output values, calculate its corresponding sum-up log prob and return the max one
155  for(OutputType classIndex = 0; classIndex != m_classPriors.size(); ++classIndex){
156  SIZE_CHECK(patterns.size2() == m_featureDistributions[classIndex].size());
157  double const classDistribution = m_classPriors[classIndex];
158  // Sum up log prob of each features and prior prob of current class
159  // We use log to ensure that the result stays in a valid range of double, even when the propability is very low
160  double currentLogProb = safeLog(classDistribution);
161  std::size_t featureIndex = 0u;
162  BOOST_FOREACH(AbstractDistPtr const& featureDistribution, m_featureDistributions[classIndex])
163  currentLogProb += featureDistribution->logP(patterns(p,featureIndex++));
164 
165  // Record the greater one
166  if (currentLogProb > maxLogProb)
167  {
168  maxLogProb = currentLogProb;
169  bestProbClass = classIndex;
170  }
171  }
172  SHARK_ASSERT(maxLogProb != - std::numeric_limits<double>::max());//should never happen!
173  outputs(p) = bestProbClass;
174  }
175  }
176 
177  /// Set prior distribution of @a class to be @a probability
178  /// @param classToAdd the class of which probability will be updated
179  /// @param probability the probability of the class
180  /// @tparam OutputType the type of output class
181  void setClassPrior(OutputType classToAdd, double probability)
182  {
183  if (classToAdd == m_classPriors.size())
184  m_classPriors.push_back(probability);
185  else
186  throw SHARKEXCEPTION("[NBClassifier] class probability must be added in ascending order.");
187  }
188 
189  /// This model does not have any parameters.
190  RealVector parameterVector() const {
191  return RealVector();
192  }
193 
194  /// This model does not have any parameters
195  void setParameterVector(const RealVector& param) {
196  SHARK_ASSERT(param.size() == 0);
197  }
198 
199 protected:
200 
201  /// Feature and class distributions
202  ///@{
203  FeatureDistributionsType m_featureDistributions;
204  ClassPriorsType m_classPriors;
205  ///@}
206 
207  /// Output should be integer/discrete type
208  BOOST_STATIC_ASSERT(boost::is_integral<OutputType>::value);
209 };
210 
211 } // namespace shark {
212 
213 #endif // SHARK_MODEL_NB_CLASSIFIER_H