ConcatenatedModel.h
Go to the documentation of this file.
1 //===========================================================================
2 /*!
3  *
4  *
5  * \brief concatenation of two models, with type erasure
6  *
7  *
8  *
9  * \author O. Krause
10  * \date 2010-2011
11  *
12  *
13  * \par Copyright 1995-2015 Shark Development Team
14  *
15  * <BR><HR>
16  * This file is part of Shark.
17  * <http://image.diku.dk/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_MODEL_CONCATENATEDMODEL_H
36 #define SHARK_MODEL_CONCATENATEDMODEL_H
37 
39 #include <boost/scoped_ptr.hpp>
40 #include <boost/serialization/scoped_ptr.hpp>
41 
42 namespace shark {
43 
44 namespace detail{
45 
46 
47 ///\brief Baseclass for the wrapper which is used to hide the matrix type.
48 ///
49 ///Additional to the requirement of a Model, a clone() method must be implemented which is used to
50 ///copy a wrapper
51 template<class InputType, class OutputType>
52 class ConcatenatedModelWrapperBase:public AbstractModel<InputType,OutputType>{
53 public:
54  ConcatenatedModelWrapperBase():m_optimizeFirst(true), m_optimizeSecond(true){}
55  virtual ConcatenatedModelWrapperBase<InputType,OutputType>* clone() const = 0;
56 
57  bool optimizeFirstModelParameters()const{
58  return m_optimizeFirst;
59  }
60 
61  bool& optimizeFirstModelParameters(){
62  return m_optimizeFirst;
63  }
64 
65  bool optimizeSecondModelParameters()const{
66  return m_optimizeSecond;
67  }
68 
69  bool& optimizeSecondModelParameters(){
70  return m_optimizeSecond;
71  }
72 protected:
73  bool m_optimizeFirst;
74  bool m_optimizeSecond;
75 };
76 
77 ///\brief Internal Wrappertype to connect the output of the first model with the input of the second model.
78 ///
79 ///This model is also created when concatenating two models with operator>> (firstModel>>secondModel)
80 template<class InputType, class IntermediateType, class OutputType>
81 class ConcatenatedModelWrapper : public ConcatenatedModelWrapperBase<InputType, OutputType> {
82 protected:
83  typedef typename AbstractModel<InputType,IntermediateType>::BatchOutputType BatchIntermediateType;
84  AbstractModel<InputType,IntermediateType>* m_firstModel;
85  AbstractModel<IntermediateType,OutputType>* m_secondModel;
86 
87  typedef ConcatenatedModelWrapperBase<InputType, OutputType> base_type;
88  using base_type::m_optimizeFirst;
89  using base_type::m_optimizeSecond;
90 
91  struct InternalState: public State{
92  BatchIntermediateType intermediateResult;
93  boost::shared_ptr<State> firstModelState;
94  boost::shared_ptr<State> secondModelState;
95  };
96 public:
97  typedef typename base_type::BatchInputType BatchInputType;
98 
99  typedef typename base_type::BatchOutputType BatchOutputType;
100  ConcatenatedModelWrapper(
101  AbstractModel<InputType, IntermediateType>* firstModel,
102  AbstractModel<IntermediateType, OutputType>* secondModel)
103  : m_firstModel(firstModel), m_secondModel(secondModel)
104  {
105  if (firstModel->hasFirstParameterDerivative()
106  && secondModel->hasFirstParameterDerivative()
107  && secondModel ->hasFirstInputDerivative())
108  {
109  this->m_features |= base_type::HAS_FIRST_PARAMETER_DERIVATIVE;
110  }
111 
112  if (firstModel->hasFirstInputDerivative()
113  && secondModel->hasFirstInputDerivative())
114  {
115  this->m_features |= base_type::HAS_FIRST_INPUT_DERIVATIVE;
116  }
117  }
118 
119  /// \brief From INameable: return the class name.
120  std::string name() const
121  { return "Concatenation<" + m_firstModel->name() + "," + m_secondModel->name() + ">"; }
122 
123  ConcatenatedModelWrapperBase<InputType, OutputType>* clone()const{
124  return new ConcatenatedModelWrapper<InputType, IntermediateType, OutputType>(*this);
125  }
126 
127  RealVector parameterVector() const {
128  RealVector params(numberOfParameters());
129  if(m_optimizeFirst && m_optimizeSecond)
130  init(params) << parameters(*m_firstModel), parameters(*m_secondModel);
131  else if (m_optimizeFirst)
132  params = m_firstModel->parameterVector();
133  else if (m_optimizeSecond)
134  params = m_secondModel->parameterVector();
135  return params;
136  }
137 
138  void setParameterVector(RealVector const& newParameters) {
139  if(m_optimizeFirst && m_optimizeSecond)
140  init(newParameters) >> parameters(*m_firstModel), parameters(*m_secondModel);
141  else if (m_optimizeFirst)
142  m_firstModel->setParameterVector(newParameters);
143  else if (m_optimizeSecond)
144  m_secondModel->setParameterVector(newParameters);
145 
146  }
147 
148  boost::shared_ptr<State> createState()const{
149  InternalState* state = new InternalState();
150  boost::shared_ptr<State> ptrState(state);
151  state->firstModelState = m_firstModel->createState();
152  state->secondModelState = m_secondModel->createState();
153  return ptrState;
154  }
155 
156  std::size_t numberOfParameters() const {
157  std::size_t numParams = 0;
158  if(m_optimizeFirst)
159  numParams += m_firstModel->numberOfParameters();
160  if (m_optimizeSecond)
161  numParams += m_secondModel->numberOfParameters();
162  return numParams;
163 
164  }
165 
166  void eval( BatchInputType const& patterns, BatchOutputType& outputs)const{
167  m_secondModel->eval(
168  (*m_firstModel)(patterns),
169  outputs
170  );
171  }
172 
173  void eval( BatchInputType const& patterns, BatchOutputType& outputs, State& state)const{
174  InternalState& s = state.toState<InternalState>();
175  m_firstModel->eval(patterns, s.intermediateResult,*s.firstModelState);
176  m_secondModel->eval(s.intermediateResult, outputs,*s.secondModelState);
177  }
178 
179  void weightedParameterDerivative(
180  BatchInputType const& patterns, BatchOutputType const& coefficients, State const& state, RealVector& gradient
181  )const{
182  InternalState const& s = state.toState<InternalState>();
183 
184  //don't compute the derivative of the first model if it does not have parameters.
185  std::size_t numParamsFirst = m_firstModel->numberOfParameters();
186  if(m_optimizeFirst && m_optimizeSecond && numParamsFirst != 0){
187  RealVector firstParameterDerivative;
188  BatchIntermediateType secondInputDerivative;
189  RealVector secondParameterDerivative;
190 
191  m_secondModel->weightedDerivatives(
192  s.intermediateResult,coefficients,*s.secondModelState,
193  secondParameterDerivative,secondInputDerivative
194  );
195  m_firstModel->weightedParameterDerivative(patterns,secondInputDerivative,*s.firstModelState,firstParameterDerivative);
196 
197  gradient.resize(numberOfParameters());
198  init(gradient)<<firstParameterDerivative,secondParameterDerivative;
199  }else if(m_optimizeFirst && numParamsFirst != 0){
200  RealVector firstParameterDerivative;
201  BatchIntermediateType secondInputDerivative;
202 
203  m_secondModel->weightedInputDerivative(
204  s.intermediateResult,coefficients,*s.secondModelState,secondInputDerivative
205  );
206  m_firstModel->weightedParameterDerivative(patterns,secondInputDerivative,*s.firstModelState,gradient);
207  }else if(m_optimizeSecond){
208  m_secondModel->weightedParameterDerivative(
209  s.intermediateResult,coefficients,*s.secondModelState,
210  gradient
211  );
212  }else {
213  gradient.resize(0);
214  }
215  }
216 
217  void weightedInputDerivative(
218  BatchInputType const& patterns, BatchOutputType const& coefficients, State const& state, BatchOutputType& gradient
219  )const{
220  InternalState const& s = state.toState<InternalState>();
221  BatchIntermediateType secondInputDerivative;
222  m_secondModel->weightedInputDerivative(s.intermediateResult, coefficients, *s.secondModelState, secondInputDerivative);
223  m_firstModel->weightedInputDerivative(patterns, secondInputDerivative, *s.firstModelState, gradient);
224  }
225 
226  //special implementation, because we can reuse the input derivative of the second model for the calculation of both derivatives of the first
227  virtual void weightedDerivatives(
228  BatchInputType const & patterns,
229  BatchOutputType const & coefficients,
230  State const& state,
231  RealVector& parameterDerivative,
232  BatchInputType& inputDerivative
233  )const{
234  InternalState const& s = state.toState<InternalState>();
235  std::size_t firstParam=m_firstModel->numberOfParameters();
236  std::size_t secondParam=m_secondModel->numberOfParameters();
237  parameterDerivative.resize(firstParam+secondParam);
238 
239  RealVector firstParameterDerivative;
240  BatchIntermediateType secondInputDerivative;
241  RealVector secondParameterDerivative;
242  if(m_optimizeSecond){
243  m_secondModel->weightedDerivatives(
244  s.intermediateResult, coefficients, *s.firstModelState, secondParameterDerivative, secondInputDerivative
245  );
246  }else{
247  m_secondModel->weightedInputDerivative(
248  s.intermediateResult, coefficients, *s.firstModelState, secondInputDerivative
249  );
250  }
251  if(m_optimizeFirst){
252  m_firstModel->weightedDerivatives(
253  patterns, secondInputDerivative, *s.secondModelState, parameterDerivative, inputDerivative
254  );
255  }else{
256  m_firstModel->weightedInputDerivative(
257  patterns, secondInputDerivative, *s.secondModelState, inputDerivative
258  );
259  }
260 
261  parameterDerivative.resize(firstParam+secondParam);
262  init(parameterDerivative)<<firstParameterDerivative,secondParameterDerivative;
263  }
264  /// From ISerializable
265  void read( InArchive & archive ){
266  m_firstModel->read(archive);
267  m_secondModel->read(archive);
268  archive >> m_optimizeFirst;
269  archive >> m_optimizeSecond;
270  }
271 
272  /// From ISerializable
273  void write( OutArchive & archive ) const{
274  m_firstModel->write(archive);
275  m_secondModel->write(archive);
276  archive << m_optimizeFirst;
277  archive << m_optimizeSecond;
278  }
279 };
280 
281 ///\brief When using operator>> to connect more than two models, this type is created.
282 ///
283 ///When concatenating two models, the ConcatenatedModelWrapper is created. But it is only a temporary object.
284 ///Thus when concatenating it with another model, it must be made persistent. We do that by simply calling clone() and saving the now
285 ///persistens pointer. Note, that the right-hand-side is not allowed to be a ConcatenatedModelWrapperBase. This is not checked.
286 template<class InputType, class IntermediateType, class OutputType>
287 class ConcatenatedModelList:public ConcatenatedModelWrapper<InputType,IntermediateType,OutputType>{
288 private:
289  typedef ConcatenatedModelWrapper<InputType,IntermediateType,OutputType> base_type;
290  typedef ConcatenatedModelWrapperBase<InputType,IntermediateType> FirstModelType;
291 public:
292 
293  ConcatenatedModelList(
294  const FirstModelType& firstModel,
295  AbstractModel<IntermediateType, OutputType>* secondModel
296  ):base_type(firstModel.clone(),secondModel){
297  if (base_type::m_firstModel->hasFirstParameterDerivative()
298  && secondModel->hasFirstParameterDerivative()
299  && secondModel ->hasFirstInputDerivative())
300  {
301  this->m_features |= base_type::HAS_FIRST_PARAMETER_DERIVATIVE;
302  }
303 
304  if (base_type::m_firstModel->hasFirstInputDerivative()
305  && secondModel->hasFirstInputDerivative())
306  {
307  this->m_features |= base_type::HAS_FIRST_INPUT_DERIVATIVE;
308  }
309  }
310 
311  ~ConcatenatedModelList(){
312  delete base_type::m_firstModel;
313  }
314 
315  /// \brief From INameable: return the class name.
316  std::string name() const
317  { return "Concatenation<" + base_type::m_firstModel->name() + "," + base_type::m_secondModel->name() + ">"; }
318 
319  ConcatenatedModelWrapperBase<InputType, OutputType>* clone()const{
320  return new ConcatenatedModelList<InputType, IntermediateType, OutputType>(
321  *static_cast<FirstModelType*>(base_type::m_firstModel),//get the type information back
322  base_type::m_secondModel
323  );
324  }
325 };
326 
327 }
328 
329 ///\brief Connects two AbstractModels so that the output of the first model is the input of the second.
330 ///
331 ///The type of the output of the first model must match the type of the input of the second model exactly.
332 template<class InputT,class IntermediateT,class OutputT>
333 detail::ConcatenatedModelWrapper<InputT,IntermediateT,OutputT>
335  return detail::ConcatenatedModelWrapper<InputT,IntermediateT,OutputT> (&firstModel,&secondModel);
336 }
337 
338 ///\brief Connects another AbstractModel two a previously created connection of models
339 template<class InputT,class IntermediateT,class OutputT>
340 detail::ConcatenatedModelList<InputT,IntermediateT,OutputT>
342  const detail::ConcatenatedModelWrapperBase<InputT,IntermediateT>& firstModel,
344 ){
345  return detail::ConcatenatedModelList<InputT,IntermediateT,OutputT> (firstModel,&secondModel);
346 }
347 
348 
349 
350 ///\brief ConcatenatedModel concatenates two models such that the output of the first model is input to the second.
351 ///
352 ///Sometimes a series of models is needed to generate the desired output. For example when input data needs to be
353 ///normalized before it can be put into the trained model. In this case, the ConcatenatedModel can be used to
354 ///represent this series as one model.
355 ///The easiest way to do is is using the operator >> of AbstractModel:
356 ///ConcatenatedModel<InputType,OutputType> model = model1>>model2;
357 ///InputType must be the type of input model1 receives and model2 the output of model2. The output of model1 and input
358 ///of model2 must match. Another way of construction is calling the constructor of ConcatenatedModel using the constructor:
359 /// ConcatenatedModel<InputType,OutputType> model (&modell,&model2);
360 ///warning: model1 and model2 must outlive model. When they are destroyed first, behavior is undefined.
361 template<class InputType, class OutputType>
362 class ConcatenatedModel: public AbstractModel<InputType,OutputType> {
363 private:
364  boost::scoped_ptr<detail::ConcatenatedModelWrapperBase<InputType, OutputType> > m_wrapper;
365  typedef AbstractModel<InputType, OutputType> base_type;
366 
367 public:
370 
371 
372  ///creates a concatenated model using two base model. this is equivalent to concModel = *firstModel >> *secondModel;
373  template<class T>
375  m_wrapper.reset(new detail::ConcatenatedModelWrapper<InputType, T, OutputType>(firstModel, secondModel));
376  if (m_wrapper->hasFirstParameterDerivative()){
377  this->m_features |= base_type::HAS_FIRST_PARAMETER_DERIVATIVE;
378  }
379 
380  if (m_wrapper->hasFirstInputDerivative()){
381  this->m_features |= base_type::HAS_FIRST_INPUT_DERIVATIVE;
382  }
383  }
384  ///copy constructor to allow ConcatenatedModel concModel = model1 >> model2 >> model3;
385  ConcatenatedModel(const detail::ConcatenatedModelWrapperBase<InputType,OutputType>& wrapper) {
386  m_wrapper.reset(wrapper.clone());
387  if (m_wrapper->hasFirstParameterDerivative()){
388  this->m_features |= base_type::HAS_FIRST_PARAMETER_DERIVATIVE;
389  }
390 
391  if (m_wrapper->hasFirstInputDerivative()){
392  this->m_features |= base_type::HAS_FIRST_INPUT_DERIVATIVE;
393  }
394  }
395  ///operator = to allow concModel = model1 >> model2 >> model3; for a previously declared concatenadel model
396  ConcatenatedModel<InputType,OutputType>& operator = ( detail::ConcatenatedModelWrapperBase<InputType,OutputType>& wrapper ){
397  m_wrapper.reset(wrapper.clone());
398  if (m_wrapper->hasFirstParameterDerivative()){
399  this->m_features |= base_type::HAS_FIRST_PARAMETER_DERIVATIVE;
400  }
401 
402  if (m_wrapper->hasFirstInputDerivative()){
403  this->m_features |= base_type::HAS_FIRST_INPUT_DERIVATIVE;
404  }
405 
406  return *this;
407  }
408 
409  /// \brief Returns whether the parameters of the first model are going to be optimized
410  ///
411  /// Remember that concatModel = first >> second, so it is the lower layer.
413  return m_wrapper->optimizeFirstModelParameters();
414  }
415 
416  /// \brief Returns a variable indicting whether the parameters of the first model are going to be optimized
417  ///
418  /// Remember that concatModel = first >> second, so it is the lower layer.
420  return m_wrapper->optimizeFirstModelParameters();
421  }
422 
423  /// \brief Returns whether the parameters of the second model are going to be optimized
424  ///
425  /// Remember that concatModel = first >> second, so it is the upper layer.
427  return m_wrapper->optimizeSecondModelParameters();
428  }
429 
430  /// \brief Returns a variable indicting whether the parameters of the second model are going to be optimized
431  ///
432  /// Remember that concatModel = first >> second, so it is the upper layer.
434  return m_wrapper->optimizeSecondModelParameters();
435  }
436 
438  :m_wrapper(src.m_wrapper->clone()) {
439  this->m_features = src.m_features;
440  }
441 
442  /// \brief From INameable: return the class name.
443  std::string name() const
444  { return m_wrapper->name(); }
445 
448  swap(m_wrapper,copy.m_wrapper);
449  std::swap(base_type::m_features,copy.m_features);
450  return *this;
451  }
452 
453  RealVector parameterVector() const {
454  return m_wrapper->parameterVector();
455  }
456 
457  void setParameterVector(RealVector const& newParameters) {
458  m_wrapper->setParameterVector(newParameters);
459  }
460 
461  size_t numberOfParameters() const {
462  return m_wrapper->numberOfParameters();
463  }
464 
465  boost::shared_ptr<State> createState()const{
466  return m_wrapper->createState();
467  }
468 
469  using base_type::eval;
470  void eval(BatchInputType const& patterns, BatchOutputType& outputs)const {
471  m_wrapper->eval(patterns, outputs);
472  }
473  void eval(BatchInputType const& patterns, BatchOutputType& outputs, State& state)const {
474  m_wrapper->eval(patterns, outputs, state);
475  }
476 
478  BatchInputType const& patterns, BatchOutputType const& coefficients, State const& state, RealVector& gradient
479  )const{
480  m_wrapper->weightedParameterDerivative(patterns, coefficients, state, gradient);
481  }
482 
484  BatchInputType const& patterns, BatchOutputType const& coefficients, State const& state, BatchOutputType& derivatives
485  )const{
486  m_wrapper->weightedInputDerivative(patterns, coefficients, state, derivatives);
487  }
488 
489  virtual void weightedDerivatives(
490  BatchInputType const & patterns,
491  BatchOutputType const & coefficients,
492  State const& state,
493  RealVector& parameterDerivative,
494  BatchInputType& inputDerivative
495  )const{
496  m_wrapper->weightedDerivatives(patterns, coefficients, state, parameterDerivative,inputDerivative);
497  }
498 
499  /// From ISerializable
500  void read( InArchive & archive ){
501  m_wrapper->read(archive);
502  }
503 
504  /// From ISerializable
505  void write( OutArchive & archive ) const{
506  m_wrapper->write(archive);
507  }
508 };
509 
510 
511 }
512 #endif