Release 1.0 is almost out

This commit is contained in:
blacklight 2009-09-02 21:16:47 +02:00
parent def2cc98e0
commit 89b0ad2f8a
10 changed files with 329 additions and 126 deletions

View file

@ -1,3 +1,25 @@
--- Release 1.0 ---
2009-09-02 BlackLight <blacklight@autistici.org>
* all: Most of the code has been rewritten. Now a trained network should
be saved, via save() method, to an XML file, and loaded from that using
the constructor. This way is much cleaner, and easily fixable and
customizable too. The old approach through binary files has been kept yet
through saveToBinary() and loadFromBinary() methods, for
back-compatibility purposes, though it's strongly deprecated. Moreover,
the XML formats (for training the network and for loading a trained
network's information) are fully compatible with the ones used by
NeuralPerl module, mostly a Perl version of this library, so you can use
the same XML files to train and load a neural network using C++ and Perl.
Moreover, a great step forward in the fixing of the dirty old
vulnerability (synaptical random weights overflow) has been made, keeping
the values of the weights very low and ~ 0. I've made tons of tests and
the network never overflowed. Anyway, if you should experience an
overflow (synaptical weights go > 1), just write me. I've also done a
deep re-design of library's classes, using as public methods only the
stuff you *REALLY* need to be public.
--- Release 0.4 --- --- Release 0.4 ---
2009-08-16 BlackLight <blacklight@autistici.org> 2009-08-16 BlackLight <blacklight@autistici.org>

View file

@ -1 +1 @@
0.4 1.0

View file

@ -2,10 +2,9 @@ all:
g++ -Wall -o learnAdd learnAdd.cpp -lneural++ g++ -Wall -o learnAdd learnAdd.cpp -lneural++
g++ -Wall -o doAdd doAdd.cpp -lneural++ g++ -Wall -o doAdd doAdd.cpp -lneural++
g++ -Wall -o adderFromScratch adderFromScratch.cpp -lneural++ g++ -Wall -o adderFromScratch adderFromScratch.cpp -lneural++
g++ -Wall -o Add Add.cpp -lneural++
clean: clean:
rm learnAdd rm learnAdd
rm doAdd rm doAdd
rm adderFromScratch rm adderFromScratch
rm adder.net
rm adder.xml

View file

@ -12,7 +12,7 @@
using namespace std; using namespace std;
using namespace neuralpp; using namespace neuralpp;
#define NETFILE "adder.net" #define NETFILE "network.xml"
int main() { int main() {
double a,b; double a,b;

View file

@ -25,10 +25,10 @@ int main() {
// => 2 neurons for the input layer // => 2 neurons for the input layer
// => 2 neurons for the hidden layer // => 2 neurons for the hidden layer
// => 1 neuron for the output layer // => 1 neuron for the output layer
// => a learning rate == 0.005 (just get it doing some tests until satisfied) // => a learning rate == 0.002 (just get it doing some tests until satisfied, but remember to keep its value quite low and ~ 0 to keep the network stable)
// => 1000 learning steps (i.e. the network will be ready after 1000 training steps to adjust the synaptical weights // => 1000 learning steps (i.e. the network will be ready after 1000 training steps to adjust the synaptical weights
// => 0.1 as neural threshold (the threshold above which a neuron activates) // => 0.1 as neural threshold (the threshold above which a neuron activates)
NeuralNet net(2, 2, 1, 0.005, 1000, 0.1); NeuralNet net(2, 2, 1, 0.002, 2000);
// Initialize a training XML as a string in 'xml' // Initialize a training XML as a string in 'xml'
NeuralNet::initXML(xml); NeuralNet::initXML(xml);
@ -45,6 +45,10 @@ int main() {
xml += NeuralNet::XMLFromSet(id, "-1,-2;-3"); xml += NeuralNet::XMLFromSet(id, "-1,-2;-3");
xml += NeuralNet::XMLFromSet(id, "8,9;17"); xml += NeuralNet::XMLFromSet(id, "8,9;17");
xml += NeuralNet::XMLFromSet(id, "10,10;20"); xml += NeuralNet::XMLFromSet(id, "10,10;20");
xml += NeuralNet::XMLFromSet(id, "4,1;5");
xml += NeuralNet::XMLFromSet(id, "2,6;8");
xml += NeuralNet::XMLFromSet(id, "2,7;9");
xml += NeuralNet::XMLFromSet(id, "8,9;17");
NeuralNet::closeXML(xml); NeuralNet::closeXML(xml);
// Save the XML string just created to a file // Save the XML string just created to a file
@ -61,7 +65,7 @@ int main() {
// Save the trained network to a binary file, that can be reloaded from any // Save the trained network to a binary file, that can be reloaded from any
// application that is going to use that network // application that is going to use that network
net.save("adder.net"); net.save("network.xml");
cout << "Network trained in " << (t2-t1) << " seconds. You can use adder.net file now to load this network\n"; cout << "Network trained in " << (t2-t1) << " seconds. You can use adder.net file now to load this network\n";
return 0; return 0;
} }

View file

@ -20,11 +20,8 @@
#include "neural++_exception.hpp" #include "neural++_exception.hpp"
//! Default rand value: |sin(rand)|, always >= 0 and <= 1 #define RAND (double) ( (rand() / 10.0) / ((double) RAND_MAX) )
#define RAND (double) ( (rand() / (RAND_MAX/2)) - 1) #define BETA0 0.8
//! Initial value for the inertial momentum of the synapses
#define BETA0 1.0
/** /**
* @namespace neuralpp * @namespace neuralpp
@ -74,13 +71,6 @@ namespace neuralpp {
*/ */
void updateWeights(); void updateWeights();
/**
* @brief It commits the changes made by updateWeights() to the layer l.
* In-class use only
* @param l Layer to commit the changes
*/
void commitChanges (Layer& l);
/** /**
* @brief Get the error made on the expected result as squared deviance * @brief Get the error made on the expected result as squared deviance
* @param ex Expected value * @param ex Expected value
@ -94,6 +84,52 @@ namespace neuralpp {
*/ */
double (*actv_f)(double); double (*actv_f)(double);
/**
* @brief Get the expected value (in case you have an only neuron in output layer). Of course you should specify this when you
* build your network by using setExpected.
* @return The expected output value for a certain training phase
*/
double expected() const;
/**
* @brief Get the expected value (in case you have an only neuron in output layer). Of course you should specify this when you
* build your network by using setExpected.
* @return The expected output value for a certain training phase
*/
std::vector<double> getExpected() const;
/**
* @brief It sets the value you expect from your network (in case the network has an only neuron in its output layer)
* @param ex Expected output value
*/
void setExpected(double ex);
/**
* @brief Set the values you expect from your network
* @param ex Expected output values
*/
void setExpected(std::vector<double> ex);
/**
* @brief It updates through back-propagation the weights of the synapsis and
* computes again the output value for <i>epochs</i> times, calling back
* updateWeights and commitChanges functions
*/
void update();
/**
* @brief It links the layers of the network (input, hidden, output)
*/
void link();
/**
* @brief Splits a string into a vector of doubles, given a delimitator
* @param delim Delimitator
* @param str String to be splitted
* @return Vector of doubles containing splitted values
*/
static std::vector<double> split (char delim, std::string str);
public: public:
Layer* input; Layer* input;
Layer* hidden; Layer* hidden;
@ -139,12 +175,6 @@ namespace neuralpp {
*/ */
double getOutput() const; double getOutput() const;
/**
* @brief Get the threshold of the neurons in the network
* @return The threshold of the neurons
*/
double getThreshold() const;
/** /**
* @brief It gets the output of the network in case the output layer contains more neurons * @brief It gets the output of the network in case the output layer contains more neurons
* @return A vector containing the output values of the network * @return A vector containing the output values of the network
@ -152,37 +182,10 @@ namespace neuralpp {
std::vector<double> getOutputs(); std::vector<double> getOutputs();
/** /**
* @brief Get the expected value (in case you have an only neuron in output layer). Of course you should specify this when you * @brief Get the threshold of the neurons in the network
* build your network by using setExpected. * @return The threshold of the neurons
* @return The expected output value for a certain training phase
*/ */
double expected() const; double getThreshold() const;
/**
* @brief Get the expected value (in case you have an only neuron in output layer). Of course you should specify this when you
* build your network by using setExpected.
* @return The expected output value for a certain training phase
*/
std::vector<double> getExpected() const;
/**
* @brief It sets the value you expect from your network (in case the network has an only neuron in its output layer)
* @param ex Expected output value
*/
void setExpected(double ex);
/**
* @brief Set the values you expect from your network
* @param ex Expected output values
*/
void setExpected(std::vector<double> ex);
/**
* @brief It updates through back-propagation the weights of the synapsis and
* computes again the output value for <i>epochs</i> times, calling back
* updateWeights and commitChanges functions
*/
void update();
/** /**
* @brief It propagates values through the network. Use this when you want to give * @brief It propagates values through the network. Use this when you want to give
@ -196,12 +199,6 @@ namespace neuralpp {
*/ */
void setInput (std::vector<double> v); void setInput (std::vector<double> v);
/**
* @brief It links the layers of the network (input, hidden, output). Don't use unless
* you exactly know what you're doing, it is already called by the constructor
*/
void link();
/** /**
* @brief Save a trained neural network to a binary file * @brief Save a trained neural network to a binary file
* @param fname Binary file where you're going to save your network * @param fname Binary file where you're going to save your network
@ -210,6 +207,30 @@ namespace neuralpp {
*/ */
void save (const char* fname) throw(NetworkFileWriteException); void save (const char* fname) throw(NetworkFileWriteException);
/**
* @brief DEPRECATED. Load a trained neural network from a binary file.
* This function is deprecated and kept for back-compatibility. Use
* the XML format instead to load and neural networks and, respectly,
* the NeuralNetwork(const std::string) constructor or the save(const char*)
* methods.
* @param fname Name of the file to be loaded
* @throws NetworkFileNotFoundException When you're trying to load
* an invalid network file
*/
void loadFromBinary (const std::string fname) throw(NetworkFileNotFoundException);
/**
* @brief DEPRECATED. Save a trained neural network to a binary file.
* This function is deprecated and kept for back-compatibility. Use
* the XML format instead to load and neural networks and, respectly,
* the NeuralNetwork(const std::string) constructor or the save(const char*)
* methods.
* @param fname Name of the file to be saved with the network information
* @throws NetworkFileWriteException When you try to write the network
* information to an invalid file
*/
void saveToBinary (const char* fname) throw(NetworkFileWriteException);
/** /**
* @brief Train a network using a training set loaded from an XML file. A sample XML file * @brief Train a network using a training set loaded from an XML file. A sample XML file
* is available in examples/adder.xml * is available in examples/adder.xml
@ -225,14 +246,6 @@ namespace neuralpp {
*/ */
static void initXML (std::string& xml); static void initXML (std::string& xml);
/**
* @brief Splits a string into a vector of doubles, given a delimitator
* @param delim Delimitator
* @param str String to be splitted
* @return Vector of doubles containing splitted values
*/
static std::vector<double> split (char delim, std::string str);
/** /**
* @brief Get a training set from a string and copies it to an XML * @brief Get a training set from a string and copies it to an XML
* For example, these strings could be training sets for making sums: * For example, these strings could be training sets for making sums:
@ -271,13 +284,9 @@ namespace neuralpp {
public: public:
/** /**
* @brief Constructor * @brief Empty constructor (it does nothing)
* @param i Input neuron
* @param o Output neuron
* @param w Weight for the synapsis
* @param d Delta for the synapsis
*/ */
Synapsis(Neuron* i, Neuron* o, double w, double d); Synapsis() {}
/** /**
* @brief Constructor * @brief Constructor
@ -424,6 +433,9 @@ namespace neuralpp {
*/ */
void setProp (double p); void setProp (double p);
void setSynIn (size_t n);
void setSynOut (size_t n);
/** /**
* @brief Get the activation value of the neuron * @brief Get the activation value of the neuron
* @return Activation value for the neuron * @return Activation value for the neuron

View file

@ -19,7 +19,7 @@ using std::vector;
namespace neuralpp { namespace neuralpp {
Layer::Layer(size_t sz, double (*a) (double), double th) { Layer::Layer(size_t sz, double (*a) (double), double th) {
for (size_t i = 0; i < sz; i++) { for (size_t i = 0; i < sz; i++) {
Neuron n(a); Neuron n(a,th);
elements.push_back(n); elements.push_back(n);
} }
@ -47,15 +47,17 @@ namespace neuralpp {
void Layer::link(Layer& l) { void Layer::link(Layer& l) {
srand((unsigned) time(NULL)); srand((unsigned) time(NULL));
for (size_t i = 0; i < l.size(); i++)
l.elements[i].setSynOut(size());
for (size_t i = 0; i < size(); i++)
elements[i].setSynIn(l.size());
for (size_t i = 0; i < l.size(); i++) { for (size_t i = 0; i < l.size(); i++) {
Neuron *n1 = &(l.elements[i]);
for (size_t j = 0; j < size(); j++) { for (size_t j = 0; j < size(); j++) {
Neuron *n2 = &(elements[j]); Synapsis *s = new Synapsis( &(l.elements[i]), &(elements[i]), RAND, actv_f );
Synapsis s(n1, n2, RAND, actv_f); l.elements[i].synOut(j) = *s;
elements[j].synIn(i) = *s;
n1->push_out(s);
n2->push_in(s);
} }
} }
} }

View file

@ -42,27 +42,11 @@ namespace neuralpp {
actv_f = a; actv_f = a;
threshold = th; threshold = th;
input = new Layer(in_size, __actv, th);
hidden = new Layer(hidden_size, __actv, th);
output = new Layer(out_size, __actv, th);
link();
}
/*NeuralNet::NeuralNet(size_t in_size, size_t hidden_size,
size_t out_size, double (*a) (double),
double l, int e, double th) {
epochs = e;
ref_epochs = epochs;
l_rate = l;
actv_f = a;
threshold = th;
input = new Layer(in_size, a, th); input = new Layer(in_size, a, th);
hidden = new Layer(hidden_size, a, th); hidden = new Layer(hidden_size, a, th);
output = new Layer(out_size, a, th); output = new Layer(out_size, a, th);
link(); link();
}*/ }
double NeuralNet::getOutput() const { double NeuralNet::getOutput() const {
return (*output)[0].getActv(); return (*output)[0].getActv();
@ -143,7 +127,9 @@ namespace neuralpp {
(-l_rate) * (z-d) * f * y; (-l_rate) * (z-d) * f * y;
Dk += ( (z-d) * f * s->getWeight() ); Dk += ( (z-d) * f * s->getWeight() );
s->setDelta(out_delta); s->setDelta(out_delta);
(*hidden)[j].synOut(i).setDelta(out_delta);
} }
} }
@ -166,13 +152,12 @@ namespace neuralpp {
(-l_rate) * d * x; (-l_rate) * d * x;
s->setDelta(hidden_delta); s->setDelta(hidden_delta);
} (*input)[j].synOut(i).setDelta(hidden_delta);
} }
} }
void NeuralNet::commitChanges(Layer& l) { for (size_t i = 0; i < output->size(); i++) {
for (size_t i = 0; i < l.size(); i++) { Neuron *n = &((*output)[i]);
Neuron *n = &(l[i]);
for (size_t j = 0; j < n->nIn(); j++) { for (size_t j = 0; j < n->nIn(); j++) {
Synapsis *s = &(n->synIn(j)); Synapsis *s = &(n->synIn(j));
@ -181,20 +166,204 @@ namespace neuralpp {
s->setDelta(0.0); s->setDelta(0.0);
} }
} }
for (size_t i = 0; i < hidden->size(); i++) {
Neuron *n = &((*hidden)[i]);
for (size_t j = 0; j < n->nOut(); j++) {
Synapsis *s = &(n->synOut(j));
s->setWeight(s->getWeight() +
s->getDelta());
s->setDelta(0.0);
}
}
for (size_t i = 0; i < hidden->size(); i++) {
Neuron *n = &((*hidden)[i]);
for (size_t j = 0; j < n->nIn(); j++) {
Synapsis *s = &(n->synIn(j));
s->setWeight(s->getWeight() +
s->getDelta());
s->setDelta(0.0);
}
}
for (size_t i = 0; i < input->size(); i++) {
Neuron *n = &((*input)[i]);
for (size_t j = 0; j < n->nOut(); j++) {
Synapsis *s = &(n->synOut(j));
s->setWeight(s->getWeight() +
s->getDelta());
s->setDelta(0.0);
}
}
} }
void NeuralNet::update() { void NeuralNet::update() {
epochs = ref_epochs; epochs = ref_epochs;
while ((epochs--) > 0) { while ((epochs--) > 0) {
updateWeights();
commitChanges(*output);
commitChanges(*hidden);
propagate(); propagate();
updateWeights();
} }
} }
void NeuralNet::save (const char *fname) throw(NetworkFileWriteException) { void NeuralNet::save (const char *fname) throw(NetworkFileWriteException) {
ofstream out(fname);
stringstream xml(stringstream::in | stringstream::out);
if (!out)
throw NetworkFileWriteException();
xml << "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
<< "<!DOCTYPE NETWORK SYSTEM \"http://blacklight.gotdns.org/prog/neuralpp/network.dtd\">\n"
<< "<!-- Automatically generated by BlackLight's Neural++ module -->\n\n"
<< "<network name=\"Put here the name for this neural network\" epochs=\"" << ref_epochs << "\" "
<< "learning_rate=\"" << l_rate << "\" threshold=\"" << threshold << "\">\n"
<< "\t<layer class=\"input\" size=\"" << input->size() << "\"></layer>\n"
<< "\t<layer class=\"hidden\" size=\"" << hidden->size() << "\"></layer>\n"
<< "\t<layer class=\"output\" size=\"" << output->size() << "\"></layer>\n\n";
for (unsigned int i = 0; i < hidden->size(); i++) {
int nin = (*hidden)[i].nIn();
for (int j = 0; j < nin; j++)
xml << "\t<synapsis class=\"inhid\" input=\"" << j << "\" output=\"" << i << "\" "
<< "weight=\"" << (*hidden)[i].synIn(j).getWeight() << "\"></synapsis>\n";
}
for (unsigned int i = 0; i < output->size(); i++) {
int nin = (*output)[i].nIn();
for (int j = 0; j < nin; j++)
xml << "\t<synapsis class=\"hidout\" input=\"" << j << "\" output=\"" << i << "\" "
<< "weight=\"" << (*output)[i].synIn(j).getWeight() << "\"></synapsis>\n";
}
xml << "</network>\n";
out << xml.str();
}
NeuralNet::NeuralNet(const string fname) throw(NetworkFileNotFoundException) {
unsigned int in_size = 0, hid_size = 0, out_size = 0;
vector< vector<double> > in_hid_synapses, hid_out_synapses;
CMarkup xml;
xml.Load(fname.c_str());
if (!xml.IsWellFormed()) {
throw InvalidXMLException();
return;
}
if (xml.FindElem("network")) {
if (xml.GetAttrib("epochs").empty())
throw InvalidXMLException();
if (xml.GetAttrib("learning_rate").empty())
throw InvalidXMLException();
epochs = atoi(xml.GetAttrib("epochs").c_str());
l_rate = atof(xml.GetAttrib("learning_rate").c_str());
threshold = 0.0;
if (!xml.GetAttrib("threshold").empty())
threshold = atof(xml.GetAttrib("threshold").c_str());
while (xml.FindChildElem("layer")) {
if (xml.GetChildAttrib("class").empty())
throw InvalidXMLException();
if (xml.GetChildAttrib("size").empty())
throw InvalidXMLException();
if (!xml.GetChildAttrib("class").compare("input"))
in_size = atoi(xml.GetChildAttrib("size").c_str());
else if (!xml.GetChildAttrib("class").compare("hidden"))
hid_size = atoi(xml.GetChildAttrib("size").c_str());
else if (!xml.GetChildAttrib("class").compare("output"))
out_size = atoi(xml.GetChildAttrib("size").c_str());
else
throw InvalidXMLException();
}
if (in_size && hid_size && out_size) {
in_hid_synapses = vector< vector<double> >(in_size);
for (unsigned int i=0; i < in_size; i++)
in_hid_synapses[i] = vector<double>(hid_size);
hid_out_synapses = vector< vector<double> >(hid_size);
for (unsigned int i=0; i < hid_size; i++)
hid_out_synapses[i] = vector<double>(out_size);
}
while (xml.FindChildElem("synapsis")) {
if (!(in_size && hid_size && out_size))
throw InvalidXMLException();
if (xml.GetChildAttrib("class").empty())
throw InvalidXMLException();
if (xml.GetChildAttrib("input").empty())
throw InvalidXMLException();
if (xml.GetChildAttrib("output").empty())
throw InvalidXMLException();
if (xml.GetChildAttrib("weight").empty())
throw InvalidXMLException();
unsigned int in = atoi(xml.GetChildAttrib("input").c_str());
unsigned int out = atoi(xml.GetChildAttrib("output").c_str());
if (xml.GetChildAttrib("class") == "inhid") {
if (in >= in_size || out >= hid_size)
throw InvalidXMLException();
in_hid_synapses[in][out] = atof(xml.GetChildAttrib("weight").c_str());
}
if (xml.GetChildAttrib("class") == "hidout") {
if (in >= hid_size || out >= out_size)
throw InvalidXMLException();
hid_out_synapses[in][out] = atof(xml.GetChildAttrib("weight").c_str());
}
}
}
*this = NeuralNet(in_size, hid_size, out_size, l_rate, epochs, threshold);
hidden->link(*input);
output->link(*hidden);
// Restore synapses
for (unsigned int i = 0; i < input->size(); i++) {
for (unsigned int j = 0; j < hidden->size(); j++)
(*input)[i].synOut(j).setWeight( in_hid_synapses[i][j] );
}
for (unsigned int i = 0; i < output->size(); i++) {
for (unsigned int j = 0; j < hidden->size(); j++)
(*output)[i].synIn(j).setWeight( (hid_out_synapses[j][i]) );
}
for (unsigned int i = 0; i < hidden->size(); i++) {
for (unsigned int j = 0; j < input->size(); j++)
(*hidden)[i].synIn(j).setWeight( (in_hid_synapses[j][i]) );
}
for (unsigned int i = 0; i < hidden->size(); i++) {
for (unsigned int j = 0; j < output->size(); j++)
(*hidden)[i].synOut(j).setWeight( hid_out_synapses[i][j] );
}
}
void NeuralNet::saveToBinary (const char *fname) throw(NetworkFileWriteException) {
struct netrecord record; struct netrecord record;
ofstream out(fname); ofstream out(fname);
@ -308,7 +477,7 @@ namespace neuralpp {
out.close(); out.close();
} }
NeuralNet::NeuralNet(const string fname) throw(NetworkFileNotFoundException) { void NeuralNet::loadFromBinary (const string fname) throw(NetworkFileNotFoundException) {
struct netrecord record; struct netrecord record;
ifstream in(fname.c_str()); ifstream in(fname.c_str());
@ -479,7 +648,6 @@ namespace neuralpp {
setInput(input); setInput(input);
setExpected(output); setExpected(output);
propagate();
update(); update();
} }
} }

