XMM - Probabilistic Models for Motion Recognition and Mapping

xmmModel.hpp
Go to the documentation of this file.
1 /*
2  * xmmModel.hpp
3  *
4  * Probabilistic machine learning model for multiclass recognition and
5  * regression
6  *
7  * Contact:
8  * - Jules Francoise <jules.francoise@ircam.fr>
9  *
10  * This code has been initially authored by Jules Francoise
11  * <http://julesfrancoise.com> during his PhD thesis, supervised by Frederic
12  * Bevilacqua <href="http://frederic-bevilacqua.net>, in the Sound Music
13  * Movement Interaction team <http://ismm.ircam.fr> of the
14  * STMS Lab - IRCAM, CNRS, UPMC (2011-2015).
15  *
16  * Copyright (C) 2015 UPMC, Ircam-Centre Pompidou.
17  *
18  * This File is part of XMM.
19  *
20  * XMM is free software: you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License as published by
22  * the Free Software Foundation, either version 3 of the License, or
23  * (at your option) any later version.
24  *
25  * XMM 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 General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with XMM. If not, see <http://www.gnu.org/licenses/>.
32  */
33 
34 #ifndef xmmModel_h
35 #define xmmModel_h
36 
38 #include "xmmModelResults.hpp"
39 #include "xmmModelSingleClass.hpp"
40 #include <atomic>
41 #include <thread>
42 
43 namespace xmm {
51 template <typename SingleClassModel, typename ModelType>
52 class Model : public Writable {
53  public:
58  Model(bool bimodal = false)
59  : shared_parameters(std::make_shared<SharedParameters>()),
60  cancel_required_(false),
61  is_training_(false),
62  is_joining_(false),
64  shared_parameters->bimodal.set(bimodal);
65  if (shared_parameters->bimodal.get()) {
66  shared_parameters->dimension.set(2, true);
67  shared_parameters->dimension_input.set(1, true);
68  } else {
69  shared_parameters->dimension.set(1, true);
70  shared_parameters->dimension_input.set(0, true);
71  }
72  }
73 
80  std::make_shared<SharedParameters>(*src.shared_parameters)),
83  cancel_required_(false),
84  is_training_(false),
85  is_joining_(false),
87  if (src.is_training_)
88  throw std::runtime_error(
89  "Cannot copy: source model is still training");
90  models = src.models;
91  for (auto& model : models) {
92  model.second.training_events.removeListeners();
93  model.second.training_events.addListener(
94  this,
96  }
97  }
98 
103  explicit Model(Json::Value const& root)
104  : shared_parameters(std::make_shared<SharedParameters>()),
105  cancel_required_(false),
106  is_training_(false),
107  is_joining_(false),
109  shared_parameters->fromJson(root["shared_parameters"]);
110  configuration.fromJson(root["configuration"]);
111  models.clear();
112  for (auto p : root["models"]) {
113  std::string l = p["label"].asString();
114  models.insert(std::pair<std::string, SingleClassModel>(
115  l, SingleClassModel(shared_parameters, p)));
116  models[l].training_events.removeListeners();
117  models[l].training_events.addListener(
118  this,
120  }
121  }
122 
129  if (this != &src) {
130  if (is_training_)
131  throw std::runtime_error(
132  "Cannot copy: target model is still training");
133  if (src.is_training_)
134  throw std::runtime_error(
135  "Cannot copy: source model is still training");
137  std::make_shared<SharedParameters>(*src.shared_parameters);
140  is_joining_ = false;
141  is_training_ = false;
142  cancel_required_ = false;
144 
145  models.clear();
146  models = src.models;
147  for (auto& model : this->models) {
148  model.second.training_events.removeListeners();
149  model.second.training_events.addListener(
150  this,
152  }
153  }
154  return *this;
155  }
156 
160  virtual ~Model() {
161  cancelTraining();
162  clear();
163  }
164 
166 
172  unsigned int size() const {
173  return static_cast<unsigned int>(models.size());
174  }
175 
181  bool hasClass(std::string const& label) const {
182  checkTraining();
183  return (models.count(label) > 0);
184  }
185 
191  int getIndex(std::string const& label) const {
192  checkTraining();
193  int i(-1);
194  for (auto& m : models) {
195  i++;
196  if (m.first == label) {
197  return i;
198  }
199  }
200  return -1;
201  }
202 
207  virtual void removeClass(std::string const& label) {
208  while (is_joining_) {
209  }
210  cancelTraining(label);
211  auto it = models.find(label);
212  if (it == models.end())
213  throw std::out_of_range("Class " + label + " does not exist");
214  models.erase(it);
215  reset();
216  }
217 
221  virtual void clear() {
223  models.clear();
224  reset();
225  }
226 
228 
230 
235  bool trained() const { return (!is_training_ && size() > 0); }
236 
240  bool training() const { return is_training_; }
241 
246  virtual void train(TrainingSet* trainingSet) {
247  if (!trainingSet) return;
248  cancelTraining();
249  clear();
250 
251  is_training_ = true;
252 
253  // Fetch training set parameters
254  shared_parameters->dimension.set(trainingSet->dimension.get());
255  if (shared_parameters->bimodal.get()) {
256  shared_parameters->dimension_input.set(
257  trainingSet->dimension_input.get());
258  }
259  shared_parameters->column_names.set(trainingSet->column_names.get());
260 
261  // Update models
262  bool contLoop(true);
263  while (contLoop) {
264  contLoop = false;
265  for (auto it = models.begin(); it != models.end(); ++it) {
266  if (trainingSet->labels().count(it->first) == 0) {
267  models.erase(it->first);
268  contLoop = true;
269  break;
270  }
271  }
272  }
273  for (typename std::set<std::string>::iterator it =
274  trainingSet->labels().begin();
275  it != trainingSet->labels().end(); ++it) {
276  addModelForClass(*it);
277  }
278 
279  // Start class training
280  for (auto it = this->models.begin(); it != this->models.end(); ++it) {
281  it->second.is_training_ = true;
282  it->second.cancel_training_ = false;
284  if ((configuration.multithreading ==
286  (configuration.multithreading ==
288  training_threads_[it->first] =
289  std::thread(&SingleClassModel::train, &it->second,
290  trainingSet->getPhrasesOfClass(it->first));
291  } else {
292  it->second.train(trainingSet->getPhrasesOfClass(it->first));
293  }
294  }
295  if (configuration.multithreading == MultithreadingMode::Parallel) {
296  joinTraining();
297  }
298  if (configuration.multithreading == MultithreadingMode::Sequential) {
299  is_training_ = false;
300  }
301  }
302 
312  virtual void train(TrainingSet* trainingSet, std::string const& label) {
313  if (!trainingSet) return;
314  if (trainingSet->labels().count(label) == 0)
315  throw std::out_of_range("Class " + label + " does not exist");
316 
317  // Cancel Training of the class
318  cancelTraining(label);
319 
320  // Check Consistency of the new Training Set
321  if (size() > 0) {
322  if ((shared_parameters->dimension.get() !=
323  trainingSet->dimension.get()) ||
324  (shared_parameters->bimodal.get() &&
325  (shared_parameters->dimension_input.get() !=
326  trainingSet->dimension_input.get())))
327  throw std::runtime_error(
328  "Dimensions of the new Training Set do not match existing "
329  "models");
330  }
331 
332  is_training_ = true;
333 
334  // Fetch training set parameters
335  shared_parameters->dimension.set(trainingSet->dimension.get());
336  if (shared_parameters->bimodal.get()) {
337  shared_parameters->dimension_input.set(
338  trainingSet->dimension_input.get());
339  }
340  shared_parameters->column_names.set(trainingSet->column_names.get());
341 
342  addModelForClass(label);
343 
344  // Start class training
345  models[label].is_training_ = true;
346  models[label].cancel_training_ = false;
348  if (configuration.multithreading == MultithreadingMode::Sequential) {
349  models[label].train(trainingSet->getPhrasesOfClass(label));
350  } else {
351  training_threads_[label] =
352  std::thread(&SingleClassModel::train, &(this->models[label]),
353  trainingSet->getPhrasesOfClass(label));
354  }
355  if (configuration.multithreading == MultithreadingMode::Parallel) {
356  joinTraining();
357  }
358  if (configuration.multithreading == MultithreadingMode::Sequential) {
359  is_training_ = false;
360  }
361  }
362 
366  void cancelTraining() {
367  if (is_training_) {
368  for (auto& it : this->models) {
369  cancel_required_ = true;
370  it.second.cancelTraining();
371  while (it.second.isTraining()) {
372  }
373  }
374  joinTraining();
375  }
376  cancel_required_ = false;
377  }
378 
383  void cancelTraining(std::string const& label) {
384  if (is_training_ && (this->models.count(label) > 0)) {
385  cancel_required_ = true;
386  models[label].cancelTraining();
387  while (models[label].isTraining()) {
388  }
389  if (models_still_training_ == 0) {
390  joinTraining();
391  }
392  }
393  cancel_required_ = false;
394  }
395 
397 
399 
404  virtual void reset() {
405  checkTraining();
406  // checkConfigurationChanges();
407  for (auto it = this->models.begin(); it != this->models.end(); ++it) {
408  it->second.reset();
409  }
410  }
411 
418  virtual void filter(std::vector<float> const& observation) {
419  checkTraining();
420  // checkConfigurationChanges();
421  }
422 
424 
426 
432  virtual Json::Value toJson() const {
433  Json::Value root;
434  root["shared_parameters"] = shared_parameters->toJson();
435  root["configuration"] = configuration.toJson();
436  root["models"].resize(static_cast<Json::ArrayIndex>(size()));
437  Json::ArrayIndex modelIndex(0);
438  for (auto& it : models) {
439  root["models"][modelIndex++] = it.second.toJson();
440  }
441  return root;
442  }
443 
449  void fromJson(Json::Value const& root) {
450  try {
451  EventGenerator<TrainingEvent> _tmp_training_events(training_events);
453  *this = tmp;
454  training_events = _tmp_training_events;
455  } catch (JsonException& e) {
456  throw e;
457  }
458  }
459 
461 
465  std::shared_ptr<SharedParameters> shared_parameters;
466 
471 
476 
480  std::map<std::string, SingleClassModel> models;
481 
482  protected:
487  virtual void joinTraining() {
488  if (!is_training_) return;
489  bool expectedState(false);
490  if (is_joining_.compare_exchange_weak(expectedState, true)) {
491  while (!training_threads_.empty()) {
492  training_threads_.begin()->second.join();
493  training_threads_.erase(training_threads_.begin());
494  }
495  bool classesUntrained(true);
496  while (classesUntrained) {
497  classesUntrained = false;
498  for (auto it = this->models.begin(); it != this->models.end();
499  it++) {
500  if (it->second.training_status.status ==
501  TrainingEvent::Status::Cancel ||
502  it->second.training_status.status ==
503  TrainingEvent::Status::Error) {
504  models.erase(it);
505  classesUntrained = true;
506  break;
507  }
508  }
509  }
510  is_joining_ = false;
511  is_training_ = false;
512  reset();
513  TrainingEvent event(this, "", TrainingEvent::Status::Alldone);
514  training_events.notifyListeners(event);
515  } else {
516  while (is_joining_) {
517  }
518  }
519  }
520 
525  event_mutex_.lock();
526  TrainingEvent event(e);
527  event.model = this;
528  training_events.notifyListeners(event);
529  if (e.status != TrainingEvent::Status::Run) {
531  ((SingleClassModel*)e.model)->is_training_ = false;
532  if (configuration.multithreading ==
534  !is_joining_ && // avoid to call "joinTraining" if already
535  // called by the main thread
536  !cancel_required_ && // avoid to call "joinTraining" if cancel
537  // required by the main thread
538  models_still_training_ == 0) {
539  is_joining_ = false;
541  this)
542  .detach();
543  }
544  }
545  event_mutex_.unlock();
546  }
547 
552  inline void checkTraining() const {
553  if (is_training_) throw std::runtime_error("The Model is training");
554  while (is_joining_) {
555  }
556  }
557 
562  inline void checkConfigurationChanges() const {
563  bool configChanged(false);
564  if (configuration.changed) configChanged = true;
565  for (auto& config : configuration.class_parameters_) {
566  if (config.second.changed) configChanged = true;
567  }
568  if (configChanged) {
569  throw std::runtime_error(
570  "Configuration has changed, models need to be trained.");
571  }
572  }
573 
579  virtual void addModelForClass(std::string const& label) {
580  if (models.count(label) > 0 && models[label].isTraining())
581  throw std::runtime_error("The Model is already training");
582 
583  if (models.find(label) == models.end()) {
584  models.insert(std::pair<std::string, SingleClassModel>(
585  label, shared_parameters));
586  models[label].training_events.addListener(
587  this,
589  models[label].label = label;
590  }
591  if (configuration.class_parameters_.count(label) > 0) {
592  models[label].parameters = configuration.class_parameters_[label];
593  } else {
594  models[label].parameters = configuration;
595  }
596  }
597 
601  std::map<std::string, std::thread> training_threads_;
602 
607  std::atomic<bool> cancel_required_;
608 
613  std::atomic<bool> is_training_;
614 
619  std::atomic<bool> is_joining_;
620 
625 
629  std::mutex event_mutex_;
630 };
631 }
632 
633 #endif
virtual void train(TrainingSet *trainingSet)
Train all classes from the training set passed in argument.
Definition: xmmModel.hpp:246
Model(Json::Value const &root)
Constructor from Json Structure.
Definition: xmmModel.hpp:103
virtual void removeClass(std::string const &label)
Remove a specific class by label.
Definition: xmmModel.hpp:207
virtual void addModelForClass(std::string const &label)
Update training set for a specific label.
Definition: xmmModel.hpp:579
T get() const
get the attribute&#39;s current value
Definition: xmmAttribute.hpp:203
bool changed
specifies if parameters have changed (model is invalid)
Definition: xmmModelParameters.hpp:76
Shared Parameters for models with multiple classes.
Definition: xmmModelSharedParameters.hpp:53
Attribute< unsigned int > dimension
total dimension of the training data
Definition: xmmTrainingSet.hpp:298
Model(Model< SingleClassModel, ModelType > const &src)
Copy Constructor.
Definition: xmmModel.hpp:78
virtual void joinTraining()
Finishes the background training process by joining threads and deleting the models which training fa...
Definition: xmmModel.hpp:487
virtual Json::Value toJson() const
Write the object to a JSON Structure.
Definition: xmmModel.hpp:432
Configuration< ModelType > configuration
Configuration (default and class-specific parameters)
Definition: xmmModel.hpp:470
void checkConfigurationChanges() const
Look for configuration changes and throws an exception if the model is not up to date.
Definition: xmmModel.hpp:562
std::atomic< bool > is_training_
locks the Model while the models are training Used in cancel method
Definition: xmmModel.hpp:613
MultithreadingMode multithreading
Multithreading Training Mode.
Definition: xmmModelConfiguration.hpp:210
virtual void clear()
Remove all classes.
Definition: xmmModel.hpp:221
void notifyListeners(EventType &e) const
Propagates the event to all listeners.
Definition: xmmEvents.hpp:99
Probabilistic machine learning model for multiclass recognition and regression.
Definition: xmmModel.hpp:52
void cancelTraining()
Cancels the training of all models.
Definition: xmmModel.hpp:366
int getIndex(std::string const &label) const
Checks if a class exists.
Definition: xmmModel.hpp:191
void * model
Source Model.
Definition: xmmModelSingleClass.hpp:140
unsigned int size() const
Get the number of classes in the model.
Definition: xmmModel.hpp:172
std::map< std::string, std::thread > training_threads_
Training Threads.
Definition: xmmModel.hpp:601
std::atomic< bool > cancel_required_
locks the Model while the models are training Used in cancel method
Definition: xmmModel.hpp:607
virtual ~Model()
Destructor.
Definition: xmmModel.hpp:160
virtual void filter(std::vector< float > const &observation)
filters a incoming observation (performs recognition or regression)
Definition: xmmModel.hpp:418
No multithreading: all classes are trained sequentially.
TrainingSet * getPhrasesOfClass(std::string const &label)
get the pointer to the sub-training set containing all phrases with a given label ...
Definition: xmmTrainingSet.cpp:276
Model configuration.
Definition: xmmModelConfiguration.hpp:89
const std::set< std::string > & labels() const
get the list of labels currently in the training set
Definition: xmmTrainingSet.hpp:234
void onTrainingEvent(TrainingEvent const &e)
Monitors the training of each Model of the group.
Definition: xmmModel.hpp:524
std::mutex event_mutex_
Mutex that prevents concurrent calls to onEvent()
Definition: xmmModel.hpp:629
Status status
Status of the training process.
Definition: xmmModelSingleClass.hpp:150
Multithreading in Background: all classes are trained in parallel in different threads. the train function returns after the training has started.
unsigned int models_still_training_
Number of models that are still training.
Definition: xmmModel.hpp:624
std::map< std::string, SingleClassModel > models
models stored in a map. Each Model is associated with a label
Definition: xmmModel.hpp:480
Base class for the definition of training sets.
Definition: xmmTrainingSet.hpp:46
Generator class for a specific type of events.
Definition: xmmEvents.hpp:47
Attribute< std::vector< std::string > > column_names
labels of the columns of the training set (e.g. descriptor names)
Definition: xmmTrainingSet.hpp:308
bool hasClass(std::string const &label) const
Checks if a class exists.
Definition: xmmModel.hpp:181
EventGenerator< TrainingEvent > training_events
Generator for training process events.
Definition: xmmModel.hpp:475
Abstract class for handling JSON + File I/O.
Definition: xmmJson.hpp:50
Multithreading: all classes are trained in parallel in different threads. the train function returns ...
Model(bool bimodal=false)
Constructor.
Definition: xmmModel.hpp:58
Model< SingleClassModel, ModelType > & operator=(Model< SingleClassModel, ModelType > const &src)
Assignment.
Definition: xmmModel.hpp:127
bool trained() const
Checks if the model is trained (training finished and not empty)
Definition: xmmModel.hpp:235
Exception class for handling JSON parsing errors.
Definition: xmmJson.hpp:127
void cancelTraining(std::string const &label)
Cancels the training of a given class.
Definition: xmmModel.hpp:383
std::vector< std::string > get() const
get the attribute&#39;s current value
Definition: xmmAttribute.hpp:546
virtual void reset()
Resets the fitering process (recognition or regression)
Definition: xmmModel.hpp:404
Event for monitoring the training process.
Definition: xmmModelSingleClass.hpp:49
void checkTraining() const
Checks if the Model is still training.
Definition: xmmModel.hpp:552
Definition: xmmAttribute.hpp:42
std::shared_ptr< SharedParameters > shared_parameters
Set of Parameters shared among classes.
Definition: xmmModel.hpp:465
std::map< std::string, ClassParameters< ModelType > > class_parameters_
Parameters for each class.
Definition: xmmModelConfiguration.hpp:222
virtual void train(TrainingSet *trainingSet, std::string const &label)
Train a specific class from the training set passed in argument.
Definition: xmmModel.hpp:312
Attribute< unsigned int > dimension_input
dimension of the input modality in bimodal mode
Definition: xmmTrainingSet.hpp:303
bool training() const
Checks if the model is still training.
Definition: xmmModel.hpp:240
std::atomic< bool > is_joining_
specifies if a thread for joining the training process has been launched.
Definition: xmmModel.hpp:619
void fromJson(Json::Value const &root)
Read the object from a JSON Structure.
Definition: xmmModel.hpp:449