/** * @file: Mlp.cpp * * Implementation of the bare-bones MLP computations. See header file * for documentation. */ #include "Mlp.hpp" #include #include #include #include using std::ostream; using std::ostringstream; using std::runtime_error; using std::endl; using namespace jymlp; /** Local helper function: Compute total number of weights. * TODO: Move to some utils.cpp if need arises. */ static size_t total_nweights(const vector& nneur){ if (nneur.size() == 0) return 0; size_t tot = 0; for (size_t i=1; i& inneur){ nneur = inneur; weights.resize(total_nweights(nneur)); actf.resize(nneur.size()); for (double & w : weights){ w = 0.0; } actf[0] = actf::Unset; for (size_t i=1; i& inneur, const vector& iactf, const vector& iweights){ nneur = inneur; actf = iactf; weights = iweights; } Mlp::Mlp(const Mlp &other){ nneur = other.nneur; actf = other.actf; weights = other.weights; } Mlp::Mlp(const vector& inneur, const vector& iactf, SynapticRandomizer& sr){ nneur = inneur; actf = iactf; weights.resize(total_nweights(nneur)); initRnd(1.0,sr); } // -------------------------- Protected: void Mlp::initRnd(double a, SynapticRandomizer & sr){ for(double & w : weights){ w = sr.rndU(a); } } // FIXME: Rethink the computations - need less space after all? size_t Mlp::getWorkspaceSize(){ size_t maxn = 0; for (size_t i=0;i maxn) maxn = nneur[i]; } return maxn*getNLayers(); } // -------------------------- Public: size_t Mlp::getNHiddenNeurons() const{ size_t res = 0; for (size_t i=1;i0.0?1:0" in here. ... instead of // this obscure passage (which can "short circuit", though): if (weights[ineur*(nneur[0]+1)+1+iin] != 0.0){ isConnected = true; break; // No need to look further. } // .. and, of course, this is one of the representation // issues in evolutionary MLPs. We could have a separate // bit string for active inputs, but we decided to follow // the "straightforward" storage format here, for // resemblance to pre-existing MLP codes. } if (isConnected) res++; } return res; } string Mlp::prettyPrintLayerSizes(){ ostringstream oss(""); size_t nlay = getNLayers(); for (size_t i=0;i & nneur, const vector weights){ ostringstream oss(""); // TODO: Can only do plaintext as of now. LaTeX could be nice? size_t wcount = 0; size_t nlay = nneur.size(); for (size_t lay = 1; lay < nlay ; ++lay){ for (size_t ineur = 0; ineur < (size_t) nneur[lay]; ++ineur){ for (size_t iw = 0; iw < (size_t) nneur[lay-1] + 1; ++iw){ oss << weights[wcount++]; if (iw<(size_t)nneur[lay-1]) { oss << " "; } else {oss << endl;} } } oss << endl; } return oss.str(); } string Mlp::prettyPrintWeights() const{ return doPrettyPrint(nneur,weights); } string Mlp::prettyPrintGradient(const vector & grad) const{ return doPrettyPrint(nneur,grad); } string Mlp::prettyPrint(){ ostringstream oss; oss << prettyPrintLayerSizes(); oss << " (" << prettyPrintLayerActivations() << ")" << endl; oss << prettyPrintWeights() << endl; return oss.str(); } void Mlp::toStream(ostream & o){ size_t nlay = getNLayers(); o << "1 "; // 'version ID' o << nlay; for (size_t i=0;i> version_id; // Expect 0 so far.. switch (version_id){ case 1: ins >> s; nneur.resize(s); for (size_t i = 0;i> nneur[i]; } actf.resize(nneur.size()); for (size_t i = 0;i> en; actf[i] = static_cast(en); } weights.resize(total_nweights(nneur)); for (size_t i = 0;i> weights[i]; } break; default: throw runtime_error("Unknown version_id"); } } // MLP evaluations /* Activation function on forward pass */ static double act (ActF af, double s){ switch(af){ case actf::linear: return s; case actf::logsig: return 1.0/(1.0 + exp(-s)); case actf::hyptan: return 2.0/(1.0 + exp(-2.0 * s)) -1.0; default: throw runtime_error("Unknown activation function"); } } /* Activation function derivative on backward pass; fp must be the * previously computed value */ static double actD (ActF af, double fp){ switch(af){ case actf::linear: return 1.0; case actf::logsig: return fp * (1.0 - fp); case actf::hyptan: return (1.0 - fp * fp); default: throw runtime_error("Unknown activation function"); } } void Mlp::forward(const vector &input, double * workspace) const{ /* Make a copy of the input data. Costly, but simplifies backward loop: */ for (size_t i=0;i