View file

@ -24,8 +24,8 @@ namespace neuralpp {
Neuron::Neuron(vector < Synapsis > i, vector < Synapsis > o, Neuron::Neuron(vector < Synapsis > i, vector < Synapsis > o,
double (*a) (double), double th) { double (*a) (double), double th) {
in = i; in.assign(i.begin(), i.end());
out = o; out.assign(o.begin(), o.end());
actv_f = a; actv_f = a;
threshold = th; threshold = th;
} }
@ -51,10 +51,17 @@ namespace neuralpp {
} }
void Neuron::setActv(double val) { void Neuron::setActv(double val) {
//actv_val = actv_f(val);
actv_val = val; actv_val = val;
} }
void Neuron::setSynIn (size_t n) {
in = vector<Synapsis>(n);
}
void Neuron::setSynOut (size_t n) {
out = vector<Synapsis>(n);
}
size_t Neuron::nIn() { size_t Neuron::nIn() {
return in.size(); return in.size();
} }

View file

@ -15,14 +15,6 @@
#include "neural++.hpp" #include "neural++.hpp"
namespace neuralpp { namespace neuralpp {
Synapsis::Synapsis(Neuron * i, Neuron * o, double w, double d) {
in = i;
out = o;
weight = w;
delta = d;
prev_delta = 0;
}
Synapsis::Synapsis(Neuron * i, Neuron * o, double (*a) (double)) { Synapsis::Synapsis(Neuron * i, Neuron * o, double (*a) (double)) {
srand((unsigned) time(NULL)); srand((unsigned) time(NULL));
@ -75,9 +67,6 @@ namespace neuralpp {
} }
void Synapsis::setDelta(double d) throw(InvalidSynapticalWeightException) { void Synapsis::setDelta(double d) throw(InvalidSynapticalWeightException) {
if (d > 1.0)
throw InvalidSynapticalWeightException();
prev_delta = delta; prev_delta = delta;
delta = d; delta = d;
} }