diff --git a/src/mlpack/methods/ann/ffn_impl.hpp b/src/mlpack/methods/ann/ffn_impl.hpp index e11b624cc8..703e67d459 100644 --- a/src/mlpack/methods/ann/ffn_impl.hpp +++ b/src/mlpack/methods/ann/ffn_impl.hpp @@ -641,6 +641,7 @@ FFN::FFN( { this->network.push_back(boost::apply_visitor(copyVisitor, network.network[i])); + boost::apply_visitor(resetVisitor, this->network.back()); } }; diff --git a/src/mlpack/methods/ann/layer/linear.hpp b/src/mlpack/methods/ann/layer/linear.hpp index b34a462d99..1930181654 100644 --- a/src/mlpack/methods/ann/layer/linear.hpp +++ b/src/mlpack/methods/ann/layer/linear.hpp @@ -52,6 +52,18 @@ class Linear const size_t outSize, RegularizerType regularizer = RegularizerType()); + //! Copy constructor. + Linear(const Linear& layer); + + //! Move constructor. + Linear(Linear&&); + + //! Copy assignment operator. + Linear& operator=(const Linear& layer); + + //! Move assignment operator. + Linear& operator=(Linear&& layer); + /* * Reset the layer parameter. */ diff --git a/src/mlpack/methods/ann/layer/linear_impl.hpp b/src/mlpack/methods/ann/layer/linear_impl.hpp index 22caf180f9..79799fea98 100644 --- a/src/mlpack/methods/ann/layer/linear_impl.hpp +++ b/src/mlpack/methods/ann/layer/linear_impl.hpp @@ -41,6 +41,62 @@ Linear::Linear( weights.set_size(outSize * inSize + outSize, 1); } +template +Linear::Linear( + const Linear& layer) : + inSize(layer.inSize), + outSize(layer.outSize), + weights(layer.weights), + regularizer(layer.regularizer) +{ + // Nothing to do here. +} + +template +Linear::Linear( + Linear&& layer) : + inSize(0), + outSize(0), + weights(std::move(layer.weights)), + regularizer(std::move(layer.regularizer)) +{ + // Nothing to do here. +} + +template +Linear& +Linear:: +operator=(const Linear& layer) +{ + if (this != &layer) + { + inSize = layer.inSize; + outSize = layer.outSize; + weights = layer.weights; + regularizer = layer.regularizer; + } + return *this; +} + +template +Linear& +Linear:: +operator=(Linear&& layer) +{ + if (this != &layer) + { + inSize = layer.inSize; + outSize = layer.outSize; + weights = std::move(layer.weights); + regularizer = std::move(layer.regularizer); + } + return *this; +} + template void Linear::Reset() @@ -92,11 +148,7 @@ void Linear::serialize( { ar & BOOST_SERIALIZATION_NVP(inSize); ar & BOOST_SERIALIZATION_NVP(outSize); - - // This is inefficient, but we have to allocate this memory so that - // WeightSetVisitor gets the right size. - if (Archive::is_loading::value) - weights.set_size(outSize * inSize + outSize, 1); + ar & BOOST_SERIALIZATION_NVP(weights); } } // namespace ann diff --git a/src/mlpack/tests/feedforward_network_test.cpp b/src/mlpack/tests/feedforward_network_test.cpp index f9a338e0b3..ba45afd29d 100644 --- a/src/mlpack/tests/feedforward_network_test.cpp +++ b/src/mlpack/tests/feedforward_network_test.cpp @@ -57,6 +57,104 @@ void TestNetwork(ModelType& model, REQUIRE(classificationError <= classificationErrorThreshold); } +// network1 should be allocated with `new`, and trained on some data. +template +void CheckCopyFunction(ModelType* network1, + MatType& trainData, + MatType& trainLabels, + const size_t maxEpochs) +{ + ens::RMSProp opt(0.01, 32, 0.88, 1e-8, maxEpochs * trainData.n_cols, -1); + network1->Train(trainData, trainLabels, opt); + + arma::mat predictions1; + network1->Predict(trainData, predictions1); + FFN<> network2; + network2 = *network1; + delete network1; + + // Deallocating all of network1's memory, so that + // if network2 is trying to use any of that memory. + arma::mat predictions2; + network2.Predict(trainData, predictions2); + CheckMatrices(predictions1, predictions2); +} + +// network1 should be allocated with `new`, and trained on some data. +template +void CheckMoveFunction(ModelType* network1, + MatType& trainData, + MatType& trainLabels, + const size_t maxEpochs) +{ + ens::RMSProp opt(0.01, 32, 0.88, 1e-8, maxEpochs * trainData.n_cols, -1); + network1->Train(trainData, trainLabels, opt); + + arma::mat predictions1; + network1->Predict(trainData, predictions1); + FFN<> network2(std::move(*network1)); + delete network1; + + // Deallocating all of network1's memory, so that + // if network2 is trying to use any of that memory. + arma::mat predictions2; + network2.Predict(trainData, predictions2); + CheckMatrices(predictions1, predictions2); +} + +/** + * Check whether copying and moving Vanila network is working or not. + */ +TEST_CASE("CheckCopyMovingVanillaNetworkTest", "[FeedForwardNetworkTest]") +{ + // Load the dataset. + arma::mat trainData; + data::Load("thyroid_train.csv", trainData, true); + + arma::mat trainLabels = trainData.row(trainData.n_rows - 1); + trainData.shed_row(trainData.n_rows - 1); + + /* + * Construct a feed forward network with trainData.n_rows input nodes, + * hiddenLayerSize hidden nodes and trainLabels.n_rows output nodes. The + * network structure looks like: + * + * Input Hidden Output + * Layer Layer Layer + * +-----+ +-----+ +-----+ + * | | | | | | + * | +------>| +------>| | + * | | +>| | +>| | + * +-----+ | +--+--+ | +-----+ + * | | + * Bias | Bias | + * Layer | Layer | + * +-----+ | +-----+ | + * | | | | | | + * | +-----+ | +-----+ + * | | | | + * +-----+ +-----+ + */ + + FFN > *model = new FFN >; + model->Add >(trainData.n_rows, 8); + model->Add >(); + model->Add >(8, 3); + model->Add >(); + + FFN > *model1 = new FFN >; + model1->Add >(trainData.n_rows, 8); + model1->Add >(); + model1->Add >(8, 3); + model1->Add >(); + + // Check whether copy cpnstructor is working or not. + CheckCopyFunction<>(model, trainData, trainLabels, 1); + + // Check whether move cpnstructor is working or not. + CheckMoveFunction<>(model1, trainData, trainLabels, 1); +} + /** * Train the vanilla network on a larger dataset. */