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-2017 Shark Development Team
15  *
16  * <BR><HR>
17  * This file is part of Shark.
18  * <http://shark-ml.org/>
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/noncopyable.hpp>
43 #include <boost/smart_ptr/shared_ptr.hpp>
44 
45 #include <limits>
46 #include <utility>
47 namespace shark {
48 
49 /// @brief Naive Bayes classifier
50 ///
51 /// This model summarizes a Naive Bayes classifier, which assumes that the data X is generated by a mixture
52 /// of class-conditional (i.e., dependent on the value of the class variable Y) distributions. Furthermore, the Naive Bayes
53 /// assumption introduces the additional constraint that the attribute values Xi are independent of one another within
54 /// each of these mixture components.
55 template <class InputType = RealVector, class OutputType = unsigned int>
56 class NBClassifier :
57  public AbstractModel<InputType, OutputType>,
58  private boost::noncopyable
59 {
60 private:
61 
63 
64 public:
65 
68 
69  /// Type of class distribution
70  typedef std::vector<double> ClassPriorsType;
71 
72  typedef boost::shared_ptr<AbstractDistribution> AbstractDistPtr;
73 
74  /// Type of features distribution
75  typedef std::vector<std::vector<AbstractDistPtr> > FeatureDistributionsType;
76 
77  /// Size of distribution in format of (number of classes, number of features)
78  typedef std::pair<std::size_t, std::size_t> DistSizeType;
79 
80  /// Ctor
81  /// Will build hypothesis that all features in each class follows Normal distribution
82  /// @param classSize size of class
83  /// @param featureSize size of feature
84  NBClassifier(std::size_t classSize, std::size_t featureSize)
85  {
86  SIZE_CHECK(classSize > 0u);
87  SIZE_CHECK(featureSize > 0u);
88  for (std::size_t i = 0; i < classSize; ++i)
89  {
90  std::vector<AbstractDistPtr> featureDist;
91  for (std::size_t j = 0; j < featureSize; ++j)
92  featureDist.push_back(AbstractDistPtr(new Normal<DefaultRngType>(Rng::globalRng)));
93  m_featureDistributions.push_back(featureDist);
94  }
95  }
96 
97  /// Ctor
98  /// The distributions for each feature in each class are given by @a featureDists
99  /// @param featureDists the distribution of features
100  explicit NBClassifier(FeatureDistributionsType const& featureDists)
101  : m_featureDistributions(featureDists)
102  {
103  SIZE_CHECK(m_featureDistributions.size() > 0u);
104  }
105 
106  /// \brief From INameable: return the class name.
107  std::string name() const
108  { return "NBClassifier"; }
109 
110  /// Get a feature distribution for feature @a featureIndex given class @a classIndex
111  /// @param classIndex index of class
112  /// @param featureIndex index of feature
113  /// @return the distribution for feature @a featureIndex given class @a classIndex
114  AbstractDistribution& getFeatureDist(std::size_t classIndex, std::size_t featureIndex) const
115  {
116  SIZE_CHECK(classIndex < m_featureDistributions.size());
117  SIZE_CHECK(featureIndex < m_featureDistributions[0].size());
118 
119  AbstractDistPtr const& featureDist = m_featureDistributions[classIndex][featureIndex];
120  SHARK_ASSERT(featureDist);
121  return *featureDist;
122  }
123 
124  /// Get the size of distribution in format of (class size, feature size)
125  /// @return the size of distribution
126  DistSizeType getDistSize() const
127  {
128  SIZE_CHECK(m_featureDistributions.size() > 0u);
129  return std::make_pair(m_featureDistributions.size(), m_featureDistributions[0].size());
130  }
131 
132  using base_type::eval;
133 
134  boost::shared_ptr<State> createState()const{
135  return boost::shared_ptr<State>(new EmptyState());
136  }
137 
138  /// see AbstractModel::eval
139  void eval(BatchInputType const& patterns, BatchOutputType& outputs, State& state)const{
141  SIZE_CHECK(m_classPriors.size() > 0u);
142 
143  outputs.resize(patterns.size1());
144 
145  for(std::size_t p = 0; p != patterns.size1(); ++p){
146  OutputType bestProbClass = 0; // just initialized to avoid warning
147  double maxLogProb = - std::numeric_limits<double>::max(); // initialized as smallest negative value
148 
149  // For each of possible output values, calculate its corresponding sum-up log prob and return the max one
150  for(OutputType classIndex = 0; classIndex != m_classPriors.size(); ++classIndex){
151  SIZE_CHECK(patterns.size2() == m_featureDistributions[classIndex].size());
152  double const classDistribution = m_classPriors[classIndex];
153  // Sum up log prob of each features and prior prob of current class
154  // We use log to ensure that the result stays in a valid range of double, even when the propability is very low
155  double currentLogProb = safeLog(classDistribution);
156  std::size_t featureIndex = 0u;
157  for(auto const& featureDistribution: m_featureDistributions[classIndex])
158  currentLogProb += featureDistribution->logP(patterns(p,featureIndex++));
159 
160  // Record the greater one
161  if (currentLogProb > maxLogProb)
162  {
163  maxLogProb = currentLogProb;
164  bestProbClass = classIndex;
165  }
166  }
167  SHARK_ASSERT(maxLogProb != - std::numeric_limits<double>::max());//should never happen!
168  outputs(p) = bestProbClass;
169  }
170  }
171 
172  /// Set prior distribution of @a class to be @a probability
173  /// @param classToAdd the class of which probability will be updated
174  /// @param probability the probability of the class
175  /// @tparam OutputType the type of output class
176  void setClassPrior(OutputType classToAdd, double probability)
177  {
178  if (classToAdd == m_classPriors.size())
179  m_classPriors.push_back(probability);
180  else
181  throw SHARKEXCEPTION("[NBClassifier] class probability must be added in ascending order.");
182  }
183 
184  /// This model does not have any parameters.
185  RealVector parameterVector() const {
186  return RealVector();
187  }
188 
189  /// This model does not have any parameters
190  void setParameterVector(const RealVector& param) {
191  SHARK_ASSERT(param.size() == 0);
192  }
193 
194 protected:
195 
196  /// Feature and class distributions
197  ///@{
198  FeatureDistributionsType m_featureDistributions;
199  ClassPriorsType m_classPriors;
200  ///@}
201 };
202 
203 } // namespace shark {
204 
205 #endif // SHARK_MODEL_NB_CLASSIFIER_H