ConvolutionalRBM.h
Go to the documentation of this file.
1 /*!
2  *
3  *
4  * \brief -
5  *
6  * \author -
7  * \date -
8  *
9  *
10  * \par Copyright 1995-2017 Shark Development Team
11  *
12  * <BR><HR>
13  * This file is part of Shark.
14  * <http://shark-ml.org/>
15  *
16  * Shark is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU Lesser General Public License as published
18  * by the Free Software Foundation, either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * Shark is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public License
27  * along with Shark. If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30 #ifndef SHARK_UNSUPERVISED_RBM_CONVOLUTIONALRBM_H
31 #define SHARK_UNSUPERVISED_RBM_CONVOLUTIONALRBM_H
32 
35 #include <shark/Unsupervised/RBM/Impl/ConvolutionalEnergyGradient.h>
36 
37 #include <sstream>
38 #include <boost/serialization/string.hpp>
39 namespace shark{
40 
41 ///\brief Implements a convolutional RBM with a single greyscale input imge and a set of squared image filters
42 ///
43 /// This class implements a simple RBM which interprets is input as images and instead of learning arbitrary filters, learns a convolution
44 /// Thus the ConvolutionalRBM is to an RBM what a ConvolutionalFFNet is to an FFNet.
45 template<class VisibleLayerT,class HiddenLayerT, class RngT>
46 class ConvolutionalRBM : public AbstractModel<RealVector, RealVector>{
47 private:
49 public:
50  typedef detail::ConvolutionalEnergyGradient<ConvolutionalRBM> GradientType;
51  typedef HiddenLayerT HiddenType; //< type of the hidden layer
52  typedef VisibleLayerT VisibleType; //< type of the visible layer
53  typedef RngT RngType;
54  typedef Energy<ConvolutionalRBM<VisibleType,HiddenType,RngT> > EnergyType;//< Type of the energy function
55 
58 
59 private:
60  std::size_t m_inputSize1;
61  std::size_t m_inputSize2;
62 
63  /// \brief The weight matrix connecting hidden and visible layer.
64  std::vector<RealMatrix> m_filters;
65 
66  ///The layer of hidden Neurons
67  HiddenType m_hiddenNeurons;
68 
69  ///The Layer of visible Neurons
70  VisibleType m_visibleNeurons;
71 
72  RngType* mpe_rng;
73  bool m_forward;
74  bool m_evalMean;
75 
76  ///\brief Evaluates the input by propagating the visible input to the hidden neurons.
77  ///
78  ///@param patterns batch of states of visible units
79  ///@param outputs batch of (expected) states of hidden units
80  void evalForward(BatchInputType const& state,BatchOutputType& output)const{
81  std::size_t batchSize=state.size1();
82  typename HiddenType::StatisticsBatch statisticsBatch(batchSize,numberOfHN());
83  RealMatrix inputBatch(batchSize,numberOfHN());
84  output.resize(state.size1(),numberOfHN());
85 
86  energy().inputHidden(inputBatch,state);
87  hiddenNeurons().sufficientStatistics(inputBatch,statisticsBatch,blas::repeat(1.0,batchSize));
88 
89  if(m_evalMean){
90  noalias(output) = hiddenNeurons().mean(statisticsBatch);
91  }
92  else{
93  hiddenNeurons().sample(statisticsBatch,output,0.0,*mpe_rng);
94  }
95  }
96 
97  ///\brief Evaluates the input by propagating the hidden input to the visible neurons.
98  ///
99  ///@param patterns batch of states of hidden units
100  ///@param outputs batch of (expected) states of visible units
101  void evalBackward(BatchInputType const& state,BatchOutputType& output)const{
102  std::size_t batchSize = state.size1();
103  typename VisibleType::StatisticsBatch statisticsBatch(batchSize,numberOfVN());
104  RealMatrix inputBatch(batchSize,numberOfVN());
105  output.resize(batchSize,numberOfVN());
106 
107  energy().inputVisible(inputBatch,state);
108  visibleNeurons().sufficientStatistics(inputBatch,statisticsBatch,blas::repeat(1.0,batchSize));
109 
110  if(m_evalMean){
111  noalias(output) = visibleNeurons().mean(statisticsBatch);
112  }
113  else{
114  visibleNeurons().sample(statisticsBatch,output,0.0,*mpe_rng);
115  }
116  }
117 public:
118  ConvolutionalRBM(RngType& rng):mpe_rng(&rng),m_forward(true),m_evalMean(true)
119  { }
120 
121  /// \brief From INameable: return the class name.
122  std::string name() const
123  { return "ConvolutionalRBM"; }
124 
125  ///\brief Returns the total number of parameters of the model.
126  std::size_t numberOfParameters()const {
127  std::size_t parameters = numFilters()*filterSize1()*filterSize2();
128  parameters += m_hiddenNeurons.numberOfParameters();
129  parameters += m_visibleNeurons.numberOfParameters();
130  return parameters;
131  }
132 
133  ///\brief Returns the parameters of the Model as parameter vector.
134  RealVector parameterVector () const {
135  RealVector ret(numberOfParameters());
136  init(ret) << matrixSet(m_filters),blas::parameters(m_hiddenNeurons),blas::parameters(m_visibleNeurons);
137  return ret;
138  };
139 
140  ///\brief Sets the parameters of the model.
141  ///
142  /// @param newParameters vector of parameters
143  void setParameterVector(const RealVector& newParameters) {
144  init(newParameters) >>matrixSet(m_filters),blas::parameters(m_hiddenNeurons),blas::parameters(m_visibleNeurons);
145  }
146 
147  ///\brief Creates the structure of the ConvolutionalRBM.
148  ///
149  ///@param newInputSize1 width of input image
150  ///@param newInputSize2 height of input image
151  ///@param newNumFilters number of filters to train
152  ///@param filterSize size of the sides of the filter
154  std::size_t newInputSize1, std::size_t newInputSize2,
155  std::size_t newNumFilters,
156  std::size_t filterSize
157  ){
158  //check that we have at least one row/column of hidden units
159  SIZE_CHECK(newInputSize1 > filterSize);
160  SIZE_CHECK(newInputSize2 > filterSize);
161 
162  std::size_t numVisible = newInputSize1*newInputSize2;
163  std::size_t numHidden = (newInputSize1-filterSize+1)*(newInputSize2-filterSize+1)*newNumFilters;
164 
165  m_filters.clear();
166  m_filters.resize(newNumFilters,RealMatrix(filterSize,filterSize,0.0));
167 
168  m_hiddenNeurons.resize(numHidden);
169  m_visibleNeurons.resize(numVisible);
170 
171  m_inputSize1 = newInputSize1;
172  m_inputSize2 = newInputSize2;
173  }
174 
175  ///\brief Returns the layer of hidden neurons.
176  HiddenType const& hiddenNeurons()const{
177  return m_hiddenNeurons;
178  }
179  ///\brief Returns the layer of hidden neurons.
180  HiddenType& hiddenNeurons(){
181  return m_hiddenNeurons;
182  }
183  ///\brief Returns the layer of visible neurons.
184  VisibleType& visibleNeurons(){
185  return m_visibleNeurons;
186  }
187  ///\brief Returns the layer of visible neurons.
188  VisibleType const& visibleNeurons()const{
189  return m_visibleNeurons;
190  }
191 
192  std::size_t numFilters()const{
193  return m_filters.size();
194  }
195  std::size_t filterSize1()const{
196  return m_filters.size1();
197  }
198  std::size_t filterSize2()const{
199  return m_filters.size2();
200  }
201 
202  std::size_t inputSize1()const{
203  return m_inputSize1;
204  }
205 
206  std::size_t inputSize2()const{
207  return m_inputSize2;
208  }
209 
210 
211  std::size_t responseSize1()const{
212  return m_inputSize1-m_filters.size1()+1;
213  }
214  std::size_t responseSize2()const{
215  return m_inputSize2-m_filters.size2()+1;
216  }
217 
218  ///\brief Returns the weight matrix connecting the layers.
219  std::vector<RealMatrix>& filters(){
220  return m_filters;
221  }
222  ///\brief Returns the weight matrix connecting the layers.
223  std::vector<RealMatrix> const& weightMatrix()const{
224  return m_filters;
225  }
226 
227  ///\brief Returns the energy function of the ConvolutionalRBM.
228  EnergyType energy()const{
229  return EnergyType(*this);
230  }
231 
232  ///\brief Returns the random number generator associated with this ConvolutionalRBM.
233  RngType& rng(){
234  return *mpe_rng;
235  }
236 
237  ///\brief Sets the type of evaluation, eval will perform.
238  ///
239  ///Eval performs its operation based on the state of this function.
240  ///There are two ways to pass data through an ConvolutionalRBM: either forward, setting the states of the
241  ///visible neurons and sample the hidden states or backwards, where the state of the hidden is fixed and the visible
242  ///are sampled.
243  ///Instead of the state of the hidden/visible, one often wants the mean of the state \f$ E_{p(h|v)}\left(h\right)\f$.
244  ///By default, the ConvolutionalRBM uses the forward evaluation and returns the mean of the state
245  ///
246  ///@param forward whether the forward view should be used false=backwards
247  ///@param evalMean whether the mean state should be returned. false=a sample is returned
248  void evaluationType(bool forward,bool evalMean){
249  m_forward = forward;
250  m_evalMean = evalMean;
251  }
252 
253  boost::shared_ptr<State> createState()const{
254  return boost::shared_ptr<State>(new EmptyState());
255  }
256 
257  ///\brief Passes information through/samples from an ConvolutionalRBM in a forward or backward way.
258  ///
259  ///Eval performs its operation based on the given evaluation type.
260  ///There are two ways to pass data through an ConvolutionalRBM: either forward, setting the states of the
261  ///visible neurons and sample the hidden states or backwards, where the state of the hidden is fixed and the visible
262  ///are sampled.
263  ///Instead of the state of the hidden/visible, one often wants the mean of the state \f$ E_{p(h|v)}\left(h\right)\f$.
264  ///By default, the ConvolutionalRBM uses the forward evaluation and returns the mean of the state,
265  ///but other evaluation modes can be set by evaluationType().
266  ///
267  ///@param patterns the batch of (visible or hidden) inputs
268  ///@param outputs the batch of (visible or hidden) outputs
269  void eval(BatchInputType const& patterns,BatchOutputType& outputs)const{
270  if(m_forward){
271  evalForward(patterns,outputs);
272  }
273  else{
274  evalBackward(patterns,outputs);
275  }
276  }
277 
278 
279  void eval(BatchInputType const& patterns, BatchOutputType& outputs, State& state)const{
280  eval(patterns,outputs);
281  }
282 
283  ///\brief Calculates the input of the hidden neurons given the state of the visible in a batch-vise fassion.
284  ///
285  ///@param inputs the batch of vectors the input of the hidden neurons is stored in
286  ///@param visibleStates the batch of states of the visible neurons
287  void inputHidden(RealMatrix& inputs, RealMatrix const& visibleStates)const{
288  SIZE_CHECK(visibleStates.size1() == inputs.size1());
289  SIZE_CHECK(inputs.size2() == numberOfHN());
290  SIZE_CHECK( visibleStates.size2() == numberOfVN());
291  inputs.clear();
292 
293  for(std::size_t i= 0; i != inputs.size1();++i){
294  blas::dense_matrix_adaptor<double const> visibleState =
295  to_matrix(row(visibleStates,i),inputSize1(),inputSize2());
296  blas::dense_matrix_adaptor<double> responses =
297  to_matrix(row(inputs,i),m_filters.size()*responseSize1(),responseSize2());
298 
299  for (std::size_t x1=0; x1 != responseSize1(); ++x1) {
300  for (std::size_t x2=0; x2 != responseSize2(); ++x2) {
301  std::size_t end1= x1+m_filters.size1();
302  std::size_t end2= x2+m_filters.size2();
303  for(std::size_t f = 0; f != m_filters.size();++f){
304  responses(f*responseSize1()+x1,x2)=sum(m_filters[f]*subrange(visibleState,x1,end1,x2,end2));
305  }
306  }
307  }
308  }
309  }
310 
311 
312  ///\brief Calculates the input of the visible neurons given the state of the hidden.
313  ///
314  ///@param inputs the vector the input of the visible neurons is stored in
315  ///@param hiddenStates the state of the hidden neurons
316  void inputVisible(RealMatrix& inputs, RealMatrix const& hiddenStates)const{
317  SIZE_CHECK(hiddenStates.size1() == inputs.size1());
318  SIZE_CHECK(inputs.size2() == numberOfVN());
319  SIZE_CHECK(hiddenStates.size2() == numberOfHN());
320  typedef blas::dense_matrix_adaptor<double> Response;
321  inputs.clear();
322 
323  //we slightly optimize this routine by checking whether the hiddens are 0 -
324  //this is likely when the hiddens are binary.
325  for(std::size_t i= 0; i != inputs.size1();++i){
326  blas::dense_matrix_adaptor<double const> hiddenState =
327  to_matrix(row(hiddenStates,i),responseSize1()*m_filters.size(),responseSize2());
328  Response responses =
329  to_matrix(row(inputs,i),m_inputSize1,m_inputSize2);
330 
331  for (std::size_t x1=0; x1 != responseSize1(); ++x1) {
332  for (std::size_t x2=0; x2 != responseSize2(); ++x2) {
333  std::size_t end1= x1+m_filters.size1();
334  std::size_t end2= x2+m_filters.size2();
335  blas::matrix_range<Response> receptiveArea = subrange(responses,x1,end1,x2,end2);
336 
337  for(std::size_t f = 0; f != m_filters.size();++f){
338  double neuronResponse = hiddenState(f*responseSize1()+x1,x2);
339  if(neuronResponse == 0.0) continue;
340  noalias(receptiveArea) += neuronResponse * m_filters[f];
341  }
342  }
343  }
344  }
345  }
346 
347  using base_type::eval;
348 
349 
350  ///\brief Returns the number of hidden Neurons, that is the number of filter responses
351  std::size_t numberOfHN()const{
352  return m_hiddenNeurons.size();
353  }
354  ///\brief Returns the number of visible Neurons, which is the number of pixels of the image
355  std::size_t numberOfVN()const{
356  return m_visibleNeurons.size();
357  }
358 
359  /// \brief Reads the network from an archive.
360  void read(InArchive& archive){
361  archive >> m_filters;
362  archive >> m_hiddenNeurons;
363  archive >> m_visibleNeurons;
364 
365  //serialization of the rng is a bit...complex
366  //let's hope that we can remove this hack one time. But we really can't ignore the state of the rng.
367  std::string str;
368  archive>> str;
369  std::stringstream stream(str);
370  stream>> *mpe_rng;
371  }
372 
373  /// \brief Writes the network to an archive.
374  void write(OutArchive& archive) const{
375  archive << m_filters;
376  archive << m_hiddenNeurons;
377  archive << m_visibleNeurons;
378 
379  std::stringstream stream;
380  stream <<*mpe_rng;
381  std::string str = stream.str();
382  archive <<str;
383  }
384 
385 };
386 
387 }
388 
389 #endif