iCub-main
TrainModule.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007-2010 RobotCub Consortium, European Commission FP6 Project IST-004370
3  * author: Arjan Gijsberts
4  * email: arjan.gijsberts@iit.it
5  * website: www.robotcub.org
6  * Permission is granted to copy, distribute, and/or modify this program
7  * under the terms of the GNU General Public License, version 2 or any
8  * later version published by the Free Software Foundation.
9  *
10  * A copy of the license can be found at
11  * http://www.robotcub.org/icub/license/gpl.txt
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details
17  */
18 
19 #include <iostream>
20 #include <stdexcept>
21 #include <cassert>
22 
23 #include <yarp/os/Vocab.h>
24 
28 
29 namespace iCub {
30 namespace learningmachine {
31 
32 void TrainProcessor::onRead(yarp::os::PortablePair<yarp::sig::Vector,yarp::sig::Vector>& sample) {
33  if(this->getMachinePortable().hasWrapped() && this->enabled) {
34  try {
35  // Event Code
36  if(EventDispatcher::instance().hasListeners()) {
37  Prediction prediction = this->getMachine().predict(sample.head);
38  TrainEvent te(sample.head, sample.body, prediction);
40  }
41  // Event Code
42 
43  this->getMachine().feedSample(sample.head, sample.body);
44 
45  } catch(const std::exception& e) {
46  std::cerr << "Error: " << e.what() << std::endl;
47  }
48  }
49 
50  return;
51 }
52 
53 
54 void TrainModule::printOptions(std::string error) {
55  if(error != "") {
56  std::cout << "Error: " << error << std::endl;
57  }
58  std::cout << "Available options for training module" << std::endl;
59  std::cout << "--help Display this help message" << std::endl;
60  std::cout << "--list Print a list of available algorithms" << std::endl;
61  std::cout << "--load file Load serialized machine from a file" << std::endl;
62  std::cout << "--machine type Desired type of learning machine" << std::endl;
63  std::cout << "--port pfx Prefix for registering the ports" << std::endl;
64  std::cout << "--commands file Load configuration commands from a file" << std::endl;
65 }
66 
67 
68 void TrainModule::printMachineList() {
69  std::vector<std::string> keys = FactoryT<std::string, IMachineLearner>::instance().getKeys();
70  std::cout << "Available Machine Learners:" << std::endl;
71  for(unsigned int i = 0; i < keys.size(); i++) {
72  std::cout << " " << keys[i] << std::endl;
73  }
74 }
75 
76 
77 void TrainModule::registerAllPorts() {
78  // ports from PredictModule, without model:i
79  //this->registerPort(this->model_in, "/" + this->portPrefix + "/model:i");
80  this->registerPort(this->predict_inout, this->portPrefix + "/predict:io");
81  this->predict_inout.setStrict();
82  this->registerPort(this->cmd_in, this->portPrefix + "/cmd:i");
83 
84  this->registerPort(this->model_out, this->portPrefix + "/model:o");
85  this->registerPort(this->train_in, this->portPrefix + "/train:i");
86  this->train_in.setStrict();
87 }
88 
89 void TrainModule::unregisterAllPorts() {
91  this->train_in.close();
92  this->model_out.close();
93 }
94 
97  train_in.interrupt();
98  return true;
99 }
100 
101 bool TrainModule::configure(yarp::os::ResourceFinder& opt) {
102  /* Implementation note:
103  * Calling open() in the base class (i.e. PredictModule) is cumbersome due
104  * to different ordering and dynamic binding (e.g. it calls
105  * registerAllPorts()) and because we do bother with an incoming model port.
106  */
107 
108  // read for the general specifiers:
109  yarp::os::Value* val;
110  std::string machineName;
111 
112  // cache resource finder
113  this->setResourceFinder(&opt);
114 
115  // check for help request
116  if(opt.check("help")) {
117  this->printOptions();
118  return false;
119  }
120 
121  // check for algorithm listing request
122  if(opt.check("list")) {
123  this->printMachineList();
124  return false;
125  }
126 
127  // check for port specifier: portSuffix
128  if(opt.check("port", val)) {
129  this->portPrefix = val->asString().c_str();
130  }
131 
132  // check for filename to load machine from
133  if(opt.check("load", val)) {
134  this->getMachinePortable().readFromFile(val->asString().c_str());
135  } else{
136  // not loading anything, require machine name
137  if(opt.check("machine", val)) {
138  machineName = val->asString().c_str();
139  } else {
140  this->printOptions("No machine type specified");
141  return false;
142  }
143 
144  // construct new machine
145  this->getMachinePortable().setWrapped(machineName);
146 
147  // send configuration options to the machine
148  this->getMachine().configure(opt);
149  }
150 
151 
152  // add replier for incoming data (prediction requests)
153  this->predict_inout.setReplier(this->predictProcessor);
154 
155  // add processor for incoming data (training samples)
156  this->train_in.useCallback(trainProcessor);
157 
158  // register ports before connecting
159  this->registerAllPorts();
160 
161  // and finally load command file
162  if(opt.check("commands", val)) {
163  this->loadCommandFile(val->asString().c_str());
164  }
165 
166  // attach to the incoming command port and terminal
167  this->attach(cmd_in);
168  this->attachTerminal();
169 
170  return true;
171 }
172 
173 
174 bool TrainModule::respond(const yarp::os::Bottle& cmd, yarp::os::Bottle& reply) {
175  // NOTE: the module class spawns a new thread, which implies that exception
176  // handling needs to be done in this thread, so not the 'main' thread.
177  bool success = false;
178 
179  try {
180  switch(cmd.get(0).asVocab32()) {
181  case yarp::os::createVocab32('h','e','l','p'): // print help information
182  reply.add(yarp::os::Value::makeVocab32("help"));
183 
184  reply.addString("Training module configuration options");
185  reply.addString(" help Displays this message");
186  reply.addString(" train Trains the machine and sends the model");
187  reply.addString(" model Sends the model to the prediction module");
188  reply.addString(" reset Resets the machine to its current state");
189  reply.addString(" info Outputs information about the machine");
190  reply.addString(" pause Disable passing the samples to the machine");
191  reply.addString(" continue Enable passing the samples to the machine");
192  reply.addString(" set key val Sets a configuration option for the machine");
193  reply.addString(" load fname Loads a machine from a file");
194  reply.addString(" save fname Saves the current machine to a file");
195  reply.addString(" event [cmd ...] Sends commands to event dispatcher (see: event help)");
196  reply.addString(" cmd fname Loads commands from a file");
197  reply.addString(this->getMachine().getConfigHelp().c_str());
198  success = true;
199  break;
200 
201  case yarp::os::createVocab32('t','r','a','i'): // train the machine, implies sending model
202  this->getMachine().train();
203  reply.addString("Training completed.");
204 
205  case yarp::os::createVocab32('m','o','d','e'): // send model
206  this->model_out.write(this->machinePortable);
207  reply.addString("The model has been written to the port.");
208  success = true;
209  break;
210 
211  case yarp::os::createVocab32('c','l','e','a'): // clear machine
212  case yarp::os::createVocab32('c','l','r'):
213  case yarp::os::createVocab32('r','e','s','e'): // reset
214  case yarp::os::createVocab32('r','s','t'):
215  this->getMachine().reset();
216  reply.addString("Machine cleared.");
217  success = true;
218  break;
219 
220  case yarp::os::createVocab32('p','a','u','s'): // pause sample stream
221  case yarp::os::createVocab32('d','i','s','a'): // disable
222  this->trainProcessor.setEnabled(false);
223  reply.addString("Sample stream to machine disabled.");
224  success = true;
225  break;
226 
227  case yarp::os::createVocab32('c','o','n','t'): // continue sample stream
228  case yarp::os::createVocab32('e','n','a','b'): // enable
229  this->trainProcessor.setEnabled(true);
230  reply.addString("Sample stream to machine enabled.");
231  success = true;
232  break;
233 
234  case yarp::os::createVocab32('i','n','f','o'): // information
235  case yarp::os::createVocab32('s','t','a','t'): // statistics
236  { // prevent identifier initialization to cross borders of case
237  reply.add(yarp::os::Value::makeVocab32("help"));
238  reply.addString("Machine Information: ");
239  reply.addString(this->getMachine().getInfo().c_str());
240  success = true;
241  break;
242  }
243 
244  case yarp::os::createVocab32('l','o','a','d'): // load
245  { // prevent identifier initialization to cross borders of case
246  reply.add(yarp::os::Value::makeVocab32("help"));
247  std::string replymsg = std::string("Loading machine from '") +
248  cmd.get(1).asString().c_str() + "'... " ;
249  if(!cmd.get(1).isString()) {
250  replymsg += "failed";
251  } else {
252  this->getMachinePortable().readFromFile(cmd.get(1).asString().c_str());
253  replymsg += "succeeded";
254  }
255  reply.addString(replymsg.c_str());
256  success = true;
257  break;
258  }
259 
260  case yarp::os::createVocab32('s','a','v','e'): // save
261  { // prevent identifier initialization to cross borders of case
262  reply.add(yarp::os::Value::makeVocab32("help"));
263  std::string replymsg = std::string("Saving machine to '") +
264  cmd.get(1).asString().c_str() + "'... " ;
265  if(!cmd.get(1).isString()) {
266  replymsg += "failed";
267  } else {
268  this->getMachinePortable().writeToFile(cmd.get(1).asString().c_str());
269  replymsg += "succeeded";
270  }
271  reply.addString(replymsg.c_str());
272  success = true;
273  break;
274  }
275 
276  case yarp::os::createVocab32('s','e','t'): // set a configuration option for the machine
277  { // prevent identifier initialization to cross borders of case
278  yarp::os::Bottle property;
279  /*
280  * This is a simple hack to enable multiple parameters The need
281  * for this hack lies in the fact that a group can only be found
282  * using findGroup if it is a nested list in a Bottle. If the
283  * Bottle itself is the list, then the group will _not_ be found.
284  */
285  property.addList() = cmd.tail();
286  std::string replymsg = "Setting configuration option ";
287  bool ok = this->getMachine().configure(property);
288  replymsg += ok ? "succeeded" :
289  "failed; please check key and value type.";
290  reply.addString(replymsg.c_str());
291  success = true;
292  break;
293  }
294 
295  case yarp::os::createVocab32('e','v','e','n'): // event
296  { // prevent identifier initialization to cross borders of case
297  success = this->dmanager.respond(cmd.tail(), reply);
298  break;
299  }
300 
301  case yarp::os::createVocab32('c','m','d'): // cmd
302  case yarp::os::createVocab32('c','o','m','m'): // command
303  { // prevent identifier initialization to cross borders of case
304  reply.add(yarp::os::Value::makeVocab32("help"));
305  std::string replymsg;
306  if(!cmd.get(1).isString()) {
307  replymsg = "Please supply a valid filename.";
308  } else {
309  std::string full_fname = this->findFile(cmd.get(1).asString().c_str());
310  replymsg = std::string("Loading commands from '") +
311  full_fname + "'... " ;
312  this->loadCommandFile(full_fname, &reply);
313  replymsg += "succeeded";
314  }
315  reply.addString(replymsg.c_str());
316  success = true;
317  break;
318  }
319 
320  default:
321  break;
322 
323  }
324  } catch(const std::exception& e) {
325  std::string msg = std::string("Error: ") + e.what();
326  reply.addString(msg.c_str());
327  success = true;
328  }
329 
330  return success;
331 }
332 
333 } // learningmachine
334 } // iCub
bool respond(const yarp::os::Bottle &cmd, yarp::os::Bottle &reply)
Respond to a command or configuration message.
virtual void raise(IEvent &event)
Raises an IEvent, causing it to be dispatched to each registered IEventListener.
static EventDispatcher & instance()
An instance retrieval method that follows the Singleton pattern.
static FactoryT< K, T > & instance()
An instance retrieval method that follows the Singleton pattern.
Definition: FactoryT.h:86
DispatcherManager dmanager
An instance of the DispatchManager to configure event listeners.
std::string portPrefix
A prefix path for the ports that will be registered.
virtual void setResourceFinder(yarp::os::ResourceFinder *rf)
Mutator for the locally stored ResourceFinder.
std::string findFile(std::string fname)
Finds the full path to a specified filename using the ResourceFinder.
void registerPort(yarp::os::Contactable &port, std::string name)
Register a port with a given name.
yarp::os::Port cmd_in
An input port for commands.
virtual void loadCommandFile(std::string fname, yarp::os::Bottle *out=(yarp::os::Bottle *) 0)
Reads bottles from a file and sends these one by one to the respond method.
virtual bool configure(yarp::os::Searchable &config)
Change parameters.
virtual void reset()=0
Forget everything and start over.
virtual void train()
Train the learning machine on the examples that have been supplied so far.
virtual Prediction predict(const yarp::sig::Vector &input)=0
Ask the learning machine to predict the output for a given input.
virtual void feedSample(const yarp::sig::Vector &input, const yarp::sig::Vector &output)=0
Provide the learning machine with an example of the desired mapping.
virtual MachinePortable & getMachinePortable()
Retrieve the machine portable machine wrapper.
Definition: PredictModule.h:57
virtual IMachineLearner & getMachine()
Convenience function to quickly retrieve the machine that is wrapped in the portable machine wrapper.
Definition: PredictModule.h:67
void setWrapped(T *w, bool wipe=true)
The mutator for the wrapped object.
Definition: PortableT.h:228
bool writeToFile(std::string filename)
Writes a wrapped object to a file.
Definition: PortableT.h:161
bool readFromFile(std::string filename)
Reads a wrapped object from a file.
Definition: PortableT.h:182
void unregisterAllPorts()
Unregisters all ports used by this module.
yarp::os::BufferedPort< yarp::sig::Vector > predict_inout
Buffered port for the incoming samples and corresponding replies.
MachinePortable machinePortable
A concrete wrapper around a learning machine.
virtual MachinePortable & getMachinePortable()
Retrieve the machine portable.
PredictProcessor predictProcessor
The processor handling prediction requests.
virtual IMachineLearner & getMachine()
Retrieve the machine that is wrapped in the portable machine wrapper.
A class that represents a prediction result.
Definition: Prediction.h:44
A TrainEvent is raised when the machine handles a training sample.
Definition: TrainEvent.h:44
virtual bool configure(yarp::os::ResourceFinder &opt)
virtual bool respond(const yarp::os::Bottle &cmd, yarp::os::Bottle &reply)
virtual void setEnabled(bool val)
Enables or disables processing of training samples.
Definition: TrainModule.h:60
virtual void onRead(yarp::os::PortablePair< yarp::sig::Vector, yarp::sig::Vector > &sample)
Definition: TrainModule.cpp:32
cmd
Definition: dataTypes.h:30
This file contains the definition of unique IDs for the body parts and the skin parts of the robot.