diff --git a/src/mlpack/methods/ann/ffn.hpp b/src/mlpack/methods/ann/ffn.hpp index 75ad31255a..ef05a67076 100644 --- a/src/mlpack/methods/ann/ffn.hpp +++ b/src/mlpack/methods/ann/ffn.hpp @@ -365,6 +365,7 @@ network.Network(); } const size_t begin, const size_t end); + // TODO: this API needs to be changed! /** * Perform the backward pass of the data in real batch mode. * diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index c1f42377f5..ab0c00afbd 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -268,8 +268,15 @@ void FFN< // Ensure the network is valid. CheckNetwork("FFN::Forward()", inputs.n_rows); - results.set_size(network.OutputSize(), inputs.n_cols); - network.Forward(inputs, results, begin, end); + // We must always store a copy of the forward pass in `networkOutputs` in case + // we do a backward pass. + networkOutput.set_size(network.OutputSize(), inputs.n_cols); + network.Forward(inputs, networkOutput, begin, end); + + // It's possible the user passed `networkOutputs` as `results`; in this case, + // we don't need to create an alias. + if (&results != &networkOutput) + results = networkOutput; } template /** * Create the BaseLayer object. */ - BaseLayer() + BaseLayer() : Layer() { // Nothing to do here. } diff --git a/src/mlpack/methods/ann/layer/multi_layer.hpp b/src/mlpack/methods/ann/layer/multi_layer.hpp index b13085c12c..dd0ec85266 100644 --- a/src/mlpack/methods/ann/layer/multi_layer.hpp +++ b/src/mlpack/methods/ann/layer/multi_layer.hpp @@ -26,6 +26,12 @@ template class MultiLayer : public Layer { public: + // TODO: implement these types of things... + MultiLayer(); + MultiLayer(const MultiLayer& other); + MultiLayer(MultiLayer&& other); + MultiLayer& operator=(const MultiLayer& other); + MultiLayer& operator=(MultiLayer&& other); virtual ~MultiLayer() { @@ -33,8 +39,6 @@ class MultiLayer : public Layer delete network[i]; } - // TODO: implement these types of things... -// MultiLayer(const MultiLayer& other); virtual MultiLayer* Clone() const { return new MultiLayer(*this); } @@ -223,6 +227,7 @@ network; } ar(cereal::base_class>(this)); ar(CEREAL_VECTOR_POINTER(network)); + ar(CEREAL_NVP(inSize)); ar(CEREAL_NVP(totalInputSize)); ar(CEREAL_NVP(totalOutputSize)); @@ -230,6 +235,7 @@ network; } { layerOutputMatrix.clear(); layerDeltaMatrix.clear(); + layerGradients.clear(); layerOutputs.resize(network.size(), OutputType()); layerDeltas.resize(network.size(), OutputType()); layerGradients.resize(network.size(), OutputType()); @@ -299,8 +305,8 @@ network; } for (size_t i = 0; i < network.size(); ++i) { const size_t weightSize = network[i]->WeightSize(); - MakeAlias(layerGradients[i], gradient.colptr(gradientStart), weightSize, - 1); + MakeAlias(layerGradients[i], gradient.memptr() + gradientStart, + weightSize, 1); gradientStart += weightSize; } } diff --git a/src/mlpack/methods/ann/layer/multi_layer_impl.hpp b/src/mlpack/methods/ann/layer/multi_layer_impl.hpp index fb608c4d47..a812012ca0 100644 --- a/src/mlpack/methods/ann/layer/multi_layer_impl.hpp +++ b/src/mlpack/methods/ann/layer/multi_layer_impl.hpp @@ -18,11 +18,128 @@ namespace mlpack { namespace ann { +template +MultiLayer::MultiLayer() : + inSize(0), + totalInputSize(0), + totalOutputSize(0) +{ + // Nothing to do. +} + +template +MultiLayer::MultiLayer(const MultiLayer& other) : + Layer(other), + inSize(other.inSize), + totalInputSize(other.totalInputSize), + totalOutputSize(other.totalOutputSize), + layerOutputMatrix(other.layerOutputMatrix), + layerDeltaMatrix(other.layerDeltaMatrix) +{ + // Copy each layer. + for (size_t i = 0; i < other.network.size(); ++i) + network.push_back(other.network[i]->Clone()); + + // Ensure that the aliases for layers during passes have the right size. + layerOutputs.resize(network.size(), OutputType()); + layerDeltas.resize(network.size(), OutputType()); + layerGradients.resize(network.size(), OutputType()); + + // layerOutputs, layerDeltas, and layerGradients will be reset the next time + // Forward(), Backward(), or Gradient() is called. +} + +template +MultiLayer::MultiLayer(MultiLayer&& other) : + Layer(other), + network(std::move(other.network)), + inSize(std::move(other.inSize)), + totalInputSize(std::move(other.totalInputSize)), + totalOutputSize(std::move(other.totalOutputSize)), + layerOutputMatrix(std::move(other.layerOutputMatrix)), + layerDeltaMatrix(std::move(other.layerDeltaMatrix)) +{ + // Ensure that the aliases for layers during passes have the right size. + layerOutputs.resize(network.size(), OutputType()); + layerDeltas.resize(network.size(), OutputType()); + layerGradients.resize(network.size(), OutputType()); + + // layerOutputs, layerDeltas, and layerGradients will be reset the next time + // Forward(), Backward(), or Gradient() is called. + + other.layerOutputs.clear(); + other.layerDeltas.clear(); + other.layerGradients.clear(); +} + +template +MultiLayer& +MultiLayer::operator=(const MultiLayer& other) +{ + if (this != &other) + { + Layer::operator=(other); + + network.clear(); + layerOutputs.clear(); + layerDeltas.clear(); + layerGradients.clear(); + + inSize = other.inSize; + totalInputSize = other.totalInputSize; + totalOutputSize = other.totalOutputSize; + + layerOutputMatrix = other.layerOutputMatrix; + layerDeltaMatrix = other.layerDeltaMatrix; + + for (size_t i = 0; i < other.network.size(); ++i) + network.push_back(other.network[i]->Clone()); + + // Ensure that the aliases for layers during passes have the right size. + layerOutputs.resize(network.size(), OutputType()); + layerDeltas.resize(network.size(), OutputType()); + layerGradients.resize(network.size(), OutputType()); + } + + return *this; +} + +template +MultiLayer& +MultiLayer::operator=(MultiLayer&& other) +{ + if (this != &other) + { + Layer::operator=(other); + + layerOutputs.clear(); + layerDeltas.clear(); + layerGradients.clear(); + + inSize = std::move(other.inSize); + totalInputSize = std::move(other.totalInputSize); + totalOutputSize = std::move(other.totalOutputSize); + + // TODO: network ownerships?? + network = std::move(other.network); + + layerOutputs.resize(network.size(), OutputType()); + layerDeltas.resize(network.size(), OutputType()); + layerGradients.resize(network.size(), OutputType()); + + other.layerOutputs.clear(); + other.layerDeltas.clear(); + other.layerGradients.clear(); + } + + return *this; +} + template void MultiLayer::Forward( const InputType& input, OutputType& output) { - Forward(input, output, 0, network.size()); + Forward(input, output, 0, network.size() - 1); } template @@ -38,17 +155,17 @@ void MultiLayer::Forward( // Note that we use `output` for the last layer; layerOutputs is only used for // intermediate values between layers. - if ((end - start) > 1) + if ((end - start) > 0) { // Initialize memory for the forward pass (if needed). InitializeForwardPassMemory(input.n_cols); - network[start]->Forward(input, layerOutputs.front()); - for (size_t i = start; i < end - 1; ++i) + network[start]->Forward(input, layerOutputs[start]); + for (size_t i = start + 1; i < end; ++i) network[i]->Forward(layerOutputs[i - 1], layerOutputs[i]); - network[end]->Forward(layerOutputs.back(), output); + network[end]->Forward(layerOutputs[end - 1], output); } - else if ((end - start) == 1) + else if ((end - start) == 0 && network.size() > 0) { network[start]->Forward(input, output); } @@ -71,7 +188,7 @@ void MultiLayer::Backward( network.back()->Backward(input, gy, layerDeltas.back()); for (size_t i = network.size() - 2; i > 0; --i) network[i]->Backward(layerOutputs[i], layerDeltas[i + 1], layerDeltas[i]); - + network[0]->Backward(layerOutputs[0], layerDeltas[1], g); } else if (network.size() == 1) { @@ -99,7 +216,7 @@ void MultiLayer::Gradient( network.front()->Gradient(input, layerDeltas[1], layerGradients.front()); for (size_t i = 1; i < network.size() - 1; ++i) { - network[i]->Gradient(layerOutputs[i], layerDeltas[i + 1], + network[i]->Gradient(layerOutputs[i - 1], layerDeltas[i + 1], layerGradients[i]); } network.back()->Gradient(layerOutputs[network.size() - 2], error,