Merge branch 'master' into gan-callbacks

This commit is contained in:
Hrishikesh Suresh
2019-10-30 01:54:50 +05:30
committed by GitHub
44 changed files with 3333 additions and 282 deletions
+1
View File
@@ -117,6 +117,7 @@ Copyright:
Copyright 2019, Yashwant Singh Parihar <yashwantsingh.sngh@gmail.com>
Copyright 2019, Heet Sankesara <heetsankesara3@gmail.com>
Copyright 2019, Jeffin Sam <sam.jeffin@gmail.com>
Copyright 2019, Vikas S Shetty <shettyvikas209@gmail.com>
License: BSD-3-clause
All rights reserved.
+10
View File
@@ -1,5 +1,15 @@
### mlpack ?.?.?
###### ????-??-??
* Add Model() to the FFN class to access individual layers (#2043).
* Update documentation for pip and conda installation packages (#2044).
* Add bindings for linear SVM (#1935); `mlpack_linear_svm` from the
command-line, `linear_svm()` from Python.
* Add support to return the layer name as `std::string` (#1987).
* Speed and memory improvements for the Transposed Convolution layer (#1493).
### mlpack 3.2.1
###### 2019-10-01
+16 -18
View File
@@ -23,7 +23,7 @@ src="https://cdn.rawgit.com/mlpack/mlpack.org/e7d36ed8/mlpack-black.svg" style="
<p align="center">
<em>
Download:
<a href="https://www.mlpack.org/files/mlpack-3.2.0.tar.gz">current stable version (3.2.0)</a>
<a href="https://www.mlpack.org/files/mlpack-3.2.1.tar.gz">current stable version (3 2.1)</a>
</em>
</p>
@@ -109,7 +109,7 @@ If you are compiling Armadillo by hand, ensure that LAPACK and BLAS are enabled.
### 4. Building mlpack from source
This section discusses how to build mlpack from source. However, mlpack is in
the repositories of many Linux distributions and so it may be easier to use the
the repositories of many Linux distributions, so it may be easier to use the
package manager for your system. For example, on Ubuntu, you can install mlpack
with the following command:
@@ -120,13 +120,13 @@ available---for instance, at the time of this writing, Ubuntu 16.04 only has
mlpack 2.0.1 available. Options include upgrading your Ubuntu version, finding
a PPA or other non-official sources, or installing with a manual build.
There are some other useful pages to consult in addition to this section:
There are some useful pages to consult in addition to this section:
- [Building mlpack From Source](https://www.mlpack.org/doc/mlpack-git/doxygen/build.html)
- [Building mlpack From Source on Windows](https://www.mlpack.org/doc/mlpack-git/doxygen/build_windows.html)
mlpack uses CMake as a build system and allows several flexible build
configuration options. One can consult any of numerous CMake tutorials for
configuration options. You can consult any of the CMake tutorials for
further documentation, but this tutorial should be enough to get mlpack built
and installed.
@@ -136,21 +136,20 @@ use mlpack-x.y.z where x.y.z is the version.
$ tar -xzf mlpack-x.y.z.tar.gz
$ cd mlpack-x.y.z
Then, make a build directory. The directory can have any name, not just
'build', but 'build' is sufficient.
Then, make a build directory. The directory can have any name, but 'build' is
sufficient.
$ mkdir build
$ cd build
The next step is to run CMake to configure the project. Running CMake is the
equivalent to running `./configure` with autotools. If you run CMake with no
options, it will configure the project to build with no debugging symbols and no
profiling information:
options, it will configure the project to build with no debugging symbols and
no profiling information:
$ cmake ../
You can specify options to compile with debugging information and profiling
information:
Options can be specified to compile with debugging information and profiling information:
$ cmake -D DEBUG=ON -D PROFILE=ON ../
@@ -187,22 +186,21 @@ Once CMake is configured, building the library is as simple as typing 'make'.
This will build all library components as well as 'mlpack_test'.
$ make
You can specify individual components which you want to build, if you do not
want to build everything in the library:
If you do not want to build everything in the library, individual components
of the build can be specified:
$ make mlpack_pca mlpack_knn mlpack_kfn
If the build fails and you cannot figure out why, register an account on Github
and submit an issue; the mlpack developers will quickly help you figure it out:
and submit an issue. The mlpack developers will quickly help you figure it out:
[mlpack on Github](https://www.github.com/mlpack/mlpack/)
Alternately, mlpack help can be found in IRC at `#mlpack` on irc.freenode.net.
If you wish to install mlpack to `/usr/local/include/mlpack/` and `/usr/local/lib/`
and `/usr/local/bin/`, once it has built, make sure you have root privileges (or
write permissions to those three directories), and simply type
If you wish to install mlpack to `/usr/local/include/mlpack/`, `/usr/local/lib/`,
and `/usr/local/bin/`, make sure you have root privileges (or write permissions
to those three directories), and simply type
$ make install
@@ -210,7 +208,7 @@ You can now run the executables by name; you can link against mlpack with
`-lmlpack`
and the mlpack headers are found in
`/usr/local/include/mlpack/`
and if Python bindings were built, they will be accessible with the `mlpack`
and if Python bindings were built, you can access them with the `mlpack`
package in Python.
If running the programs (i.e. `$ mlpack_knn -h`) gives an error of the form
+2 -2
View File
@@ -17,11 +17,11 @@ Installing the mlpack bindings for Python is straightforward. It's easy to use
conda or pip to do this:
@code{.sh}
pip install mlpack3
pip install mlpack
@endcode
@code{.sh}
conda install -c mlpack mlpack
conda install -c conda-forge mlpack
@endcode
Otherwise, we can build the Python bindings from scratch, as follows. First we
@@ -168,6 +168,12 @@ class GaussianDistribution
void Covariance(arma::mat&& covariance);
//! Return the invCov.
const arma::mat& InvCov() const { return invCov; }
//! Return the logDetCov.
double LogDetCov() const { return logDetCov; }
/**
* Serialize the distribution.
*/
@@ -238,6 +238,20 @@ class CoverTree
*/
CoverTree(CoverTree&& other);
/**
* Copy the given Cover Tree.
*
* @param other The tree to be copied.
*/
CoverTree& operator=(const CoverTree& other);
/**
* Take ownership of the given Cover Tree.
*
* @param other The tree to take ownership of.
*/
CoverTree& operator=(CoverTree&& other);
/**
* Create a cover tree from a boost::serialization archive.
*/
@@ -497,6 +497,7 @@ CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>::CoverTree(
this->metric = new MetricType();
}
// Copy Constructor.
template<
typename MetricType,
typename StatisticType,
@@ -515,9 +516,9 @@ CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>::CoverTree(
parent(other.parent),
parentDistance(other.parentDistance),
furthestDescendantDistance(other.furthestDescendantDistance),
localMetric(false),
localMetric(other.localMetric),
localDataset(other.parent == NULL && other.localDataset),
metric(other.metric),
metric((other.localMetric ? new MetricType() : other.metric)),
distanceComps(0)
{
// Copy each child by hand.
@@ -547,6 +548,76 @@ CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>::CoverTree(
}
}
// Copy Assignment.
template<
typename MetricType,
typename StatisticType,
typename MatType,
typename RootPointPolicy
>
CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>&
CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>::
operator=(const CoverTree& other)
{
if (this == &other)
return *this;
// Freeing memory that will not be used anymore.
if (localDataset)
delete dataset;
if (localMetric)
delete metric;
for (size_t i = 0; i < children.size(); ++i)
delete children[i];
children.clear();
dataset = ((other.parent == NULL && other.localDataset) ?
new MatType(*other.dataset) : other.dataset);
point = other.point;
scale = other.scale;
base = other.base;
stat = other.stat;
numDescendants = other.numDescendants;
parent = other.parent;
parentDistance = other.parentDistance;
furthestDescendantDistance = other.furthestDescendantDistance;
localMetric = other.localMetric;
localDataset = (other.parent == NULL && other.localDataset);
metric = (other.localMetric ? new MetricType() : other.metric);
distanceComps = 0;
// Copy each child by hand.
for (size_t i = 0; i < other.NumChildren(); ++i)
{
children.push_back(new CoverTree(other.Child(i)));
children[i]->Parent() = this;
}
// Propagate matrix, but only if we are the root.
if (parent == NULL && localDataset)
{
std::queue<CoverTree*> queue;
for (size_t i = 0; i < NumChildren(); ++i)
queue.push(children[i]);
while (!queue.empty())
{
CoverTree* node = queue.front();
queue.pop();
node->dataset = dataset;
for (size_t i = 0; i < node->NumChildren(); ++i)
queue.push(node->children[i]);
}
}
return *this;
}
// Move Constructor.
template<
typename MetricType,
typename StatisticType,
@@ -587,6 +658,64 @@ CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>::CoverTree(
other.metric = NULL;
}
// Move Assignment.
template<
typename MetricType,
typename StatisticType,
typename MatType,
typename RootPointPolicy
>
CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>&
CoverTree<MetricType, StatisticType, MatType, RootPointPolicy>::
operator=(CoverTree&& other)
{
if (this == &other)
return *this;
// Freeing memory that will not be used anymore.
if (localDataset)
delete dataset;
if (localMetric)
delete metric;
for (size_t i = 0; i < children.size(); ++i)
delete children[i];
dataset = other.dataset;
point = other.point;
children = std::move(other.children);
scale = other.scale;
base = other.base;
stat = std::move(other.stat);
numDescendants = other.numDescendants;
parent = other.parent;
parentDistance = other.parentDistance;
furthestDescendantDistance = other.furthestDescendantDistance;
localMetric = other.localMetric;
localDataset = other.localDataset;
metric = other.metric;
distanceComps = other.distanceComps;
// Set proper parent pointer.
for (size_t i = 0; i < children.size(); ++i)
children[i]->Parent() = this;
other.dataset = NULL;
other.point = 0;
other.scale = INT_MIN;
other.base = 0;
other.numDescendants = 0;
other.parent = NULL;
other.parentDistance = 0;
other.furthestDescendantDistance = 0;
other.localMetric = false;
other.localDataset = false;
other.metric = NULL;
return *this;
}
// Construct from a boost::serialization archive.
template<
typename MetricType,
@@ -209,6 +209,20 @@ class SpillTree
*/
SpillTree(SpillTree&& other);
/**
* Copy the given Spill Tree.
*
* @param other The tree to be copied.
*/
SpillTree& operator=(const SpillTree& other);
/**
* Take ownership of the given Spill Tree.
*
* @param other The tree to take ownership of.
*/
SpillTree& operator=(SpillTree&& other);
/**
* Initialize the tree from a boost::serialization archive.
*
@@ -192,6 +192,88 @@ SpillTree(const SpillTree& other) :
}
}
/**
* Copy Assignment.
*/
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename HyperplaneMetricType> class HyperplaneType,
template<typename SplitMetricType, typename SplitMatType>
class SplitType>
SpillTree<MetricType, StatisticType, MatType, HyperplaneType, SplitType>&
SpillTree<MetricType, StatisticType, MatType, HyperplaneType, SplitType>::
operator=(const SpillTree& other)
{
if (this == &other)
return *this;
// Freeing memory that will not be used anymore.
if (localDataset)
delete dataset;
delete pointsIndex;
delete left;
delete right;
left = NULL;
right = NULL;
parent = other.parent;
count = other.count;
pointsIndex = NULL;
overlappingNode = other.overlappingNode;
hyperplane = other.hyperplane;
bound = other.bound;
stat = other.stat;
parentDistance = other.parentDistance;
furthestDescendantDistance = other.furthestDescendantDistance;
// Copy matrix, but only if we are the root and the other tree has its own
// copy of the dataset.
dataset = (other.parent == NULL && other.localDataset) ?
new MatType(*other.dataset) : other.dataset;
localDataset = other.parent == NULL && other.localDataset;
// Create left and right children (if any).
if (other.Left())
{
left = new SpillTree(*other.Left());
left->Parent() = this; // Set parent to this, not other tree.
}
if (other.Right())
{
right = new SpillTree(*other.Right());
right->Parent() = this; // Set parent to this, not other tree.
}
// If vector of indexes, copy it.
if (other.pointsIndex)
pointsIndex = new arma::Col<size_t>(*other.pointsIndex);
// Propagate matrix, but only if we are the root.
if (parent == NULL && localDataset)
{
std::queue<SpillTree*> queue;
if (left)
queue.push(left);
if (right)
queue.push(right);
while (!queue.empty())
{
SpillTree* node = queue.front();
queue.pop();
node->dataset = dataset;
if (node->left)
queue.push(node->left);
if (node->right)
queue.push(node->right);
}
}
return *this;
}
/**
* Move constructor.
*/
@@ -237,6 +319,66 @@ SpillTree(SpillTree&& other) :
right->parent = this;
}
/**
* Move Assignment.
*/
template<typename MetricType,
typename StatisticType,
typename MatType,
template<typename HyperplaneMetricType> class HyperplaneType,
template<typename SplitMetricType, typename SplitMatType>
class SplitType>
SpillTree<MetricType, StatisticType, MatType, HyperplaneType, SplitType>&
SpillTree<MetricType, StatisticType, MatType, HyperplaneType, SplitType>::
operator=(SpillTree&& other)
{
if (this == &other)
return *this;
// Freeing memory that will not be used anymore.
if (localDataset)
delete dataset;
delete pointsIndex;
delete left;
delete right;
left = other.left;
right = other.right;
parent = other.parent;
count = other.count;
pointsIndex = other.pointsIndex;
overlappingNode = other.overlappingNode;
hyperplane = other.hyperplane;
bound = std::move(other.bound);
stat = std::move(other.stat);
parentDistance = other.parentDistance;
furthestDescendantDistance = other.furthestDescendantDistance;
minimumBoundDistance = other.minimumBoundDistance;
dataset = other.dataset;
localDataset = other.localDataset;
// Now we are a clone of the other tree. But we must also clear the other
// tree's contents, so it doesn't delete anything when it is destructed.
other.left = NULL;
other.right = NULL;
other.count = 0;
other.pointsIndex = NULL;
other.parentDistance = 0.0;
other.furthestDescendantDistance = 0.0;
other.minimumBoundDistance = 0.0;
other.dataset = NULL;
other.localDataset = false;
// Set new parent.
if (left)
left->parent = this;
if (right)
right->parent = this;
return *this;
}
/**
* Initialize the tree from an archive.
*/
+1
View File
@@ -7,6 +7,7 @@ set(SOURCES
rnn_impl.hpp
brnn.hpp
brnn_impl.hpp
layer_names.hpp
)
add_subdirectory(visitor)
+19 -4
View File
@@ -94,15 +94,18 @@ class FFN
* object, be sure to use std::move to avoid unnecessary copy.
*
* @tparam OptimizerType Type of optimizer to use to train the model.
* @tparam CallbackTypes Types of Callback Functions.
* @param predictors Input training variables.
* @param responses Outputs results from input training variables.
* @param optimizer Instantiated optimizer used to train the model.
* @param callbacks Callback Functions.
* @return The final objective of the trained model (NaN or Inf on error).
*/
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double Train(arma::mat predictors,
arma::mat responses,
OptimizerType& optimizer);
OptimizerType& optimizer,
CallbackTypes&&... callbacks);
/**
* Train the feedforward network on the given input data. By default, the
@@ -118,11 +121,15 @@ class FFN
*
* @tparam OptimizerType Type of optimizer to use to train the model.
* @param predictors Input training variables.
* @tparam CallbackTypes Types of Callback Functions.
* @param responses Outputs results from input training variables.
* @param callbacks Callback Functions.
* @return The final objective of the trained model (NaN or Inf on error).
*/
template<typename OptimizerType = ens::RMSProp>
double Train(arma::mat predictors, arma::mat responses);
template<typename OptimizerType = ens::RMSProp, typename... CallbackTypes>
double Train(arma::mat predictors,
arma::mat responses,
CallbackTypes&&... callbacks);
/**
* Predict the responses to a given set of predictors. The responses will
@@ -257,6 +264,14 @@ class FFN
*/
void Add(LayerTypes<CustomLayers...> layer) { network.push_back(layer); }
//! Get the network model.
const std::vector<LayerTypes<CustomLayers...> >& Model() const
{
return network;
}
//! Modify the network model.
std::vector<LayerTypes<CustomLayers...> >& Model() { return network; }
//! Return the number of separable functions (the number of predictor points).
size_t NumFunctions() const { return numFunctions; }
+9 -6
View File
@@ -69,17 +69,18 @@ void FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::ResetData(
template<typename OutputLayerType, typename InitializationRuleType,
typename... CustomLayers>
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
arma::mat predictors,
arma::mat responses,
OptimizerType& optimizer)
OptimizerType& optimizer,
CallbackTypes&&... callbacks)
{
ResetData(std::move(predictors), std::move(responses));
// Train the model.
Timer::Start("ffn_optimization");
const double out = optimizer.Optimize(*this, parameter);
const double out = optimizer.Optimize(*this, parameter, callbacks...);
Timer::Stop("ffn_optimization");
Log::Info << "FFN::FFN(): final objective of trained model is " << out
@@ -89,9 +90,11 @@ double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
template<typename OutputLayerType, typename InitializationRuleType,
typename... CustomLayers>
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
arma::mat predictors, arma::mat responses)
arma::mat predictors,
arma::mat responses,
CallbackTypes&&... callbacks)
{
ResetData(std::move(predictors), std::move(responses));
@@ -99,7 +102,7 @@ double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
// Train the model.
Timer::Start("ffn_optimization");
const double out = optimizer.Optimize(*this, parameter);
const double out = optimizer.Optimize(*this, parameter, callbacks...);
Timer::Stop("ffn_optimization");
Log::Info << "FFN::FFN(): final objective of trained model is " << out
+27 -11
View File
@@ -66,9 +66,12 @@ class GAN
/**
* Constructor for GAN class.
*
* @param trainData The real data.
* @param generator Generator network.
* @param discriminator Discriminator network.
* @param initializeRule Initialization rule to use for initializing
* parameters.
* @param noiseFunction Function to be used for generating noise.
* @param noiseDim Dimension of noise vector to be created.
* @param batchSize Batch size to be used for training.
* @param generatorUpdateStep Number of steps to train Discriminator
* before updating Generator.
@@ -77,8 +80,7 @@ class GAN
* @param clippingParameter Weight range for enforcing Lipschitz constraint.
* @param lambda Parameter for setting the gradient penalty.
*/
GAN(arma::mat& trainData,
Model generator,
GAN(Model generator,
Model discriminator,
InitializationRuleType& initializeRule,
Noise& noiseFunction,
@@ -96,6 +98,14 @@ class GAN
//! Move constructor.
GAN(GAN&&);
/**
* Initialize the generator, discriminator and weights of the model for
* training. This function won't actually trigger training process.
*
* @param trainData The data points of real distribution.
*/
void ResetData(arma::mat trainData);
// Reset function.
void Reset();
@@ -110,7 +120,6 @@ class GAN
*/
template<typename OptimizerType, typename... CallbackTypes>
double Train(OptimizerType& Optimizer, CallbackTypes&&... callbacks);
/**
* Evaluate function for the Standard GAN and DCGAN.
* This function gives the performance of the Standard GAN or DCGAN on the
@@ -280,11 +289,10 @@ class GAN
/**
* This function predicts the output of the network on the given input.
*
* @param input The input the Discriminator network.
* @param input The input of the Generator network.
* @param output Result of the Discriminator network.
*/
void Predict(arma::mat&& input,
arma::mat& output);
void Predict(arma::mat input, arma::mat& output);
//! Return the parameters of the network.
const arma::mat& Parameters() const { return parameter; }
@@ -318,6 +326,12 @@ class GAN
void serialize(Archive& ar, const unsigned int /* version */);
private:
/**
* Reset the module status by setting the current deterministic parameter
* for the discriminator and generator networks and their respective layers.
*/
void ResetDeterministic();
//! Locally stored parameter for training data + noise data.
arma::mat predictors;
//! Locally stored parameters of the network.
@@ -336,8 +350,6 @@ class GAN
size_t numFunctions;
//! Locally stored batch size parameter.
size_t batchSize;
//! Locally stored number of iterations that have been completed.
size_t counter;
//! Locally stored batch number which is being processed.
size_t currentBatch;
//! Locally stored number of training step before Generator is trained.
@@ -378,8 +390,12 @@ class GAN
arma::mat noise;
//! Locally stored gradient for Generator.
arma::mat gradientGenerator;
//! Locally stored output of the Generator network.
arma::mat ganOutput;
//! The current evaluation mode (training or testing).
bool deterministic;
//! To keep track of number of generator weights in total weights.
size_t genWeights;
//! To keep track of number of discriminator weights in total weights.
size_t discWeights;
};
} // namespace ann
+138 -45
View File
@@ -30,7 +30,6 @@ template<
typename PolicyType
>
GAN<Model, InitializationRuleType, Noise, PolicyType>::GAN(
arma::mat& predictors,
Model generator,
Model discriminator,
InitializationRuleType& initializeRule,
@@ -47,41 +46,23 @@ GAN<Model, InitializationRuleType, Noise, PolicyType>::GAN(
initializeRule(initializeRule),
noiseFunction(noiseFunction),
noiseDim(noiseDim),
numFunctions(0),
batchSize(batchSize),
currentBatch(0),
generatorUpdateStep(generatorUpdateStep),
preTrainSize(preTrainSize),
multiplier(multiplier),
clippingParameter(clippingParameter),
lambda(lambda),
reset(false)
reset(false),
deterministic(false),
genWeights(0),
discWeights(0)
{
// Insert IdentityLayer for joining the Generator and Discriminator.
this->discriminator.network.insert(
this->discriminator.network.begin(),
new IdentityLayer<>());
counter = 0;
currentBatch = 0;
this->discriminator.deterministic = this->generator.deterministic = true;
this->predictors.set_size(predictors.n_rows, predictors.n_cols + batchSize);
this->predictors.cols(0, predictors.n_cols - 1) = predictors;
this->discriminator.predictors = arma::mat(this->predictors.memptr(),
this->predictors.n_rows, this->predictors.n_cols, false, false);
responses.ones(1, predictors.n_cols + batchSize);
responses.cols(predictors.n_cols,
predictors.n_cols + batchSize - 1) = arma::zeros(1, batchSize);
this->discriminator.responses = arma::mat(this->responses.memptr(),
this->responses.n_rows, this->responses.n_cols, false, false);
numFunctions = predictors.n_cols;
noise.set_size(noiseDim, batchSize);
this->generator.predictors.set_size(noiseDim, batchSize);
this->generator.responses.set_size(predictors.n_rows, batchSize);
}
template<
@@ -106,11 +87,13 @@ GAN<Model, InitializationRuleType, Noise, PolicyType>::GAN(
clippingParameter(network.clippingParameter),
lambda(network.lambda),
reset(network.reset),
counter(network.counter),
currentBatch(network.currentBatch),
parameter(network.parameter),
numFunctions(network.numFunctions),
noise(network.noise)
noise(network.noise),
deterministic(network.deterministic),
genWeights(network.genWeights),
discWeights(network.discWeights)
{
/* Nothing to do here */
}
@@ -137,15 +120,59 @@ GAN<Model, InitializationRuleType, Noise, PolicyType>::GAN(
clippingParameter(network.clippingParameter),
lambda(network.lambda),
reset(network.reset),
counter(network.counter),
currentBatch(network.currentBatch),
parameter(std::move(network.parameter)),
numFunctions(network.numFunctions),
noise(std::move(network.noise))
noise(std::move(network.noise)),
deterministic(network.deterministic),
genWeights(network.genWeights),
discWeights(network.discWeights)
{
/* Nothing to do here */
}
template<
typename Model,
typename InitializationRuleType,
typename Noise,
typename PolicyType
>
void GAN<Model, InitializationRuleType, Noise, PolicyType>::ResetData(
arma::mat trainData)
{
currentBatch = 0;
numFunctions = trainData.n_cols;
noise.set_size(noiseDim, batchSize);
deterministic = true;
ResetDeterministic();
/**
* These predictors are shared by the discriminator network. The additional
* batch size predictors are taken from the generator network while training.
* For more details please look in EvaluateWithGradient() function.
*/
this->predictors.set_size(trainData.n_rows, numFunctions + batchSize);
this->predictors.cols(0, numFunctions - 1) = std::move(trainData);
this->discriminator.predictors = arma::mat(this->predictors.memptr(),
this->predictors.n_rows, this->predictors.n_cols, false, false);
responses.ones(1, numFunctions + batchSize);
responses.cols(numFunctions, numFunctions + batchSize - 1) =
arma::zeros(1, batchSize);
this->discriminator.responses = arma::mat(this->responses.memptr(),
this->responses.n_rows, this->responses.n_cols, false, false);
this->generator.predictors.set_size(noiseDim, batchSize);
this->generator.responses.set_size(predictors.n_rows, batchSize);
if (!reset)
{
Reset();
}
}
template<
typename Model,
typename InitializationRuleType,
@@ -154,8 +181,8 @@ template<
>
void GAN<Model, InitializationRuleType, Noise, PolicyType>::Reset()
{
size_t genWeights = 0;
size_t discWeights = 0;
genWeights = 0;
discWeights = 0;
NetworkInitialization<InitializationRuleType> networkInit(initializeRule);
@@ -214,8 +241,16 @@ GAN<Model, InitializationRuleType, Noise, PolicyType>::Evaluate(
const size_t i,
const size_t /* batchSize */)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
if (!deterministic)
{
deterministic = true;
ResetDeterministic();
}
currentInput = arma::mat(predictors.memptr() + (i * predictors.n_rows),
predictors.n_rows, batchSize, false, false);
@@ -263,8 +298,10 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
GradType& gradient,
const size_t /* batchSize */)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
if (gradient.is_empty())
{
@@ -275,6 +312,12 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
else
gradient.zeros();
if (this->deterministic)
{
this->deterministic = false;
ResetDeterministic();
}
if (noiseGradientDiscriminator.is_empty())
{
noiseGradientDiscriminator = arma::zeros<arma::mat>(
@@ -326,14 +369,8 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
gradientGenerator *= multiplier;
}
counter++;
currentBatch++;
// Revert the counter to zero, if the total dataset get's covered.
if (counter * batchSize >= numFunctions)
{
counter = 0;
}
if (preTrainSize > 0)
{
@@ -383,12 +420,13 @@ template<
void GAN<Model, InitializationRuleType, Noise, PolicyType>::Forward(
arma::mat&& input)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
generator.Forward(std::move(input));
ganOutput = boost::apply_visitor(
outputParameterVisitor,
arma::mat ganOutput = boost::apply_visitor(outputParameterVisitor,
generator.network.back());
discriminator.Forward(std::move(ganOutput));
@@ -401,10 +439,18 @@ template<
typename PolicyType
>
void GAN<Model, InitializationRuleType, Noise, PolicyType>::
Predict(arma::mat&& input, arma::mat& output)
Predict(arma::mat input, arma::mat& output)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
if (!deterministic)
{
deterministic = true;
ResetDeterministic();
}
Forward(std::move(input));
@@ -412,6 +458,21 @@ Predict(arma::mat&& input, arma::mat& output)
discriminator.network.back());
}
template<
typename Model,
typename InitializationRuleType,
typename Noise,
typename PolicyType
>
void GAN<Model, InitializationRuleType, Noise, PolicyType>::
ResetDeterministic()
{
this->discriminator.deterministic = deterministic;
this->generator.deterministic = deterministic;
this->discriminator.ResetDeterministic();
this->generator.ResetDeterministic();
}
template<
typename Model,
typename InitializationRuleType,
@@ -425,7 +486,39 @@ serialize(Archive& ar, const unsigned int /* version */)
ar & BOOST_SERIALIZATION_NVP(parameter);
ar & BOOST_SERIALIZATION_NVP(generator);
ar & BOOST_SERIALIZATION_NVP(discriminator);
ar & BOOST_SERIALIZATION_NVP(noiseFunction);
ar & BOOST_SERIALIZATION_NVP(reset);
ar & BOOST_SERIALIZATION_NVP(genWeights);
ar & BOOST_SERIALIZATION_NVP(discWeights);
if (Archive::is_loading::value)
{
// Share the parameters between the network.
generator.Parameters() = arma::mat(parameter.memptr(), genWeights, 1, false,
false);
discriminator.Parameters() = arma::mat(parameter.memptr() + genWeights,
discWeights, 1, false, false);
size_t offset = 0;
for (size_t i = 0; i < generator.network.size(); ++i)
{
offset += boost::apply_visitor(WeightSetVisitor(std::move(
generator.parameter), offset), generator.network[i]);
boost::apply_visitor(resetVisitor, generator.network[i]);
}
offset = 0;
for (size_t i = 0; i < discriminator.network.size(); ++i)
{
offset += boost::apply_visitor(WeightSetVisitor(std::move(
discriminator.parameter), offset), discriminator.network[i]);
boost::apply_visitor(resetVisitor, discriminator.network[i]);
}
deterministic = true;
ResetDeterministic();
}
}
} // namespace ann
+18 -9
View File
@@ -34,8 +34,16 @@ GAN<Model, InitializationRuleType, Noise, PolicyType>::Evaluate(
const size_t i,
const size_t /* batchSize */)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
if (!deterministic)
{
deterministic = true;
ResetDeterministic();
}
currentInput = arma::mat(predictors.memptr() + (i * predictors.n_rows),
predictors.n_rows, batchSize, false, false);
@@ -82,8 +90,10 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
GradType& gradient,
const size_t /* batchSize */)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
if (gradient.is_empty())
{
@@ -94,6 +104,12 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
else
gradient.zeros();
if (this->deterministic)
{
this->deterministic = false;
ResetDeterministic();
}
if (noiseGradientDiscriminator.is_empty())
{
noiseGradientDiscriminator = arma::zeros<arma::mat>(
@@ -147,15 +163,8 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
gradientGenerator *= multiplier;
}
counter++;
currentBatch++;
// Revert the counter to zero, if the total dataset get's covered.
if (counter * batchSize >= numFunctions)
{
counter = 0;
}
if (preTrainSize > 0)
{
preTrainSize--;
+18 -9
View File
@@ -35,8 +35,16 @@ GAN<Model, InitializationRuleType, Noise, PolicyType>::Evaluate(
const size_t i,
const size_t /* batchSize */)
{
if (!reset)
if ((parameter.is_empty()))
{
Reset();
}
if (!deterministic)
{
deterministic = true;
ResetDeterministic();
}
currentInput = arma::mat(predictors.memptr() + (i * predictors.n_rows),
predictors.n_rows, batchSize, false, false);
@@ -96,8 +104,10 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
GradType& gradient,
const size_t /* batchSize */)
{
if (!reset)
if (parameter.is_empty())
{
Reset();
}
if (gradient.is_empty())
{
@@ -108,6 +118,12 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
else
gradient.zeros();
if (this->deterministic)
{
this->deterministic = false;
ResetDeterministic();
}
if (noiseGradientDiscriminator.is_empty())
{
noiseGradientDiscriminator = arma::zeros<arma::mat>(
@@ -171,15 +187,8 @@ EvaluateWithGradient(const arma::mat& /* parameters */,
gradientGenerator *= multiplier;
}
counter++;
currentBatch++;
// Revert the counter to zero, if the total dataset get's covered.
if (counter * batchSize >= numFunctions)
{
counter = 0;
}
if (preTrainSize > 0)
{
preTrainSize--;
@@ -309,6 +309,28 @@ class AtrousConvolution
} // namespace ann
} // namespace mlpack
//! Set the serialization version of the AtrousConvolution class.
namespace boost {
namespace serialization {
template<
typename ForwardConvolutionRule,
typename BackwardConvolutionRule,
typename GradientConvolutionRule,
typename InputDataType,
typename OutputDataType
>
struct version<
mlpack::ann::AtrousConvolution<ForwardConvolutionRule,
BackwardConvolutionRule, GradientConvolutionRule, InputDataType,
OutputDataType> >
{
BOOST_STATIC_CONSTANT(int, value = 1);
};
} // namespace serialization
} // namespace boost
// Include implementation
#include "atrous_convolution_impl.hpp"
@@ -339,8 +339,7 @@ void AtrousConvolution<
GradientConvolutionRule,
InputDataType,
OutputDataType
>::serialize(
Archive& ar, const unsigned int /* version */)
>::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(inSize);
ar & BOOST_SERIALIZATION_NVP(outSize);
@@ -358,6 +357,9 @@ void AtrousConvolution<
ar & BOOST_SERIALIZATION_NVP(dilationW);
ar & BOOST_SERIALIZATION_NVP(dilationH);
if (version > 0)
ar & BOOST_SERIALIZATION_NVP(padding);
if (Archive::is_loading::value)
weights.set_size((outSize * inSize * kW * kH) + outSize, 1);
}
@@ -300,6 +300,27 @@ class Convolution
} // namespace ann
} // namespace mlpack
//! Set the serialization version of the Convolution class.
namespace boost {
namespace serialization {
template<
typename ForwardConvolutionRule,
typename BackwardConvolutionRule,
typename GradientConvolutionRule,
typename InputDataType,
typename OutputDataType
>
struct version<
mlpack::ann::Convolution<ForwardConvolutionRule, BackwardConvolutionRule,
GradientConvolutionRule, InputDataType, OutputDataType> >
{
BOOST_STATIC_CONSTANT(int, value = 1);
};
} // namespace serialization
} // namespace boost
// Include implementation.
#include "convolution_impl.hpp"
@@ -319,8 +319,7 @@ void Convolution<
GradientConvolutionRule,
InputDataType,
OutputDataType
>::serialize(
Archive& ar, const unsigned int /* version */)
>::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(inSize);
ar & BOOST_SERIALIZATION_NVP(outSize);
@@ -336,6 +335,9 @@ void Convolution<
ar & BOOST_SERIALIZATION_NVP(outputWidth);
ar & BOOST_SERIALIZATION_NVP(outputHeight);
if (version > 0)
ar & BOOST_SERIALIZATION_NVP(padding);
if (Archive::is_loading::value)
weights.set_size((outSize * inSize * kW * kH) + outSize, 1);
}
+1 -1
View File
@@ -213,7 +213,7 @@ using LayerTypes = boost::variant<
NaiveConvolution<FullConvolution>,
NaiveConvolution<ValidConvolution>, arma::mat, arma::mat>*,
TransposedConvolution<NaiveConvolution<ValidConvolution>,
NaiveConvolution<FullConvolution>,
NaiveConvolution<ValidConvolution>,
NaiveConvolution<ValidConvolution>, arma::mat, arma::mat>*,
DropConnect<arma::mat, arma::mat>*,
Dropout<arma::mat, arma::mat>*,
@@ -40,7 +40,7 @@ namespace ann /** Artificial Neural Network. */ {
*/
template <
typename ForwardConvolutionRule = NaiveConvolution<ValidConvolution>,
typename BackwardConvolutionRule = NaiveConvolution<FullConvolution>,
typename BackwardConvolutionRule = NaiveConvolution<ValidConvolution>,
typename GradientConvolutionRule = NaiveConvolution<ValidConvolution>,
typename InputDataType = arma::mat,
typename OutputDataType = arma::mat
@@ -52,8 +52,14 @@ class TransposedConvolution
TransposedConvolution();
/**
* Create the Transposed Convolution object using the specified number of input maps,
* output maps, filter size, stride and padding parameter.
* Create the Transposed Convolution object using the specified number of
* input maps, output maps, filter size, stride and padding parameter.
*
* Note: The equivalent stride of a transposed convolution operation is always
* equal to 1. In this implementation, stride of filter represents the stride
* of the associated convolution operation.
* Note: Padding of input represents padding of associated convolution
* operation.
*
* @param inSize The number of input maps.
* @param outSize The number of output maps.
@@ -65,6 +71,8 @@ class TransposedConvolution
* @param padH Padding height of the input.
* @param inputWidth The width of the input data.
* @param inputHeight The height of the input data.
* @param outputWidth The width of the output data.
* @param outputHeight The height of the output data.
*/
TransposedConvolution(const size_t inSize,
const size_t outSize,
@@ -75,7 +83,9 @@ class TransposedConvolution
const size_t padW = 0,
const size_t padH = 0,
const size_t inputWidth = 0,
const size_t inputHeight = 0);
const size_t inputHeight = 0,
const size_t outputWidth = 0,
const size_t outputHeight = 0);
/*
* Set the weight and bias term.
@@ -173,24 +183,6 @@ class TransposedConvolution
void serialize(Archive& ar, const unsigned int /* version */);
private:
/*
* Return the transposed convolution output size.
*
* @param size The size of the input (row or column).
* @param k The size of the filter (width or height).
* @param s The stride size (x or y direction).
* @param p The size of the padding (width or height).
* @return The transposed convolution output size.
*/
size_t TransposedConvOutSize(const size_t size,
const size_t k,
const size_t s,
const size_t p)
{
size_t out = std::floor(size - k + 2 * p) / s;
return out * s + 2 * (k - p) - 1 + ((((size + 2 * p - k) % s) + s) % s);
}
/*
* Rotates a 3rd-order tensor counterclockwise by 180 degrees.
*
@@ -220,6 +212,119 @@ class TransposedConvolution
output = arma::fliplr(arma::flipud(input));
}
/*
* Pad the given input data.
*
* @param input The input to be padded.
* @param wPad Padding width of the input.
* @param hPad Padding height of the input.
* @param wExtra The number of extra zeros to the right.
* @param hExtra The number of extra zeros to the bottom.
* @param output The padded output data.
*/
template<typename eT>
void Pad(const arma::Mat<eT>& input,
const size_t wPad,
const size_t hPad,
const size_t wExtra,
const size_t hExtra,
arma::Mat<eT>& output)
{
if (output.n_rows != input.n_rows + wPad * 2 + wExtra ||
output.n_cols != input.n_cols + hPad * 2 + hExtra)
{
output = arma::zeros(input.n_rows + wPad * 2 + wExtra,
input.n_cols + hPad * 2 + hExtra);
}
output.submat(wPad, hPad, wPad + input.n_rows - 1,
hPad + input.n_cols - 1) = input;
}
/*
* Pad the given input data.
*
* @param input The input to be padded.
* @param wPad Padding width of the input.
* @param hPad Padding height of the input.
* @param wExtra The number of extra zeros to the right.
* @param hExtra The number of extra zeros to the bottom.
* @param output The padded output data.
*/
template<typename eT>
void Pad(const arma::Cube<eT>& input,
const size_t wPad,
const size_t hPad,
const size_t wExtra,
const size_t hExtra,
arma::Cube<eT>& output)
{
output = arma::zeros(input.n_rows + wPad * 2 + wExtra,
input.n_cols + hPad * 2 + hExtra, input.n_slices);
for (size_t i = 0; i < input.n_slices; ++i)
{
Pad<eT>(input.slice(i), wPad, hPad, wExtra, hExtra, output.slice(i));
}
}
/*
* Insert zeros between the units of the given input data.
* Note: This function should be used before the Pad() function.
*
* @param input The input to be padded.
* @param dW Stride of filter application in the x direction.
* @param dH Stride of filter application in the y direction.
* @param output The padded output data.
*/
template<typename eT>
void InsertZeros(const arma::Mat<eT>& input,
const size_t dW,
const size_t dH,
arma::Mat<eT>& output)
{
if (output.n_rows != input.n_rows * dW - dW + 1 ||
output.n_cols != input.n_cols * dH - dH + 1)
{
output = arma::zeros(input.n_rows * dW - dW + 1,
input.n_cols * dH - dH + 1);
}
for (size_t i = 0; i < output.n_rows; i += dH)
{
for (size_t j = 0; j < output.n_cols; j += dW)
{
// TODO: Use [] instead of () for speedup after this is completely
// debugged and approved.
output(i, j) = input(i / dH, j / dW);
}
}
}
/*
* Insert zeros between the units of the given input data.
* Note: This function should be used before the Pad() function.
*
* @param input The input to be padded.
* @param dW Stride of filter application in the x direction.
* @param dH Stride of filter application in the y direction.
* @param output The padded output data.
*/
template<typename eT>
void InsertZeros(const arma::Cube<eT>& input,
const size_t dW,
const size_t dH,
arma::Cube<eT>& output)
{
output = arma::zeros(input.n_rows * dW - dW + 1,
input.n_cols * dH - dH + 1, input.n_slices);
for (size_t i = 0; i < input.n_slices; ++i)
{
InsertZeros<eT>(input.slice(i), dW, dH, output.slice(i));
}
}
//! Locally-stored number of input channels.
size_t inSize;
@@ -247,6 +352,12 @@ class TransposedConvolution
//! Locally-stored padding height.
size_t padH;
//! Locally-stored number of zeros added to the right of input.
size_t aW;
//! Locally-stored number of zeros added to the top of input.
size_t aH;
//! Locally-stored weight object.
OutputDataType weights;
@@ -277,6 +388,9 @@ class TransposedConvolution
//! Locally-stored transformed padded input parameter.
arma::cube inputPaddedTemp;
//! Locally-stored transformed expanded input parameter.
arma::cube inputExpandedTemp;
//! Locally-stored transformed error parameter.
arma::cube gTemp;
@@ -60,22 +60,37 @@ TransposedConvolution<
const size_t padW,
const size_t padH,
const size_t inputWidth,
const size_t inputHeight) :
const size_t inputHeight,
const size_t outputWidth,
const size_t outputHeight) :
inSize(inSize),
outSize(outSize),
kW(kW),
kH(kH),
dW(dW),
dH(dH),
padW(padW),
padH(padH),
padW(kW - padW - 1),
padH(kH - padH - 1),
inputWidth(inputWidth),
inputHeight(inputHeight),
outputWidth(0),
outputHeight(0)
outputWidth(outputWidth),
outputHeight(outputHeight)
{
weights.set_size((outSize * inSize * kW * kH) + outSize, 1);
padding = new Padding<>(padW, padW, padH, padH);
// TODO: Use the Padding layer.
// padding = new Padding<>(this->padW, this->padW, this->padH, this->padH);
aW = (outputWidth + kW - 2 * this->padW - 2) % dW;
aH = (outputHeight + kH - 2 * this->padH - 2) % dH;
// Check if the output height and width are possible given the other
// parameters of the layer.
if (outputWidth != dW * (inputWidth - 1) + aW + 2 * this->padW + 2 - kW ||
outputHeight != dH * (inputHeight - 1) + aH + 2 * this->padH + 2 - kH)
{
Log::Fatal << "The output width / output height is not possible given "
<< "the other parameters of the layer." << std::endl;
}
}
template<
@@ -119,8 +134,23 @@ void TransposedConvolution<
inputTemp = arma::cube(const_cast<arma::Mat<eT>&&>(input).memptr(),
inputWidth, inputHeight, inSize * batchSize, false, false);
outputWidth = TransposedConvOutSize(inputWidth, kW, dW, padW);
outputHeight = TransposedConvOutSize(inputHeight, kH, dH, padH);
if (dW > 1 || dH > 1)
{
InsertZeros(inputTemp, dW, dH, inputExpandedTemp);
if (padW != 0 || padH != 0 || aW != 0 || aH != 0)
Pad(inputExpandedTemp, padW, padH, aW, aH, inputPaddedTemp);
else
{
inputPaddedTemp = arma::Cube<eT>(inputExpandedTemp.memptr(),
inputExpandedTemp.n_rows, inputExpandedTemp.n_cols,
inputExpandedTemp.n_slices, false, false);;
}
}
else if (padW != 0 || padH != 0 || aW != 0 || aH != 0)
{
Pad(inputTemp, padW, padH, aW, aH, inputPaddedTemp);
}
output.set_size(outputWidth * outputHeight * outSize, batchSize);
outputTemp = arma::Cube<eT>(output.memptr(), outputWidth, outputHeight,
@@ -141,8 +171,16 @@ void TransposedConvolution<
arma::Mat<eT> convOutput, rotatedFilter;
Rotate180(weight.slice(outMapIdx), rotatedFilter);
BackwardConvolutionRule::Convolution(inputTemp.slice(inMap +
batchCount * inSize), rotatedFilter, convOutput, 1, 1);
if (dW > 1 || dH > 1 || padW != 0 || padH != 0 || aW != 0 || aH != 0)
{
ForwardConvolutionRule::Convolution(inputPaddedTemp.slice(inMap +
batchCount * inSize), rotatedFilter, convOutput, 1, 1);
}
else
{
ForwardConvolutionRule::Convolution(inputTemp.slice(inMap +
batchCount * inSize), rotatedFilter, convOutput, 1, 1);
}
outputTemp.slice(outMap) += convOutput;
}
@@ -168,8 +206,13 @@ void TransposedConvolution<
>::Backward(
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
{
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight,
arma::Cube<eT> mappedError(gy.memptr(), outputWidth, outputHeight,
outSize * batchSize, false, false);
arma::Cube<eT> mappedErrorPadded;
if ((int)(kW - padW - 1) > 0 || (int)(kH - padH - 1) > 0)
Pad(mappedError, kW - padW - 1, kH - padH - 1, 0, 0, mappedErrorPadded);
g.set_size(inputTemp.n_rows * inputTemp.n_cols * inSize, batchSize);
gTemp = arma::Cube<eT>(g.memptr(), inputTemp.n_rows,
inputTemp.n_cols, inputTemp.n_slices, false, false);
@@ -189,8 +232,16 @@ void TransposedConvolution<
{
arma::Mat<eT> output;
ForwardConvolutionRule::Convolution(mappedError.slice(outMap),
weight.slice(outMapIdx), output, 1, 1);
if ((int)(kW - padW - 1) > 0 || (int)(kH - padH - 1) > 0)
{
BackwardConvolutionRule::Convolution(mappedErrorPadded.slice(outMap),
weight.slice(outMapIdx), output, dW, dH);
}
else
{
BackwardConvolutionRule::Convolution(mappedError.slice(outMap),
weight.slice(outMapIdx), output, dW, dH);
}
gTemp.slice(inMap + batchCount * inSize) += output;
}
@@ -216,7 +267,7 @@ void TransposedConvolution<
arma::Mat<eT>&& error,
arma::Mat<eT>&& gradient)
{
arma::cube mappedError(error.memptr(), outputWidth,
arma::Cube<eT> mappedError(error.memptr(), outputWidth,
outputHeight, outSize * batchSize, false, false);
gradient.set_size(weights.n_elem, 1);
@@ -224,6 +275,8 @@ void TransposedConvolution<
weight.n_cols, weight.n_slices, false, false);
gradientTemp.zeros();
arma::Mat<eT> inputSlice, output, deltaSlice, rotatedOutput;
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
outSize * batchSize; outMap++)
{
@@ -233,16 +286,23 @@ void TransposedConvolution<
outMapIdx = 0;
}
deltaSlice = mappedError.slice(outMap);
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
{
arma::Mat<eT> inputSlice, output;
inputSlice = inputTemp.slice(inMap + batchCount * inSize);
arma::Mat<eT> deltaSlice = mappedError.slice(outMap);
if (dW > 1 || dH > 1 || padW != 0 || padH != 0 || aW != 0 || aH != 0)
{
inputSlice = inputPaddedTemp.slice(inMap + batchCount * inSize);
}
else
{
inputSlice = inputTemp.slice(inMap + batchCount * inSize);
}
GradientConvolutionRule::Convolution(deltaSlice, inputSlice,
GradientConvolutionRule::Convolution(inputSlice, deltaSlice,
output, 1, 1);
gradientTemp.slice(outMapIdx) += output;
Rotate180(output, rotatedOutput);
gradientTemp.slice(outMapIdx) += rotatedOutput;
}
gradient.submat(weight.n_elem + (outMap % outSize), 0, weight.n_elem +
@@ -282,7 +342,12 @@ void TransposedConvolution<
ar & BOOST_SERIALIZATION_NVP(outputHeight);
if (Archive::is_loading::value)
{
weights.set_size((outSize * inSize * kW * kH) + outSize, 1);
aW = (outputWidth + kW - 2 * padW - 2) % dW;
aH = (outputHeight + kH - 2 * padH - 2) % dH;
}
}
} // namespace ann
+322
View File
@@ -0,0 +1,322 @@
/**
* @file layer_names.hpp
* @author Sreenik Seal
*
* Implementation of a class that converts a given ann layer to string format.
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <mlpack/core.hpp>
#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/layer/layer_types.hpp>
#include <boost/variant/static_visitor.hpp>
#include <string>
using namespace mlpack::ann;
/**
* Implementation of a class that returns the string representation of the
* name of the given layer.
*/
class LayerNameVisitor : public boost::static_visitor<std::string>
{
public:
//! Create the LayerNameVisitor object.
LayerNameVisitor()
{
}
/*
* Return the name of the given layer of type AtrousConvolution as a string.
*
* @param Given layer of type AtrousConvolution.
* @return The string representation of the layer.
*/
std::string LayerString(AtrousConvolution<>* /*layer*/) const
{
return "atrousconvolution";
}
/*
* Return the name of the given layer of type AlphaDropout as a string.
*
* @param Given layer of type AlphaDropout.
* @return The string representation of the layer.
*/
std::string LayerString(AlphaDropout<>* /*layer*/) const
{
return "alphadropout";
}
/*
* Return the name of the given layer of type BatchNorm as a string.
*
* @param Given layer of type BatchNorm.
* @return The string representation of the layer.
*/
std::string LayerString(BatchNorm<>* /*layer*/) const
{
return "batchnorm";
}
/*
* Return the name of the given layer of type Constant as a string.
*
* @param Given layer of type Constant.
* @return The string representation of the layer.
*/
std::string LayerString(Constant<>* /*layer*/) const
{
return "constant";
}
/*
* Return the name of the given layer of type Convolution as a string.
*
* @param Given layer of type Convolution.
* @return The string representation of the layer.
*/
std::string LayerString(Convolution<>* /*layer*/) const
{
return "convolution";
}
/*
* Return the name of the given layer of type DropConnect as a string.
*
* @param Given layer of type DropConnect.
* @return The string representation of the layer.
*/
std::string LayerString(DropConnect<>* /*layer*/) const
{
return "dropconnect";
}
/*
* Return the name of the given layer of type Dropout as a string.
*
* @param Given layer of type Dropout.
* @return The string representation of the layer.
*/
std::string LayerString(Dropout<>* /*layer*/) const
{
return "dropout";
}
/*
* Return the name of the given layer of type FlexibleReLU as a string.
*
* @param Given layer of type FlexibleReLU.
* @return The string representation of the layer.
*/
std::string LayerString(FlexibleReLU<>* /*layer*/) const
{
return "flexiblerelu";
}
/*
* Return the name of the given layer of type LayerNorm as a string.
*
* @param Given layer of type LayerNorm.
* @return The string representation of the layer.
*/
std::string LayerString(LayerNorm<>* /*layer*/) const
{
return "layernorm";
}
/*
* Return the name of the given layer of type Linear as a string.
*
* @param Given layer of type Linear.
* @return The string representation of the layer.
*/
std::string LayerString(Linear<>* /*layer*/) const
{
return "linear";
}
/*
* Return the name of the given layer of type LinearNoBias as a string.
*
* @param Given layer of type LinearNoBias.
* @return The string representation of the layer.
*/
std::string LayerString(LinearNoBias<>* /*layer*/) const
{
return "linearnobias";
}
/*
* Return the name of the given layer of type MaxPooling as a string.
*
* @param Given layer of type MaxPooling.
* @return The string representation of the layer.
*/
std::string LayerString(MaxPooling<>* /*layer*/) const
{
return "maxpooling";
}
/*
* Return the name of the given layer of type MeanPooling as a string.
*
* @param Given layer of type MeanPooling.
* @return The string representation of the layer.
*/
std::string LayerString(MeanPooling<>* /*layer*/) const
{
return "meanpooling";
}
/*
* Return the name of the given layer of type MultiplyConstant as a string.
*
* @param Given layer of type MultiplyConstant.
* @return The string representation of the layer.
*/
std::string LayerString(MultiplyConstant<>* /*layer*/) const
{
return "multiplyconstant";
}
/*
* Return the name of the given layer of type ReLULayer as a string.
*
* @param Given layer of type ReLULayer.
* @return The string representation of the layer.
*/
std::string LayerString(ReLULayer<>* /*layer*/) const
{
return "relu";
}
/*
* Return the name of the given layer of type TransposedConvolution as a
* string.
*
* @param Given layer of type TransposedConvolution.
* @return The string representation of the layer.
*/
std::string LayerString(TransposedConvolution<>* /*layer*/) const
{
return "transposedconvolution";
}
/*
* Return the name of the given layer of type IdentityLayer as a string.
*
* @param Given layer of type IdentityLayer.
* @return The string representation of the layer.
*/
std::string LayerString(IdentityLayer<>* /*layer*/) const
{
return "identity";
}
/*
* Return the name of the given layer of type TanHLayer as a string.
*
* @param Given layer of type TanHLayer.
* @return The string representation of the layer.
*/
std::string LayerString(TanHLayer<>* /*layer*/) const
{
return "tanh";
}
/*
* Return the name of the given layer of type ELU as a string.
*
* @param Given layer of type ELU.
* @return The string representation of the layer.
*/
std::string LayerString(ELU<>* /*layer*/) const
{
return "elu";
}
/*
* Return the name of the given layer of type HardTanH as a string.
*
* @param Given layer of type HardTanH.
* @return The string representation of the layer.
*/
std::string LayerString(HardTanH<>* /*layer*/) const
{
return "hardtanh";
}
/*
* Return the name of the given layer of type LeakyReLU as a string.
*
* @param Given layer of type LeakyReLU.
* @return The string representation of the layer.
*/
std::string LayerString(LeakyReLU<>* /*layer*/) const
{
return "leakyrelu";
}
/*
* Return the name of the given layer of type PReLU as a string.
*
* @param Given layer of type PReLU.
* @return The string representation of the layer.
*/
std::string LayerString(PReLU<>* /*layer*/) const
{
return "prelu";
}
/*
* Return the name of the given layer of type SigmoidLayer as a string.
*
* @param Given layer of type SigmoidLayer.
* @return The string representation of the layer.
*/
std::string LayerString(SigmoidLayer<>* /*layer*/) const
{
return "sigmoid";
}
/*
* Return the name of the given layer of type LogSoftMax as a string.
*
* @param Given layer of type LogSoftMax.
* @return The string representation of the layer.
*/
std::string LayerString(LogSoftMax<>* /*layer*/) const
{
return "logsoftmax";
}
/*
* Return the name of the layer of specified type as a string.
*
* @param Given layer of any type.
* @return A string declaring that the layer is unsupported.
*/
template<typename T>
std::string LayerString(T* /*layer*/) const
{
return "unsupported";
}
//! Overload function call.
std::string operator()(MoreTypes layer) const
{
return layer.apply_visitor(*this);
}
//! Overload function call.
template<typename LayerType>
std::string operator()(LayerType* layer) const
{
return LayerString(layer);
}
};
+11 -4
View File
@@ -91,15 +91,18 @@ class RNN
* at time slice k.
*
* @tparam OptimizerType Type of optimizer to use to train the model.
* @tparam CallbackTypes Types of Callback Functions.
* @param predictors Input training variables.
* @param responses Outputs results from input training variables.
* @param optimizer Instantiated optimizer used to train the model.
* @param callbacks Callback functions.
* @return The final objective of the trained model (NaN or Inf on error).
*/
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double Train(arma::cube predictors,
arma::cube responses,
OptimizerType& optimizer);
OptimizerType& optimizer,
CallbackTypes&&... callbacks);
/**
* Train the recurrent neural network on the given input data. By default, the
@@ -121,12 +124,16 @@ class RNN
* at time slice k.
*
* @tparam OptimizerType Type of optimizer to use to train the model.
* @tparam CallbackTypes Types of Callback Functions.
* @param predictors Input training variables.
* @param responses Outputs results from input training variables.
* @param callbacks Callback functions.
* @return The final objective of the trained model (NaN or Inf on error).
*/
template<typename OptimizerType = ens::StandardSGD>
double Train(arma::cube predictors, arma::cube responses);
template<typename OptimizerType = ens::StandardSGD, typename... CallbackTypes>
double Train(arma::cube predictors,
arma::cube responses,
CallbackTypes&&... callbacks);
/**
* Predict the responses to a given set of predictors. The responses will
+8 -6
View File
@@ -63,11 +63,12 @@ RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::~RNN()
template<typename OutputLayerType, typename InitializationRuleType,
typename... CustomLayers>
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
arma::cube predictors,
arma::cube responses,
OptimizerType& optimizer)
OptimizerType& optimizer,
CallbackTypes&&... callbacks)
{
numFunctions = responses.n_cols;
@@ -84,7 +85,7 @@ double RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
// Train the model.
Timer::Start("rnn_optimization");
const double out = optimizer.Optimize(*this, parameter);
const double out = optimizer.Optimize(*this, parameter, callbacks...);
Timer::Stop("rnn_optimization");
Log::Info << "RNN::RNN(): final objective of trained model is " << out
@@ -105,10 +106,11 @@ void RNN<OutputLayerType, InitializationRuleType,
template<typename OutputLayerType, typename InitializationRuleType,
typename... CustomLayers>
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
arma::cube predictors,
arma::cube responses)
arma::cube responses,
CallbackTypes&&... callbacks)
{
numFunctions = responses.n_cols;
@@ -127,7 +129,7 @@ double RNN<OutputLayerType, InitializationRuleType, CustomLayers...>::Train(
// Train the model.
Timer::Start("rnn_optimization");
const double out = optimizer.Optimize(*this, parameter);
const double out = optimizer.Optimize(*this, parameter, callbacks...);
Timer::Stop("rnn_optimization");
Log::Info << "RNN::RNN(): final objective of trained model is " << out
@@ -16,3 +16,7 @@ endforeach()
# append sources (with directory name) to list of all mlpack sources (used at
# the parent scope)
set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE)
add_cli_executable(linear_svm)
add_python_binding(linear_svm)
add_markdown_docs(linear_svm "cli;python" "classification")
@@ -119,6 +119,20 @@ class LinearSVM
const double lambda = 0.0001,
const double delta = 1.0,
const bool fitIntercept = false);
/**
* Initialize the Linear SVM without performing training. Default
* value of lambda is 0.0001. Be sure to use Train() before calling
* Classify() or ComputeAccuracy(), otherwise the results may be meaningless.
*
* @param numClasses Number of classes for classification.
* @param lambda L2-regularization constant.
* @paran delta Margin of difference between correct class and other classes.
* @param fitIntercept add intercept term or not.
*/
LinearSVM(const size_t numClasses = 0,
const double lambda = 0.0001,
const double delta = 1.0,
const bool fitIntercept = false);
/**
* Classify the given points, returning the predicted labels for each point.
@@ -207,6 +221,14 @@ class LinearSVM
//! Gets the regularization parameter.
double Lambda() const { return lambda; }
//! Sets the margin between the correct class and all other classes.
double& Delta() { return delta; }
//! Gets the margin between the correct class and all other classes.
double Delta() const { return delta; }
//! Sets the intercept term flag.
bool& FitIntercept() { return fitIntercept; }
//! Set the model parameters.
arma::mat& Parameters() { return parameters; }
//! Get the model parameters.
@@ -320,7 +320,7 @@ void LinearSVMFunction<MatType>::Gradient(
{
scores = parameters.rows(0, dataset.n_rows - 1).t()
* dataset.cols(firstId, lastId)
+ arma::repmat(parameters.row(dataset.n_rows).t(), 1, dataset.n_cols);
+ arma::repmat(parameters.row(dataset.n_rows).t(), 1, batchSize);
}
arma::mat margin = scores - (arma::repmat(arma::ones(numClasses).t()
@@ -52,6 +52,20 @@ LinearSVM<MatType>::LinearSVM(
numClasses, fitIntercept);
}
template <typename MatType>
LinearSVM<MatType>::LinearSVM(
const size_t numClasses,
const double lambda,
const double delta,
const bool fitIntercept) :
numClasses(numClasses),
lambda(lambda),
delta(delta),
fitIntercept(fitIntercept)
{
// No training to do here.
}
template <typename MatType>
template <typename OptimizerType>
double LinearSVM<MatType>::Train(
@@ -60,6 +74,11 @@ double LinearSVM<MatType>::Train(
const size_t numClasses,
OptimizerType optimizer)
{
if (numClasses <= 1)
{
throw std::invalid_argument("LinearSVM dataset has 0 number of classes!");
}
LinearSVMFunction<MatType> svm(data, labels, numClasses, lambda, delta,
fitIntercept);
if (parameters.is_empty())
@@ -0,0 +1,449 @@
/**
* @file linear_svm_main.cpp
* @author Yashwant Singh Parihar
*
* Main executable for linear svm.
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <mlpack/prereqs.hpp>
#include <mlpack/core/util/cli.hpp>
#include <mlpack/core.hpp>
#include <mlpack/core/util/mlpack_main.hpp>
#include "linear_svm.hpp"
#include <ensmallen.hpp>
using namespace std;
using namespace mlpack;
using namespace mlpack::svm;
using namespace mlpack::util;
PROGRAM_INFO("Linear SVM is an L2-regularized support vector machine.",
// Short description.
"An implementation of linear SVM for multiclass classification. "
"Given labeled data, a model can be trained and saved for "
"future use; or, a pre-trained model can be used to classify new points.",
// Long description.
"An implementation of linear SVMs that uses either L-BFGS or parallel SGD"
" (stochastic gradient descent) to train the model."
"\n\n"
"This program allows loading a linear SVM model (via the " +
PRINT_PARAM_STRING("input_model") + " parameter) "
"or training a linear SVM model given training data (specified "
"with the " + PRINT_PARAM_STRING("training") + " parameter), or both "
"those things at once. In addition, this program allows classification on "
"a test dataset (specified with the " + PRINT_PARAM_STRING("test") + " "
"parameter) and the classification results may be saved with the " +
PRINT_PARAM_STRING("predictions") + " output parameter."
" The trained linear SVM model may be saved using the " +
PRINT_PARAM_STRING("output_model") + " output parameter."
"\n\n"
"The training data, if specified, may have class labels as its last "
"dimension. Alternately, the " + PRINT_PARAM_STRING("labels") + " "
"parameter may be used to specify a separate vector of labels."
"\n\n"
"When a model is being trained, there are many options. L2 regularization "
"(to prevent overfitting) can be specified with the " +
PRINT_PARAM_STRING("lambda") + " option, and the number of classes can be "
"manually specified with the " + PRINT_PARAM_STRING("num_classes") +
"and if an intercept term is not desired in the model, the " +
PRINT_PARAM_STRING("no_intercept") + " parameter can be specified."
"Margin of difference between correct class and other classes can "
"be specified with the " + PRINT_PARAM_STRING("delta") + " option."
"The optimizer used to train the model can be specified with the " +
PRINT_PARAM_STRING("optimizer") + " parameter. Available options are "
"'psgd' (parallel stochastic gradient descent) and 'lbfgs' (the L-BFGS"
" optimizer). There are also various parameters for the optimizer; the " +
PRINT_PARAM_STRING("max_iterations") + " parameter specifies the maximum "
"number of allowed iterations, and the " +
PRINT_PARAM_STRING("tolerance") + " parameter specifies the tolerance for "
"convergence. For the parallel SGD optimizer, the " +
PRINT_PARAM_STRING("step_size") + " parameter controls the step size taken "
"at each iteration by the optimizer and the maximum number of epochs "
"(specified with " + PRINT_PARAM_STRING("epochs") + "). If the "
"objective function for your data is oscillating between Inf and 0, the "
"step size is probably too large. There are more parameters for the "
"optimizers, but the C++ interface must be used to access these."
"\n\n"
"Optionally, the model can be used to predict the labels for another "
"matrix of data points, if " + PRINT_PARAM_STRING("test") + " is "
"specified. The " + PRINT_PARAM_STRING("test") + " parameter can be "
"specified without the " + PRINT_PARAM_STRING("training") + " parameter, "
"so long as an existing linear SVM model is given with the " +
PRINT_PARAM_STRING("input_model") + " parameter. The output predictions "
"from the linear SVM model may be saved with the " +
PRINT_PARAM_STRING("predictions") + " parameter." +
"\n\n"
"As an example, to train a LinaerSVM on the data '" +
PRINT_DATASET("data") + "' with labels '" + PRINT_DATASET("labels") + "' "
"with L2 regularization of 0.1, saving the model to '" +
PRINT_MODEL("lsvm_model") + "', the following command may be used:"
"\n\n" +
PRINT_CALL("linear_svm", "training", "data", "labels", "labels",
"lambda", 0.1, "delta", 1.0, "num_classes", 0,
"output_model", "lsvm_model") +
"\n\n"
"Then, to use that model to predict classes for the dataset '" +
PRINT_DATASET("test") + "', storing the output predictions in '" +
PRINT_DATASET("predictions") + "', the following command may be used: "
"\n\n" +
PRINT_CALL("linear_svm", "input_model", "lsvm_model", "test", "test",
"predictions", "predictions"),
SEE_ALSO("@random_forest", "#random_forest"),
SEE_ALSO("@logistic_regression", "#logistic_regression"),
SEE_ALSO("LinearSVM on Wikipedia",
"https://en.wikipedia.org/wiki/Support-vector_machine"),
SEE_ALSO("mlpack::svm::LinearSVM C++ class documentation",
"@doxygen/classmlpack_1_1svm_1_1LinearSVM.html"));
// Training parameters.
PARAM_MATRIX_IN("training", "A matrix containing the training set (the matrix "
"of predictors, X).", "t");
PARAM_UROW_IN("labels", "A matrix containing labels (0 or 1) for the points "
"in the training set (y).", "l");
// Optimizer parameters.
PARAM_DOUBLE_IN("lambda", "L2-regularization parameter for training.", "r",
0.0001);
PARAM_DOUBLE_IN("delta", "Margin of difference between correct class and other "
"classes.", "d", 1.0);
PARAM_INT_IN("num_classes", "Number of classes for classification; if "
"unspecified (or 0), the number of classes found in the labels will be "
"used.", "c", 0);
PARAM_FLAG("no_intercept", "Do not add the intercept term to the model.", "N");
PARAM_STRING_IN("optimizer", "Optimizer to use for training ('lbfgs' or "
"'psgd').", "O", "lbfgs");
PARAM_DOUBLE_IN("tolerance", "Convergence tolerance for optimizer.", "e",
1e-10);
PARAM_INT_IN("max_iterations", "Maximum iterations for optimizer (0 indicates "
"no limit).", "n", 10000);
PARAM_DOUBLE_IN("step_size", "Step size for parallel SGD optimizer.",
"a", 0.01);
PARAM_FLAG("shuffle", "Don't shuffle the order in which data points are "
"visited for parallel SGD.", "S");
PARAM_INT_IN("epochs", "Maximum number of full epochs over dataset for "
"psgd", "E", 50);
PARAM_INT_IN("seed", "Random seed. If 0, 'std::time(NULL)' is used.", "s", 0);
class LinearSVMModel
{
public:
arma::Col<size_t> mappings;
LinearSVM<> svm;
template<typename Archive>
void serialize(Archive& ar, const unsigned int /* version */)
{
ar & BOOST_SERIALIZATION_NVP(mappings);
ar & BOOST_SERIALIZATION_NVP(svm);
}
};
// Model loading/saving.
PARAM_MODEL_IN(LinearSVMModel, "input_model", "Existing model "
"(parameters).", "m");
PARAM_MODEL_OUT(LinearSVMModel, "output_model", "Output for trained "
"linear svm model.", "M");
// Testing.
PARAM_MATRIX_IN("test", "Matrix containing test dataset.", "T");
PARAM_UROW_IN("test_labels", "Matrix containing test labels.", "L");
PARAM_UROW_OUT("predictions", "If test data is specified, this matrix is where "
"the predictions for the test set will be saved.", "P");
PARAM_MATRIX_OUT("probabilities", "If test data is specified, this "
"matrix is where the class probabilities for the test set will be saved.",
"p");
static void mlpackMain()
{
if (CLI::GetParam<int>("seed") != 0)
math::RandomSeed((size_t) CLI::GetParam<int>("seed"));
else
math::RandomSeed((size_t) std::time(NULL));
// Collect command-line options.
const double lambda = CLI::GetParam<double>("lambda");
const double delta = CLI::GetParam<double>("delta");
const string optimizerType = CLI::GetParam<string>("optimizer");
const double tolerance = CLI::GetParam<double>("tolerance");
const bool intercept = CLI::HasParam("no_intercept");
const size_t epochs = (size_t) CLI::GetParam<int>("epochs");
const size_t maxIterations = (size_t) CLI::GetParam<int>("max_iterations");
// One of training and input_model must be specified.
RequireAtLeastOnePassed({ "training", "input_model" }, true);
// If no output file is given, the user should know that the model will not be
// saved, but only if a model is being trained.
RequireAtLeastOnePassed({ "output_model", "predictions", "probabilities"},
false, "no output will be saved");
ReportIgnoredParam({{ "test", false }}, "predictions");
ReportIgnoredParam({{ "test", false }}, "probabilities");
ReportIgnoredParam({{ "test", false }}, "test_labels");
// Max Iterations needs to be positive.
RequireParamValue<int>("max_iterations", [](int x) { return x >= 0; },
true, "max_iterations must be non-negative");
// Tolerance needs to be positive.
RequireParamValue<double>("tolerance", [](double x) { return x >= 0.0; },
true, "tolerance must be non-negative");
// Optimizer has to be L-BFGS or parallel SGD.
RequireParamInSet<string>("optimizer", { "lbfgs", "psgd" },
true, "unknown optimizer");
// Epochs needs to be non-negative.
RequireParamValue<int>("epochs", [](int x) { return x >= 0; }, true,
"maximum number of epochs must be non-negative");
if (optimizerType != "psgd")
{
if (CLI::HasParam("step_size"))
{
Log::Warn << PRINT_PARAM_STRING("step_size") << " ignored because "
<< "optimizer type is not 'psgd'." << std::endl;
}
if (CLI::HasParam("shuffle"))
{
Log::Warn << PRINT_PARAM_STRING("shuffle") << " ignored because "
<< "optimizer type is not 'psgd'." << std::endl;
}
if (CLI::HasParam("epochs"))
{
Log::Warn << PRINT_PARAM_STRING("epochs") << " ignored because "
<< "optimizer type is not 'psgd'." << std::endl;
}
}
if (optimizerType != "lbfgs")
{
if (CLI::HasParam("max_iterations"))
{
Log::Warn << PRINT_PARAM_STRING("max_iterations") << " ignored because "
<< "optimizer type is not 'lbfgs'." << std::endl;
}
}
// Step Size must be positive.
RequireParamValue<double>("step_size", [](double x) { return x > 0.0; },
true, "step size must be positive");
// Lambda must be positive.
RequireParamValue<double>("lambda", [](double x) { return x >= 0.0; },
true, "lambda must be non-negative");
// Number of Classes must be Non-Negative
RequireParamValue<int>("num_classes", [](int x) { return x >= 0; },
true, "number of classes must be greater than or "
"equal to 0 (equal to 0 in case of unspecified.)");
// Delta must be positive.
RequireParamValue<double>("delta", [](double x) { return x >= 0.0; }, true,
"delta must be non-negative");
// Delta must be positive.
RequireParamValue<int>("epochs", [](int x) { return x > 0; }, true,
"epochs must be non-negative");
// These are the matrices we might use.
arma::mat trainingSet;
arma::Row<size_t> labels;
arma::Row<size_t> rawLabels;
arma::mat testSet;
arma::Row<size_t> predictedLabels;
size_t numClasses;
// Load data matrix.
if (CLI::HasParam("training"))
trainingSet = std::move(CLI::GetParam<arma::mat>("training"));
// Check if the labels are in a separate file.
if (CLI::HasParam("training") && CLI::HasParam("labels"))
{
rawLabels = std::move(CLI::GetParam<arma::Row<size_t>>("labels"));
if (trainingSet.n_cols != rawLabels.n_cols)
{
Log::Fatal << "The labels must have the same number of points as the "
<< "training dataset." << endl;
}
}
else if (CLI::HasParam("training"))
{
// Checking the size of training data if no labels are passed.
if (trainingSet.n_rows < 2)
{
Log::Fatal << "Can't get labels from training data since it has less "
<< "than 2 rows." << endl;
}
// The initial predictors for y, Nx1.
rawLabels = arma::conv_to<arma::Row<size_t>>::from(
trainingSet.row(trainingSet.n_rows - 1));
trainingSet.shed_row(trainingSet.n_rows - 1);
}
// Load the model, if necessary.
LinearSVMModel* model;
if (CLI::HasParam("input_model"))
{
model = CLI::GetParam<LinearSVMModel*>("input_model");
}
else
{
model = new LinearSVMModel();
}
// Now, do the training.
if (CLI::HasParam("training"))
{
data::NormalizeLabels(rawLabels, labels, model->mappings);
numClasses = CLI::GetParam<int>("num_classes") == 0 ?
model->mappings.n_elem : CLI::GetParam<int>("num_classes");
model->svm.Lambda() = lambda;
model->svm.Delta() = delta;
model->svm.NumClasses() = numClasses;
model->svm.FitIntercept() = intercept;
if (optimizerType == "lbfgs")
{
ens::L_BFGS lbfgsOpt;
lbfgsOpt.MaxIterations() = maxIterations;
lbfgsOpt.MinGradientNorm() = tolerance;
Log::Info << "Training model with L-BFGS optimizer." << endl;
// This will train the model.
model->svm.Train(trainingSet, labels, numClasses, lbfgsOpt);
}
else if (optimizerType == "psgd")
{
const double stepSize = CLI::GetParam<double>("step_size");
const bool shuffle = !CLI::HasParam("shuffle");
const size_t maxIt = epochs * trainingSet.n_cols;
ens::ConstantStep decayPolicy(stepSize);
#ifdef HAS_OPENMP
size_t threads = omp_get_max_threads();
#else
size_t threads = 1;
Log::Warn << "Using parallel SGD, but OpenMP support is "
<< "not available!" << endl;
#endif
ens::ParallelSGD<ens::ConstantStep> psgdOpt(maxIt, std::ceil(
(float) trainingSet.n_cols / threads), tolerance, shuffle,
decayPolicy);
Log::Info << "Training model with ParallelSGD optimizer." << endl;
// This will train the model.
model->svm.Train(trainingSet, labels, numClasses, psgdOpt);
}
}
if (CLI::HasParam("test"))
{
if (!CLI::HasParam("training"))
{
numClasses = model->svm.NumClasses();
}
// Get the test dataset, and get predictions.
testSet = std::move(CLI::GetParam<arma::mat>("test"));
arma::Row<size_t> predictions;
size_t trainingDimensionality;
// Set the dimensionality according to fitintercept.
if (intercept)
trainingDimensionality = model->svm.Parameters().n_rows - 1;
else
trainingDimensionality = model->svm.Parameters().n_rows;
// Checking the dimensionality of the test data.
if (testSet.n_rows != trainingDimensionality)
{
Log::Fatal << "Test data dimensionality (" << testSet.n_rows << ") must "
<< "be the same as the dimensionality of the training data ("
<< trainingDimensionality << ")!" << endl;
}
// Save class probabilities, if desired.
if (CLI::HasParam("probabilities"))
{
Log::Info << "Calculating class probabilities of points in "
<< CLI::GetPrintableParam<arma::mat>("test") << "." << endl;
arma::mat probabilities;
model->svm.Classify(testSet, probabilities);
CLI::GetParam<arma::mat>("probabilities") = std::move(probabilities);
}
model->svm.Classify(testSet, predictedLabels);
data::RevertLabels(predictedLabels, model->mappings, predictions);
// Calculate accuracy, if desired.
if (CLI::HasParam("test_labels"))
{
arma::Row<size_t> testLabels;
arma::Row<size_t> testRawLabels =
std::move(CLI::GetParam<arma::Row<size_t>>("test_labels"));
data::NormalizeLabels(testRawLabels, testLabels, model->mappings);
if (testSet.n_cols != testLabels.n_elem)
{
Log::Fatal << "Test data given with " << PRINT_PARAM_STRING("test")
<< " has " << testSet.n_cols << " points, but labels in "
<< PRINT_PARAM_STRING("test_labels") << " have "
<< testLabels.n_elem << " labels!" << endl;
}
numClasses = CLI::GetParam<int>("num_classes") == 0 ?
model->mappings.n_elem : CLI::GetParam<int>("num_classes");
arma::Col<size_t> correctClassCounts;
arma::Col<size_t> labelSize;
correctClassCounts.zeros(numClasses);
labelSize.zeros(numClasses);
for (arma::uword i = 0; i != predictions.n_elem; ++i)
{
if (predictions(i) == testLabels(i))
{
++correctClassCounts[testLabels(i)];
}
++labelSize[testLabels(i)];
}
size_t totalCorrectClass = 0;
for (size_t i = 0; i != correctClassCounts.size(); ++i)
{
Log::Info << "Accuracy for points with label " << i << " is "
<< (correctClassCounts[i] / static_cast<double>(labelSize[i]))
<< " (" << correctClassCounts[i] << " of " << labelSize[i] << ")."
<< endl;
totalCorrectClass += correctClassCounts[i];
}
Log::Info << "Total accuracy for all points is "
<< (totalCorrectClass) / static_cast<double>(predictions.n_elem)
<< " (" << totalCorrectClass << " of " << predictions.n_elem << ")."
<< endl;
}
// Save predictions, if desired.
if (CLI::HasParam("predictions"))
{
Log::Info << "Predicting classes of points in '"
<< CLI::GetPrintableParam<arma::mat>("test") << "'." << endl;
CLI::GetParam<arma::Row<size_t>>("predictions") = std::move(predictions);
}
}
CLI::GetParam<LinearSVMModel*>("output_model") = model;
}
@@ -123,13 +123,16 @@ class LogisticRegression
* parameters vector directly with Parameters() and modify it as desired.
*
* @tparam OptimizerType Type of optimizer to use to train the model.
* @tparam CallbackTypes Types of Callback Functions.
* @param predictors Input training variables.
* @param responses Outputs results from input training variables.
* @param callbacks Callback Functions.
* @return The final objective of the trained model (NaN or Inf on error)
*/
template<typename OptimizerType = ens::L_BFGS>
template<typename OptimizerType = ens::L_BFGS, typename... CallbackTypes>
double Train(const MatType& predictors,
const arma::Row<size_t>& responses);
const arma::Row<size_t>& responses,
CallbackTypes&&... callbacks);
/**
* Train the LogisticRegression model with the given instantiated optimizer.
@@ -143,15 +146,19 @@ class LogisticRegression
* optimizer.Function().GetInitialPoint() to the current parameters vector,
* accessible via Parameters().
*
* @tparam OptimizerType Type of optimizer to use to train the model.
* @tparam CallbackTypes Types of Callback Functions.
* @param predictors Input training variables.
* @param responses Outputs results from input training variables.
* @param optimizer Instantiated optimizer with instantiated error function.
* @param callbacks Callback Functions.
* @return The final objective of the trained model (NaN or Inf on error)
*/
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double Train(const MatType& predictors,
const arma::Row<size_t>& responses,
OptimizerType& optimizer);
OptimizerType& optimizer,
CallbackTypes&&... callbacks);
//! Return the parameters (the b vector).
const arma::rowvec& Parameters() const { return parameters; }
@@ -67,20 +67,23 @@ LogisticRegression<MatType>::LogisticRegression(
}
template<typename MatType>
template<typename OptimizerType>
double LogisticRegression<MatType>::Train(const MatType& predictors,
const arma::Row<size_t>& responses)
template<typename OptimizerType, typename... CallbackTypes>
double LogisticRegression<MatType>::Train(
const MatType& predictors,
const arma::Row<size_t>& responses,
CallbackTypes&&... callbacks)
{
OptimizerType optimizer;
return Train(predictors, responses, optimizer);
return Train(predictors, responses, optimizer, callbacks...);
}
template<typename MatType>
template<typename OptimizerType>
template<typename OptimizerType, typename... CallbackTypes>
double LogisticRegression<MatType>::Train(
const MatType& predictors,
const arma::Row<size_t>& responses,
OptimizerType& optimizer)
OptimizerType& optimizer,
CallbackTypes&&... callbacks)
{
LogisticRegressionFunction<MatType> errorFunction(predictors,
responses,
@@ -88,7 +91,8 @@ double LogisticRegression<MatType>::Train(
errorFunction.InitialPoint() = parameters;
Timer::Start("logistic_regression_optimization");
const double out = optimizer.Optimize(errorFunction, parameters);
const double out = optimizer.Optimize(errorFunction, parameters,
callbacks...);
Timer::Stop("logistic_regression_optimization");
Log::Info << "LogisticRegression::LogisticRegression(): final objective of "
+3
View File
@@ -16,6 +16,7 @@ add_executable(mlpack_test
bias_svd_test.cpp
binarize_test.cpp
block_krylov_svd_test.cpp
callback_test.cpp
cf_test.cpp
cli_binding_test.cpp
cli_test.cpp
@@ -52,6 +53,7 @@ add_executable(mlpack_test
krann_search_test.cpp
ksinit_test.cpp
lars_test.cpp
layer_names_test.cpp
lin_alg_test.cpp
linear_regression_test.cpp
linear_svm_test.cpp
@@ -134,6 +136,7 @@ add_executable(mlpack_test
main_tests/kfn_test.cpp
main_tests/knn_test.cpp
main_tests/linear_regression_test.cpp
main_tests/linear_svm_test.cpp
main_tests/logistic_regression_test.cpp
main_tests/local_coordinate_coding_test.cpp
main_tests/lmnn_test.cpp
+45 -39
View File
@@ -1726,8 +1726,8 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
{
arma::mat output, input, delta;
TransposedConvolution<> module1(1, 1, 3, 3, 1, 1, 0, 0, 4, 4);
// Test the Forward function.
TransposedConvolution<> module1(1, 1, 3, 3, 1, 1, 0, 0, 4, 4, 6, 6);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 15, 16);
module1.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros);
module1.Parameters()(0) = 1.0;
@@ -1737,11 +1737,12 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
// Value calculated using tensorflow.nn.conv2d_transpose()
BOOST_REQUIRE_EQUAL(arma::accu(output), 360.0);
// Test the Backward function.
// Test the backward function.
module1.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 720);
// Value calculated using tensorflow.nn.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 720.0);
TransposedConvolution<> module2(1, 1, 4, 4, 1, 1, 2, 2, 5, 5);
TransposedConvolution<> module2(1, 1, 4, 4, 1, 1, 1, 1, 5, 5, 6, 6);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 24, 25);
module2.Parameters() = arma::mat(16 + 1, 1, arma::fill::zeros);
@@ -1753,14 +1754,15 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
module2.Parameters()(15) = 2.0;
module2.Reset();
module2.Forward(std::move(input), std::move(output));
// Value calculated using tensorflow.nn.conv2d_transpose()
BOOST_REQUIRE_EQUAL(arma::accu(output), 2100.0);
// Value calculated using torch.nn.functional.conv_transpose2d()
BOOST_REQUIRE_EQUAL(arma::accu(output), 1512.0);
// Test the backward function.
module2.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 7740);
// Value calculated using torch.nn.functional.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 6504.0);
TransposedConvolution<> module3(1, 1, 3, 3, 1, 1, 1, 1, 5, 5);
TransposedConvolution<> module3(1, 1, 3, 3, 1, 1, 1, 1, 5, 5, 5, 5);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 24, 25);
module3.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros);
@@ -1770,14 +1772,15 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
module3.Parameters()(8) = 1.0;
module3.Reset();
module3.Forward(std::move(input), std::move(output));
// Value calculated using tensorflow.nn.conv2d_transpose()
BOOST_REQUIRE_EQUAL(arma::accu(output), 3000.0);
// Value calculated using torch.nn.functional.conv_transpose2d()
BOOST_REQUIRE_EQUAL(arma::accu(output), 2370.0);
// Test the backward function.
module3.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 21480);
// Value calculated using torch.nn.functional.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 19154.0);
TransposedConvolution<> module4(1, 1, 3, 3, 1, 1, 2, 2, 5, 5);
TransposedConvolution<> module4(1, 1, 3, 3, 1, 1, 0, 0, 5, 5, 7, 7);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 24, 25);
module4.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros);
@@ -1787,33 +1790,35 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
module4.Parameters()(8) = 8.0;
module4.Reset();
module4.Forward(std::move(input), std::move(output));
// Value calculated using tensorflow.nn.conv2d_transpose()
// Value calculated using torch.nn.functional.conv_transpose2d()
BOOST_REQUIRE_EQUAL(arma::accu(output), 6000.0);
// Test the backward function.
module4.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 86208);
// Value calculated using torch.nn.functional.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 86208.0);
TransposedConvolution<> module5(1, 1, 3, 3, 2, 2, 0, 0, 5, 5);
TransposedConvolution<> module5(1, 1, 3, 3, 2, 2, 0, 0, 2, 2, 5, 5);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 24, 25);
module5.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros);
input = arma::linspace<arma::colvec>(0, 3, 4);
module5.Parameters() = arma::mat(25 + 1, 1, arma::fill::zeros);
module5.Parameters()(2) = 8.0;
module5.Parameters()(4) = 6.0;
module5.Parameters()(6) = 4.0;
module5.Parameters()(8) = 2.0;
module5.Reset();
module5.Forward(std::move(input), std::move(output));
// Value calculated using tensorflow.nn.conv2d_transpose()
BOOST_REQUIRE_EQUAL(arma::accu(output), 6000.0);
// Value calculated using torch.nn.functional.conv_transpose2d()
BOOST_REQUIRE_EQUAL(arma::accu(output), 120.0);
// Test the backward function.
module5.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 83808);
// Value calculated using torch.nn.functional.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 960.0);
TransposedConvolution<> module6(1, 1, 3, 3, 2, 2, 1, 1, 5, 5);
TransposedConvolution<> module6(1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 5, 5);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 24, 25);
input = arma::linspace<arma::colvec>(0, 8, 9);
module6.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros);
module6.Parameters()(0) = 8.0;
module6.Parameters()(3) = 6.0;
@@ -1821,16 +1826,17 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
module6.Parameters()(8) = 4.0;
module6.Reset();
module6.Forward(std::move(input), std::move(output));
// Value calculated using tensorflow.nn.conv2d_transpose()
BOOST_REQUIRE_EQUAL(arma::accu(output), 6000.0);
// Value calculated using torch.nn.functional.conv_transpose2d()
BOOST_REQUIRE_EQUAL(arma::accu(output), 410.0);
// Test the backward function.
module6.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 87264);
// Value calculated using torch.nn.functional.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 4444.0);
TransposedConvolution<> module7(1, 1, 3, 3, 2, 2, 1, 1, 6, 6);
TransposedConvolution<> module7(1, 1, 3, 3, 2, 2, 1, 1, 3, 3, 6, 6);
// Test the forward function.
input = arma::linspace<arma::colvec>(0, 35, 36);
input = arma::linspace<arma::colvec>(0, 8, 9);
module7.Parameters() = arma::mat(9 + 1, 1, arma::fill::zeros);
module7.Parameters()(0) = 8.0;
module7.Parameters()(2) = 6.0;
@@ -1838,12 +1844,12 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
module7.Parameters()(8) = 4.0;
module7.Reset();
module7.Forward(std::move(input), std::move(output));
// Value calculated using tensorflow.nn.conv2d_transpose()
BOOST_REQUIRE_EQUAL(arma::accu(output), 12600.0);
// Value calculated using torch.nn.functional.conv_transpose2d()
BOOST_REQUIRE_EQUAL(arma::accu(output), 606.0);
// Test the backward function.
module7.Backward(std::move(input), std::move(output), std::move(delta));
BOOST_REQUIRE_EQUAL(arma::accu(delta), 185500);
// Value calculated using torch.nn.functional.conv2d()
BOOST_REQUIRE_EQUAL(arma::accu(delta), 7732.0);
}
/**
@@ -1866,8 +1872,8 @@ BOOST_AUTO_TEST_CASE(GradientTransposedConvolutionLayerTest)
model = new FFN<NegativeLogLikelihood<>, RandomInitialization>();
model->Predictors() = input;
model->Responses() = target;
model->Add<Linear<> >(36, 36);
model->Add<TransposedConvolution<> >(1, 1, 3, 3, 2, 2, 1, 1, 6, 6);
model->Add<TransposedConvolution<> >
(1, 1, 3, 3, 2, 2, 1, 1, 6, 6, 12, 12);
model->Add<LogSoftMax<> >();
}
@@ -2691,7 +2697,7 @@ void ANNLayerSerializationTest(LayerType& layer)
model.Train(input, output, opt);
arma::mat originalOutput;
model.Predict(input.col(0), originalOutput);
model.Predict(input, originalOutput);
// Now serialize the model.
FFN<NegativeLogLikelihood<>, ann::RandomInitialization> xmlModel, textModel,
@@ -2700,10 +2706,10 @@ void ANNLayerSerializationTest(LayerType& layer)
// Ensure that predictions are the same.
arma::mat modelOutput, xmlOutput, textOutput, binaryOutput;
model.Predict(input.col(0), modelOutput);
xmlModel.Predict(input.col(0), xmlOutput);
textModel.Predict(input.col(0), textOutput);
binaryModel.Predict(input.col(0), binaryOutput);
model.Predict(input, modelOutput);
xmlModel.Predict(input, xmlOutput);
textModel.Predict(input, textOutput);
binaryModel.Predict(input, binaryOutput);
CheckMatrices(originalOutput, modelOutput, 1e-5);
CheckMatrices(originalOutput, xmlOutput, 1e-5);
+138
View File
@@ -0,0 +1,138 @@
#include <ensmallen.hpp>
#include <ensmallen_bits/callbacks/callbacks.hpp>
#include <mlpack/core.hpp>
#include <mlpack/methods/ann/ffn.hpp>
#include <mlpack/methods/ann/rnn.hpp>
#include <mlpack/methods/ann/loss_functions/mean_squared_error.hpp>
#include <mlpack/methods/logistic_regression/logistic_regression.hpp>
#include <boost/test/unit_test.hpp>
using namespace mlpack;
using namespace mlpack::ann;
using namespace mlpack::regression;
BOOST_AUTO_TEST_SUITE(CallbackTest);
/**
* Test a FFN model with PrintLoss callback.
*/
BOOST_AUTO_TEST_CASE(FFNCallbackTest)
{
arma::mat data;
arma::mat labels;
data::Load("lab1.csv", data, true);
data::Load("lab3.csv", labels, true);
FFN<MeanSquaredError<>, RandomInitialization> model;
model.Add<Linear<>>(1, 2);
model.Add<SigmoidLayer<>>();
model.Add<Linear<>>(2, 1);
model.Add<SigmoidLayer<>>();
std::stringstream stream;
model.Train(data, labels, ens::PrintLoss(stream));
BOOST_REQUIRE_GT(stream.str().length(), 0);
}
/**
* Test a FFN model with PrintLoss callback and optimizer parameter.
*/
BOOST_AUTO_TEST_CASE(FFNWithOptimizerCallbackTest)
{
arma::mat data;
arma::mat labels;
data::Load("lab1.csv", data, true);
data::Load("lab3.csv", labels, true);
FFN<MeanSquaredError<>, RandomInitialization> model;
model.Add<Linear<>>(1, 2);
model.Add<SigmoidLayer<>>();
model.Add<Linear<>>(2, 1);
model.Add<SigmoidLayer<>>();
std::stringstream stream;
ens::StandardSGD opt(0.1, 1, 5);
model.Train(data, labels, opt, ens::PrintLoss(stream));
BOOST_REQUIRE_GT(stream.str().length(), 0);
}
/**
* Test a RNN model with PrintLoss callback.
*/
BOOST_AUTO_TEST_CASE(RNNCallbackTest)
{
const size_t rho = 5;
arma::cube input = arma::randu(1, 1, 5);
arma::cube target = arma::ones(1, 1, 5);
RandomInitialization init(0.5, 0.5);
// Create model with user defined rho parameter.
RNN<NegativeLogLikelihood<>, RandomInitialization> model(
rho, false, NegativeLogLikelihood<>(), init);
model.Add<IdentityLayer<> >();
model.Add<Linear<> >(1, 10);
// Use LSTM layer with rho.
model.Add<LSTM<> >(10, 3, rho);
model.Add<LogSoftMax<> >();
std::stringstream stream;
model.Train(input, target, ens::PrintLoss(stream));
BOOST_REQUIRE_GT(stream.str().length(), 0);
}
/**
* Test a RNN model with PrintLoss callback and optimizer parameter.
*/
BOOST_AUTO_TEST_CASE(RNNWithOptimizerCallbackTest)
{
const size_t rho = 5;
arma::cube input = arma::randu(1, 1, 5);
arma::cube target = arma::ones(1, 1, 5);
RandomInitialization init(0.5, 0.5);
// Create model with user defined rho parameter.
RNN<NegativeLogLikelihood<>, RandomInitialization> model(
rho, false, NegativeLogLikelihood<>(), init);
model.Add<IdentityLayer<> >();
model.Add<Linear<> >(1, 10);
// Use LSTM layer with rho.
model.Add<LSTM<> >(10, 3, rho);
model.Add<LogSoftMax<> >();
std::stringstream stream;
ens::StandardSGD opt(0.1, 1, 5);
model.Train(input, target, opt, ens::PrintLoss(stream));
BOOST_REQUIRE_GT(stream.str().length(), 0);
}
/**
* Test Logistic regression implementation with PrintLoss callback.
*/
BOOST_AUTO_TEST_CASE(LRWithOptimizerCallback)
{
arma::mat data("1 2 3;"
"1 2 3");
arma::Row<size_t> responses("1 1 0");
ens::StandardSGD sgd(0.1, 1, 5);
LogisticRegression<> logisticRegression(data, responses, sgd, 0.001);
std::stringstream stream;
logisticRegression.Train<ens::StandardSGD>(data, responses, sgd,
ens::PrintLoss(stream));
BOOST_REQUIRE_GT(stream.str().length(), 0);
}
BOOST_AUTO_TEST_SUITE_END();
+5 -3
View File
@@ -159,7 +159,7 @@ void GetRecommendationsQueriedUser()
*/
template<typename DecompositionPolicy,
typename NormalizationType = NoNormalization>
void RecommendationAccuracy()
void RecommendationAccuracy(const size_t allowedFailures = 17)
{
DecompositionPolicy decomposition;
@@ -214,7 +214,7 @@ void RecommendationAccuracy()
}
// Make sure the right item showed up in at least 2/3 of the recommendations.
BOOST_REQUIRE_LT(failures, 17);
BOOST_REQUIRE_LT(failures, allowedFailures);
}
// Make sure that Predict() is returning reasonable results.
@@ -712,7 +712,9 @@ BOOST_AUTO_TEST_CASE(RecommendationAccuracySVDIncompleteTest)
*/
BOOST_AUTO_TEST_CASE(RecommendationAccuracyBiasSVDTest)
{
RecommendationAccuracy<BiasSVDPolicy>();
// This algorithm seems to be far less effective than others.
// We therefore allow failures on 44% of the runs.
RecommendationAccuracy<BiasSVDPolicy>(22);
}
/**
+52 -19
View File
@@ -22,6 +22,7 @@
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
#include "serialization.hpp"
using namespace mlpack;
using namespace mlpack::ann;
@@ -75,7 +76,7 @@ BOOST_AUTO_TEST_CASE(DCGANMNISTTest)
<< trainData.n_cols << ")" << std::endl;
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
// Create the Discriminator network
// Create the Discriminator network.
FFN<SigmoidCrossEntropyError<> > discriminator;
discriminator.Add<Convolution<> >(1, dNumKernels, 4, 4, 2, 2, 1, 1, 28, 28);
discriminator.Add<LeakyReLU<> >(0.2);
@@ -91,48 +92,48 @@ BOOST_AUTO_TEST_CASE(DCGANMNISTTest)
discriminator.Add<Convolution<> >(8 * dNumKernels, 1, 4, 4, 1, 1,
1, 1, 2, 2);
// Create the Generator network
// Create the Generator network.
FFN<SigmoidCrossEntropyError<> > generator;
generator.Add<TransposedConvolution<> >(noiseDim, 8 * dNumKernels, 2, 2,
1, 1, 1, 1, 1, 1);
1, 1, 0, 0, 1, 1, 2, 2);
generator.Add<BatchNorm<> >(1024);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(8 * dNumKernels, 4 * dNumKernels,
2, 2, 1, 1, 0, 0, 2, 2);
2, 2, 1, 1, 0, 0, 2, 2, 3, 3);
generator.Add<BatchNorm<> >(1152);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(4 * dNumKernels, 2 * dNumKernels,
5, 5, 2, 2, 1, 1, 3, 3);
5, 5, 2, 2, 1, 1, 3, 3, 7, 7);
generator.Add<BatchNorm<> >(3136);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 8, 8,
1, 1, 1, 1, 7, 7);
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 4, 4,
2, 2, 1, 1, 7, 7, 14, 14);
generator.Add<BatchNorm<> >(6272);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 15, 15, 1, 1, 1, 1,
14, 14);
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 4, 4, 2, 2, 1, 1,
14, 14, 28, 28);
generator.Add<TanHLayer<> >();
// Create DCGAN
// Create DCGAN.
GaussianInitialization gaussian(0, 1);
ens::Adam optimizer(stepSize, batchSize, 0.9, 0.999, eps, numIterations,
tolerance, shuffle);
std::function<double()> noiseFunction = [] () {
return math::RandNormal(0, 1);};
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()>, DCGAN> dcgan(trainData, generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
std::function<double()>, DCGAN> dcgan(generator, discriminator, gaussian,
noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
Log::Info << "Training..." << std::endl;
double objVal = dcgan.Train(optimizer);
double objVal = dcgan.Train(trainData, optimizer);
// Test that objective value returned by GAN::Train() is finite.
BOOST_REQUIRE_EQUAL(std::isfinite(objVal), true);
// Generate samples
// Generate samples.
Log::Info << "Sampling..." << std::endl;
arma::mat noise(noiseDim, 1);
arma::mat noise(noiseDim, batchSize);
size_t dim = std::sqrt(trainData.n_rows);
arma::mat generatedData(2 * dim, dim * numSamples);
@@ -156,6 +157,38 @@ BOOST_AUTO_TEST_CASE(DCGANMNISTTest)
}
Log::Info << "Output generated!" << std::endl;
// Check that Serialization is working correctly.
arma::mat orgPredictions;
dcgan.Predict(noise, orgPredictions);
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()>, DCGAN> dcganText(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()>, DCGAN> dcganXml(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()>, DCGAN> dcganBinary(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
SerializeObjectAll(dcgan, dcganXml, dcganText, dcganBinary);
arma::mat predictions, xmlPredictions, textPredictions, binaryPredictions;
dcgan.Predict(noise, predictions);
dcganXml.Predict(noise, xmlPredictions);
dcganText.Predict(noise, textPredictions);
dcganBinary.Predict(noise, binaryPredictions);
CheckMatrices(orgPredictions, predictions);
CheckMatrices(orgPredictions, xmlPredictions);
CheckMatrices(orgPredictions, textPredictions);
CheckMatrices(orgPredictions, binaryPredictions);
}
@@ -357,7 +390,7 @@ BOOST_AUTO_TEST_CASE(DCGANCelebATest)
<< trainData.n_cols << ")" << std::endl;
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
// Create the Discriminator network
// Create the Discriminator network.
FFN<SigmoidCrossEntropyError<> > discriminator;
discriminator.Add<Convolution<> >(3, dNumKernels, 4, 4, 2, 2, 1, 1, 64, 64);
discriminator.Add<LeakyReLU<> >(0.2);
@@ -373,7 +406,7 @@ BOOST_AUTO_TEST_CASE(DCGANCelebATest)
discriminator.Add<Convolution<> >(8 * dNumKernels, 1, 4, 4, 1, 1,
0, 0, 4, 4);
// Create the Generator network
// Create the Generator network.
FFN<SigmoidCrossEntropyError<> > generator;
generator.Add<TransposedConvolution<> >(noiseDim, 8 * dNumKernels, 4, 4,
1, 1, 2, 2, 1, 1);
@@ -395,7 +428,7 @@ BOOST_AUTO_TEST_CASE(DCGANCelebATest)
32, 32);
generator.Add<TanHLayer<> >();
// Create DCGAN
// Create DCGAN.
GaussianInitialization gaussian(0, 1);
ens::Adam optimizer(stepSize, batchSize, 0.9, 0.999, eps, numIterations,
tolerance, shuffle);
@@ -409,7 +442,7 @@ BOOST_AUTO_TEST_CASE(DCGANCelebATest)
Log::Info << "Training..." << std::endl;
dcgan.Train(optimizer);
// Generate samples
// Generate samples.
Log::Info << "Sampling..." << std::endl;
arma::mat noise(noiseDim, 1);
size_t dim = std::sqrt(trainData.n_rows);
@@ -575,4 +575,41 @@ BOOST_AUTO_TEST_CASE(FFNTrainReturnObjective)
BOOST_REQUIRE_EQUAL(std::isfinite(objVal), true);
}
/**
* Test that FFN::Model() allows us to access the instantiated network.
*/
BOOST_AUTO_TEST_CASE(FFNReturnModel)
{
// Create dummy network.
FFN<NegativeLogLikelihood<> > model;
Linear<>* linearA = new Linear<>(3, 3);
model.Add(linearA);
Linear<>* linearB = new Linear<>(3, 4);
model.Add(linearB);
// Initialize network parameter.
model.ResetParameters();
// Set all network parameter to one.
model.Parameters().ones();
// Zero the second layer parameter.
linearB->Parameters().zeros();
// Get the layer parameter from layer A and layer B and store them in
// parameterA and parameterB.
arma::mat parameterA, parameterB;
boost::apply_visitor(ParametersVisitor(std::move(parameterA)),
model.Model()[0]);
boost::apply_visitor(ParametersVisitor(std::move(parameterB)),
model.Model()[1]);
CheckMatrices(parameterA, arma::ones(3 * 3 + 3, 1));
CheckMatrices(parameterB, arma::zeros(3 * 4 + 4, 1));
CheckMatrices(linearA->Parameters(), arma::ones(3 * 3 + 3, 1));
CheckMatrices(linearB->Parameters(), arma::zeros(3 * 4 + 4, 1));
}
BOOST_AUTO_TEST_SUITE_END();
+57 -16
View File
@@ -22,6 +22,7 @@
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
#include "serialization.hpp"
using namespace mlpack;
using namespace mlpack::ann;
@@ -53,7 +54,7 @@ BOOST_AUTO_TEST_CASE(GANTest)
trainData.imbue( [&]() { return arma::as_scalar(RandNormal(4, 0.5));});
trainData = arma::sort(trainData);
// Create the Discriminator network
// Create the Discriminator network.
FFN<SigmoidCrossEntropyError<> > discriminator;
discriminator.Add<Linear<> > (
generatorOutputSize, discriminatorHiddenLayerSize * 2);
@@ -67,30 +68,29 @@ BOOST_AUTO_TEST_CASE(GANTest)
discriminator.Add<Linear<> > (
discriminatorHiddenLayerSize * 2, discriminatorOutputSize);
// Create the Generator network
// Create the Generator network.
FFN<SigmoidCrossEntropyError<> > generator;
generator.Add<Linear<> >(noiseDim, generatorHiddenLayerSize);
generator.Add<SoftPlusLayer<> >();
generator.Add<Linear<> >(generatorHiddenLayerSize, generatorOutputSize);
// Create GAN
// Create GAN.
GaussianInitialization gaussian(0, 0.1);
std::function<double ()> noiseFunction = [](){ return math::Random(-8, 8) +
math::RandNormal(0, 1) * 0.01;};
GAN<FFN<SigmoidCrossEntropyError<> >,
GaussianInitialization,
std::function<double()> >
gan(trainData, generator, discriminator, gaussian, noiseFunction,
noiseDim, batchSize, generatorUpdateStep, discriminatorPreTrain,
multiplier);
gan.Reset();
gan(generator, discriminator, gaussian, noiseFunction, noiseDim, batchSize,
generatorUpdateStep, discriminatorPreTrain, multiplier);
gan.ResetData(trainData);
Log::Info << "Loading Parameters" << std::endl;
arma::mat parameters, generatorParameters;
parameters.load("preTrainedGAN.arm");
gan.Parameters() = parameters;
// Generate samples
// Generate samples.
Log::Info << "Sampling..." << std::endl;
arma::mat noise(noiseDim, batchSize);
@@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(GANMNISTTest)
<< trainData.n_cols << ")" << std::endl;
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
// Create the Discriminator network
// Create the Discriminator network.
FFN<SigmoidCrossEntropyError<> > discriminator;
discriminator.Add<Convolution<> >(1, dNumKernels, 5, 5, 1, 1, 2, 2, 28, 28);
discriminator.Add<ReLULayer<> >();
@@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(GANMNISTTest)
discriminator.Add<ReLULayer<> >();
discriminator.Add<Linear<> >(1024, 1);
// Create the Generator network
// Create the Generator network.
FFN<SigmoidCrossEntropyError<> > generator;
generator.Add<Linear<> >(noiseDim, 3136);
generator.Add<BatchNorm<> >(3136);
@@ -203,14 +203,14 @@ BOOST_AUTO_TEST_CASE(GANMNISTTest)
generator.Add<Convolution<> >(noiseDim / 4, 1, 3, 3, 2, 2, 1, 1, 56, 56);
generator.Add<TanHLayer<> >();
// Create GAN
// Create GAN.
GaussianInitialization gaussian(0, 1);
ens::Adam optimizer(stepSize, batchSize, 0.9, 0.999, eps, numIterations,
tolerance, shuffle);
std::function<double()> noiseFunction = [] () {
return math::RandNormal(0, 1);};
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()> > gan(trainData, generator, discriminator,
std::function<double()> > gan(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
@@ -246,6 +246,38 @@ BOOST_AUTO_TEST_CASE(GANMNISTTest)
}
Log::Info << "Output generated!" << std::endl;
// Check that Serialization is working correctly.
arma::mat orgPredictions;
gan.Predict(noise, orgPredictions);
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()> > ganText(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()> > ganXml(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<SigmoidCrossEntropyError<> >, GaussianInitialization,
std::function<double()> > ganBinary(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
SerializeObjectAll(gan, ganXml, ganText, ganBinary);
arma::mat predictions, xmlPredictions, textPredictions, binaryPredictions;
gan.Predict(noise, predictions);
ganXml.Predict(noise, xmlPredictions);
ganText.Predict(noise, textPredictions);
ganBinary.Predict(noise, binaryPredictions);
CheckMatrices(orgPredictions, predictions);
CheckMatrices(orgPredictions, xmlPredictions);
CheckMatrices(orgPredictions, textPredictions);
CheckMatrices(orgPredictions, binaryPredictions);
}
/*
@@ -263,12 +295,17 @@ BOOST_AUTO_TEST_CASE(GANMemorySharingTest)
size_t noiseDim = 1;
size_t generatorUpdateStep = 1;
double multiplier = 1;
double eps = 1e-8;
double stepSize = 0.0003;
size_t numIterations = 8;
double tolerance = 1e-5;
bool shuffle = true;
arma::mat trainData(1, 10000);
trainData.imbue( [&]() { return arma::as_scalar(RandNormal(4, 0.5));});
trainData = arma::sort(trainData);
// Create the Discriminator network
// Create the Discriminator network.
FFN<SigmoidCrossEntropyError<> > discriminator;
discriminator.Add<Linear<> > (
generatorOutputSize, discriminatorHiddenLayerSize * 2);
@@ -282,23 +319,27 @@ BOOST_AUTO_TEST_CASE(GANMemorySharingTest)
discriminator.Add<Linear<> > (
discriminatorHiddenLayerSize * 2, discriminatorOutputSize);
// Create the Generator network
// Create the Generator network.
FFN<SigmoidCrossEntropyError<> > generator;
generator.Add<Linear<> >(noiseDim, generatorHiddenLayerSize);
generator.Add<SoftPlusLayer<> >();
generator.Add<Linear<> >(generatorHiddenLayerSize, generatorOutputSize);
// Create GAN
// Create GAN.
GaussianInitialization gaussian(0, 0.1);
ens::Adam optimizer(stepSize, batchSize, 0.9, 0.999, eps, numIterations,
tolerance, shuffle);
std::function<double ()> noiseFunction = [](){ return math::Random(-8, 8) +
math::RandNormal(0, 1) * 0.01;};
GAN<FFN<SigmoidCrossEntropyError<> >,
GaussianInitialization,
std::function<double()> >
gan(trainData, generator, discriminator, gaussian, noiseFunction,
gan(generator, discriminator, gaussian, noiseFunction,
noiseDim, batchSize, generatorUpdateStep, discriminatorPreTrain,
multiplier);
gan.Train(trainData, optimizer);
CheckMatrices(gan.Predictors().head_cols(trainData.n_cols), trainData);
CheckMatrices(gan.Predictors(), gan.Discriminator().Predictors());
gan.Shuffle();
+122
View File
@@ -1329,6 +1329,62 @@ BOOST_AUTO_TEST_CASE(CopyConstructorAndOperatorRTreeTest)
CheckMatrices(distances, distances3);
}
/**
* Test the copy constructor and copy operator using the Cover Tree.
*/
BOOST_AUTO_TEST_CASE(CopyConstructorAndOperatorCoverTreeTest)
{
arma::mat dataset = arma::randu<arma::mat>(5, 500);
typedef NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat,
StandardCoverTree> NeighborSearchType;
NeighborSearchType knn(std::move(dataset));
// Copy constructor and operator.
NeighborSearchType knn2(knn);
NeighborSearchType knn3 = knn;
// Get results.
arma::mat distances, distances2, distances3;
arma::Mat<size_t> neighbors, neighbors2, neighbors3;
knn.Search(3, neighbors, distances);
knn2.Search(3, neighbors2, distances2);
knn3.Search(3, neighbors3, distances3);
CheckMatrices(neighbors, neighbors2);
CheckMatrices(neighbors, neighbors3);
CheckMatrices(distances, distances2);
CheckMatrices(distances, distances3);
}
/**
* Test the copy constructor and copy operator using the Spill Tree.
*/
BOOST_AUTO_TEST_CASE(CopyConstructorAndOperatorSpillTreeTest)
{
arma::mat dataset = arma::randu<arma::mat>(5, 500);
typedef NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat,
SPTree> NeighborSearchType;
NeighborSearchType knn(std::move(dataset));
// Copy constructor and operator.
NeighborSearchType knn2(knn);
NeighborSearchType knn3 = knn;
// Get results.
arma::mat distances, distances2, distances3;
arma::Mat<size_t> neighbors, neighbors2, neighbors3;
knn.Search(3, neighbors, distances);
knn2.Search(3, neighbors2, distances2);
knn3.Search(3, neighbors3, distances3);
CheckMatrices(neighbors, neighbors2);
CheckMatrices(neighbors, neighbors3);
CheckMatrices(distances, distances2);
CheckMatrices(distances, distances3);
}
/**
* Test the move constructor.
*/
@@ -1382,6 +1438,72 @@ BOOST_AUTO_TEST_CASE(MoveConstructorRTreeTest)
}
/**
* Test the move constructor & move assignment using Cover Tree.
*/
BOOST_AUTO_TEST_CASE(MoveConstructorCoverTreeTest)
{
arma::mat dataset = arma::randu<arma::mat>(5, 500);
typedef NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat,
StandardCoverTree> NeighborSearchType;
NeighborSearchType* knn = new NeighborSearchType(std::move(dataset));
// Get predictions.
arma::mat distances, distances2, distances3;
arma::Mat<size_t> neighbors, neighbors2, neighbors3;
knn->Search(3, neighbors, distances);
// Use move constructor.
NeighborSearchType knn2(std::move(*knn));
delete knn;
knn2.Search(3, neighbors2, distances2);
// Use move assignment.
NeighborSearchType knn3 = std::move(knn2);
knn3.Search(3, neighbors3, distances3);
CheckMatrices(neighbors, neighbors2);
CheckMatrices(neighbors, neighbors3);
CheckMatrices(distances, distances2);
CheckMatrices(distances, distances3);
}
/**
* Test the move constructor & move assignment using Spill Tree.
*/
BOOST_AUTO_TEST_CASE(MoveConstructorSpillTreeTest)
{
arma::mat dataset = arma::randu<arma::mat>(5, 500);
typedef NeighborSearch<NearestNeighborSort, EuclideanDistance, arma::mat,
SPTree> NeighborSearchType;
NeighborSearchType* knn = new NeighborSearchType(std::move(dataset));
// Get predictions.
arma::mat distances, distances2, distances3;
arma::Mat<size_t> neighbors, neighbors2, neighbors3;
knn->Search(3, neighbors, distances);
// Use move constructor.
NeighborSearchType knn2(std::move(*knn));
delete knn;
knn2.Search(3, neighbors2, distances2);
// Use move assignment.
NeighborSearchType knn3 = std::move(knn2);
knn3.Search(3, neighbors3, distances3);
CheckMatrices(neighbors, neighbors2);
CheckMatrices(neighbors, neighbors3);
CheckMatrices(distances, distances2);
CheckMatrices(distances, distances3);
}
/**
* Test the move operator.
*/
+136
View File
@@ -0,0 +1,136 @@
/**
* @file layer_names_test.cpp
* @author Sreenik Seal
*
* Tests for testing the string representation of
* layers in mlpack's ANN module.
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <mlpack/core.hpp>
#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/layer/layer_types.hpp>
#include <mlpack/methods/ann/layer_names.hpp>
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
using namespace mlpack;
using namespace ann;
BOOST_AUTO_TEST_SUITE(LayerNamesTest);
/**
* Test if the LayerNameVisitor works properly.
*/
BOOST_AUTO_TEST_CASE(LayerNameVisitorTest)
{
LayerTypes<> atrousConvolution = new AtrousConvolution<>();
LayerTypes<> alphaDropout = new AlphaDropout<>();
LayerTypes<> batchNorm = new BatchNorm<>();
LayerTypes<> constant = new Constant<>();
LayerTypes<> convolution = new Convolution<>();
LayerTypes<> dropConnect = new DropConnect<>();
LayerTypes<> dropout = new Dropout<>();
LayerTypes<> flexibleReLU = new FlexibleReLU<>();
LayerTypes<> layerNorm = new LayerNorm<>();
LayerTypes<> linear = new Linear<>();
LayerTypes<> linearNoBias = new LinearNoBias<>();
LayerTypes<> maxPooling = new MaxPooling<>();
LayerTypes<> meanPooling = new MeanPooling<>();
LayerTypes<> multiplyConstant = new MultiplyConstant<>();
LayerTypes<> reLULayer = new ReLULayer<>();
LayerTypes<> transposedConvolution = new TransposedConvolution<>();
LayerTypes<> identityLayer = new IdentityLayer<>();
LayerTypes<> tanHLayer = new TanHLayer<>();
LayerTypes<> eLU = new ELU<>();
LayerTypes<> hardTanH = new HardTanH<>();
LayerTypes<> leakyReLU = new LeakyReLU<>();
LayerTypes<> pReLU = new PReLU<>();
LayerTypes<> sigmoidLayer = new SigmoidLayer<>();
LayerTypes<> logSoftMax = new LogSoftMax<>();
// Bilinear interpolation is not yet supported by the string converter.
LayerTypes<> unsupportedLayer = new BilinearInterpolation<>();
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
atrousConvolution) == "atrousconvolution");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
alphaDropout) == "alphadropout");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
batchNorm) == "batchnorm");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
constant) == "constant");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
convolution) == "convolution");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
dropConnect) == "dropconnect");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
dropout) == "dropout");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
flexibleReLU) == "flexiblerelu");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
layerNorm) == "layernorm");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
linear) == "linear");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
linearNoBias) == "linearnobias");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
maxPooling) == "maxpooling");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
meanPooling) == "meanpooling");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
multiplyConstant) == "multiplyconstant");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
reLULayer) == "relu");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
transposedConvolution) == "transposedconvolution");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
identityLayer) == "identity");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
tanHLayer) == "tanh");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
eLU) == "elu");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
hardTanH) == "hardtanh");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
leakyReLU) == "leakyrelu");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
pReLU) == "prelu");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
sigmoidLayer) == "sigmoid");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
logSoftMax) == "logsoftmax");
BOOST_REQUIRE(boost::apply_visitor(LayerNameVisitor(),
unsupportedLayer) == "unsupported");
// Delete all instances.
boost::apply_visitor(DeleteVisitor(), atrousConvolution);
boost::apply_visitor(DeleteVisitor(), alphaDropout);
boost::apply_visitor(DeleteVisitor(), batchNorm);
boost::apply_visitor(DeleteVisitor(), constant);
boost::apply_visitor(DeleteVisitor(), convolution);
boost::apply_visitor(DeleteVisitor(), dropConnect);
boost::apply_visitor(DeleteVisitor(), dropout);
boost::apply_visitor(DeleteVisitor(), flexibleReLU);
boost::apply_visitor(DeleteVisitor(), layerNorm);
boost::apply_visitor(DeleteVisitor(), linear);
boost::apply_visitor(DeleteVisitor(), linearNoBias);
boost::apply_visitor(DeleteVisitor(), maxPooling);
boost::apply_visitor(DeleteVisitor(), meanPooling);
boost::apply_visitor(DeleteVisitor(), multiplyConstant);
boost::apply_visitor(DeleteVisitor(), reLULayer);
boost::apply_visitor(DeleteVisitor(), transposedConvolution);
boost::apply_visitor(DeleteVisitor(), identityLayer);
boost::apply_visitor(DeleteVisitor(), tanHLayer);
boost::apply_visitor(DeleteVisitor(), eLU);
boost::apply_visitor(DeleteVisitor(), hardTanH);
boost::apply_visitor(DeleteVisitor(), leakyReLU);
boost::apply_visitor(DeleteVisitor(), pReLU);
boost::apply_visitor(DeleteVisitor(), sigmoidLayer);
boost::apply_visitor(DeleteVisitor(), logSoftMax);
boost::apply_visitor(DeleteVisitor(), unsupportedLayer);
}
BOOST_AUTO_TEST_SUITE_END();
@@ -0,0 +1,947 @@
/**
* @file linear_svm_test.cpp
* @author Yashwant Singh Parihar
*
* Test mlpackMain() of logistic_regression_main.cpp
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <string>
#define BINDING_TYPE BINDING_TYPE_TEST
static const std::string testName = "LinearSVM";
#include <mlpack/core.hpp>
#include <mlpack/methods/linear_svm/linear_svm_main.cpp>
#include <mlpack/core/util/mlpack_main.hpp>
#include "test_helper.hpp"
#include <boost/test/unit_test.hpp>
#include "../test_tools.hpp"
using namespace mlpack;
struct LinearSVMTestFixture
{
public:
LinearSVMTestFixture()
{
// Cache in the options for this program.
CLI::RestoreSettings(testName);
}
~LinearSVMTestFixture()
{
// Clear the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
}
};
BOOST_FIXTURE_TEST_SUITE(LinearSVMMainTest,
LinearSVMTestFixture);
/**
* Ensure that trainingSet are necessarily passed when training.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNoTrainingData)
{
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("labels", std::move(trainLabels));
// Training data is not provided. Should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Checking that that size and dimensionality of prediction is correct.
*/
BOOST_AUTO_TEST_CASE(LinearSVMOutputDimensionTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
arma::mat testData;
if (!data::Load("iris_test.csv", testData))
BOOST_FAIL("Cannot load test dataset iris_test.csv!");
size_t testSize = testData.n_cols;
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("test", std::move(testData));
// Training the model.
mlpackMain();
// Get the output predictions of the test data.
const arma::Row<size_t>& testLabels =
CLI::GetParam<arma::Row<size_t>>("predictions");
// Output predictions size must match the test data set size.
BOOST_REQUIRE_EQUAL(testLabels.n_rows, 1);
BOOST_REQUIRE_EQUAL(testLabels.n_cols, testSize);
}
/**
* Ensuring that the labels size is checked.
*/
BOOST_AUTO_TEST_CASE(LinearSVMCheckLabelsSizeTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("vc2_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset vc2_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
// Labels with incorrect size. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Checking two options of specifying labels (extra row in train matrix and
* extra parameter) and ensuring that predictions are the same.
*/
BOOST_AUTO_TEST_CASE(LinearSVMLabelsRepresentationTest)
{
arma::mat trainData1({{1.0, 2.0, 3.0}, {1.0, 4.0, 9.0}, {0, 1, 1}});
arma::mat testData({{4.0, 5.0}, {1.0, 6.0}});
SetInputParam("training", std::move(trainData1));
SetInputParam("test", testData);
// The first solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the output.
const arma::Row<size_t> testLabels1 =
std::move(CLI::GetParam<arma::Row<size_t>>("predictions"));
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
// Now train by providing labels as extra parameter.
arma::mat trainData2({{1.0, 2.0, 3.0}, {1.0, 4.0, 9.0}});
arma::Row<size_t> trainLabels({0, 1, 1});
SetInputParam("training", std::move(trainData2));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("test", std::move(testData));
// The second solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// get the output
const arma::Row<size_t>& testLabels2 =
CLI::GetParam<arma::Row<size_t>>("predictions");
// Both solutions should be equal.
CheckMatrices(testLabels1, testLabels2);
}
/**
* Ensure that saved model can be used again.
*/
BOOST_AUTO_TEST_CASE(LinearSVMModelReuseTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
arma::mat testData;
if (!data::Load("iris_test.csv", testData))
BOOST_FAIL("Cannot load test dataset iris_test.csv!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("test", testData);
// First solution
mlpackMain();
// Get the output model obtained from training.
LinearSVMModel* model =
CLI::GetParam<LinearSVMModel*>("output_model");
// Get the output.
const arma::Row<size_t>& testLabels1 =
std::move(CLI::GetParam<arma::Row<size_t>>("predictions"));
// Reset the data passed.
CLI::GetSingleton().Parameters()["training"].wasPassed = false;
CLI::GetSingleton().Parameters()["labels"].wasPassed = false;
CLI::GetSingleton().Parameters()["test"].wasPassed = false;
SetInputParam("input_model", model);
SetInputParam("test", std::move(testData));
// Second solution.
mlpackMain();
// Get the output.
const arma::Row<size_t>& testLabels2 =
CLI::GetParam<arma::Row<size_t>>("predictions");
// Both solutions should be equal.
CheckMatrices(testLabels1, testLabels2);
}
/**
* Checking for dimensionality of the test data set.
*/
BOOST_AUTO_TEST_CASE(LinearSVMCheckDimOfTestData)
{
// Dimensionality of trainingSet is trainData.n_rows - 1 because labels are
// not provided.
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::mat testData;
if (!data::Load("iris_test.csv", testData))
BOOST_FAIL("Cannot load test dataset iris_test.csv!");
SetInputParam("training", std::move(trainData));
SetInputParam("test", std::move(testData));
// Dimensionality of test data is wrong. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that test data dimensionality is checked when model is loaded.
*/
BOOST_AUTO_TEST_CASE(LinearSVMCheckDimOfTestData2)
{
// Dimensionality of trainingSet is trainData.n_rows - 1 because labels are
// not provided.
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::mat testData;
if (!data::Load("iris_test.csv", testData))
BOOST_FAIL("Cannot load test dataset iris_test.csv!");
SetInputParam("training", std::move(trainData));
// Training the model.
mlpackMain();
// Get the output model obtained from training.
LinearSVMModel* model =
CLI::GetParam<LinearSVMModel*>("output_model");
// Reset the data passed.
CLI::GetSingleton().Parameters()["training"].wasPassed = false;
CLI::GetSingleton().Parameters()["labels"].wasPassed = false;
SetInputParam("input_model", model);
SetInputParam("test", std::move(testData));
// Test data dimensionality is wrong. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that max iteration for optimizers is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeMaxIterationTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("max_iterations", int(-1));
// Maximum iterations is negative. It should a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that lambda for optimizers is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeLambdaTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("lambda", double(-0.01));
// Lambda is negative. It should a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that number of classes for optimizers is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeNumberOfClassesTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("num_classes", int(-1));
// Number of classes is negative. It should a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that tolerance is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeToleranceTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("tolerance", double(-0.01));
// Tolerance is negative. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that delta is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeDeltaTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("delta", double(-0.01));
// Delta is negative. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that epochs is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeEpochsTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("epochs", int(-1));
// Epochs is negative. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that number classes must not be zero.
*/
BOOST_AUTO_TEST_CASE(LinearSVMZeroNumberOfClassesTest)
{
arma::mat trainData = "2 0 0;"
"0 0 0;"
"0 2 1;"
"1 0 2;"
"0 1 0";
arma::Row<size_t> trainLabels = "0 0 0";
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
// Number of classes for optimizer is zero.
// It should throw a invalid_argument error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::invalid_argument);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that Optimizer must be correct.
*/
BOOST_AUTO_TEST_CASE(LinearSVMOptimizerTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("hello"));
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring changing Maximum number of iterations changes the output model.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffMaxIterationsTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("max_iterations", int(1));
// First solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("max_iterations", int(100));
// Second solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that lambda has some effects on the output.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffLambdaTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("lambda", double(0.001));
// First solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("lambda", double(1000));
// Second solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that delta has some effects on the output.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffDeltaTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("delta", double(1.0));
// First solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("delta", double(1000));
// Second solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that no_intercept has some effects on the output.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffInterceptTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
// First solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("no_intercept", bool(true));
// Second solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that no_intercept has some effects on the output
* when the optimizer is 'psgd'.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffInterceptTestWithPsgd)
{
arma::mat trainData = "2 0 0;"
"0 0 0;"
"0 2 1;"
"1 0 2;"
"0 1 0";
arma::Row<size_t> trainLabels = "1 0 1";
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("optimizer", std::string("psgd"));
// First solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("no_intercept", bool(true));
// Second solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that step size for optimizer is non negative.
*/
BOOST_AUTO_TEST_CASE(LinearSVMNonNegativeStepSizeTest)
{
arma::mat trainData;
if (!data::Load("iris.csv", trainData))
BOOST_FAIL("Cannot load test dataset iris.csv!");
arma::Row<size_t> trainLabels;
if (!data::Load("iris_labels.txt", trainLabels))
BOOST_FAIL("Cannot load test dataset iris_labels.txt!");
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("step_size", double(-0.01));
// Step size for optimizer is negative. It should throw a runtime error.
Log::Fatal.ignoreInput = true;
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
Log::Fatal.ignoreInput = false;
}
/**
* Ensuring that epochs has some effects on the output.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffEpochsTest)
{
arma::mat trainData = "2 0 0;"
"0 0 0;"
"0 2 1;"
"1 0 2;"
"0 1 0";
arma::Row<size_t> trainLabels = "1 0 1";
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("epochs", int(5));
// First solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("epochs", int(10));
// Second solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that Step size has some effects on the output.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffStepSizeTest)
{
arma::mat trainData = "2 0 0;"
"0 0 0;"
"0 2 1;"
"1 0 2;"
"0 1 0";
arma::Row<size_t> trainLabels = "1 0 1";
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("num_classes", int(2));
SetInputParam("step_size", double(0.02));
// First solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("num_classes", int(2));
SetInputParam("step_size", double(1.02));
// Second solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that tolerance has some effects on the output.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffToleranceTest)
{
arma::mat trainData = "2 0 0;"
"0 0 0;"
"0 2 1;"
"1 0 2;"
"0 1 0";
arma::Row<size_t> trainLabels = "1 0 1";
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("num_classes", int(2));
SetInputParam("tolerance", double(1e-1));
// First solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("psgd"));
SetInputParam("num_classes", int(2));
SetInputParam("tolerance", double(1e-10));
// Second solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
/**
* Ensuring that lbfgs optimizer converges to a different result than psgd.
*/
BOOST_AUTO_TEST_CASE(LinearSVMDiffOptimizerTest)
{
arma::mat trainData = "2 0 0;"
"0 0 0;"
"0 2 1;"
"1 0 2;"
"0 1 0";
arma::Row<size_t> trainLabels = "1 0 1";
SetInputParam("training", trainData);
SetInputParam("labels", trainLabels);
SetInputParam("optimizer", std::string("lbfgs"));
// First solution.
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after first training.
const arma::mat parameters1 = std::move(
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters());
// Reset the settings.
bindings::tests::CleanMemory();
CLI::ClearSettings();
CLI::RestoreSettings(testName);
SetInputParam("training", std::move(trainData));
SetInputParam("labels", std::move(trainLabels));
SetInputParam("optimizer", std::string("psgd"));
// Second solution.
#ifdef HAS_OPENMP
omp_set_num_threads(1);
#endif
mlpack::math::FixedRandomSeed();
mlpackMain();
// Get the parameters of the output model obtained after second training.
const arma::mat& parameters2 =
CLI::GetParam<LinearSVMModel*>("output_model")->svm.Parameters();
// Both solutions should be not equal.
CheckMatricesNotEqual(parameters1, parameters2);
}
BOOST_AUTO_TEST_SUITE_END();
+94 -30
View File
@@ -22,6 +22,7 @@
#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"
#include "serialization.hpp"
using namespace mlpack;
using namespace mlpack::ann;
@@ -76,7 +77,7 @@ BOOST_AUTO_TEST_CASE(WGANMNISTTest)
<< trainData.n_cols << ")" << std::endl;
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
// Create the Discriminator network
// Create the Discriminator network.
FFN<EarthMoverDistance<> > discriminator;
discriminator.Add<Convolution<> >(1, dNumKernels, 4, 4, 2, 2, 1, 1, 28, 28);
discriminator.Add<LeakyReLU<> >(0.2);
@@ -93,46 +94,46 @@ BOOST_AUTO_TEST_CASE(WGANMNISTTest)
1, 1, 2, 2);
discriminator.Add<SigmoidLayer<> >();
// Create the Generator network
// Create the Generator network.
FFN<EarthMoverDistance<> > generator;
generator.Add<TransposedConvolution<> >(noiseDim, 8 * dNumKernels, 2, 2,
1, 1, 1, 1, 1, 1);
1, 1, 0, 0, 1, 1, 2, 2);
generator.Add<BatchNorm<> >(1024);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(8 * dNumKernels, 4 * dNumKernels,
2, 2, 1, 1, 0, 0, 2, 2);
2, 2, 1, 1, 0, 0, 2, 2, 3, 3);
generator.Add<BatchNorm<> >(1152);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(4 * dNumKernels, 2 * dNumKernels,
5, 5, 2, 2, 1, 1, 3, 3);
5, 5, 2, 2, 1, 1, 3, 3, 7, 7);
generator.Add<BatchNorm<> >(3136);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 8, 8,
1, 1, 1, 1, 7, 7);
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 4, 4,
2, 2, 1, 1, 7, 7, 14, 14);
generator.Add<BatchNorm<> >(6272);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 15, 15, 1, 1, 1, 1,
14, 14);
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 4, 4, 2, 2, 1, 1,
14, 14, 28, 28);
generator.Add<TanHLayer<> >();
// Create WGAN
// Create WGAN.
GaussianInitialization gaussian(0, 1);
ens::Adam optimizer(stepSize, batchSize, 0.9, 0.999, eps, numIterations,
tolerance, shuffle);
std::function<double()> noiseFunction = [] () {
return math::RandNormal(0, 1);};
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGAN> wgan(trainData, generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
std::function<double()>, WGAN> wgan(generator, discriminator, gaussian,
noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier, clippingParameter);
Log::Info << "Training..." << std::endl;
double objVal = wgan.Train(optimizer);
double objVal = wgan.Train(trainData, optimizer);
// Test that objective value returned by GAN::Train() is finite.
BOOST_REQUIRE_EQUAL(std::isfinite(objVal), true);
// Generate samples
// Generate samples.
Log::Info << "Sampling..." << std::endl;
arma::mat noise(noiseDim, batchSize);
size_t dim = std::sqrt(trainData.n_rows);
@@ -158,6 +159,38 @@ BOOST_AUTO_TEST_CASE(WGANMNISTTest)
}
Log::Info << "Output generated!" << std::endl;
// Check that Serialization is working correctly.
arma::mat orgPredictions;
wgan.Predict(noise, orgPredictions);
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGAN> wganText(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGAN> wganXml(generator, discriminator, gaussian,
noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGAN> wganBinary(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
SerializeObjectAll(wgan, wganXml, wganText, wganBinary);
arma::mat predictions, xmlPredictions, textPredictions, binaryPredictions;
wgan.Predict(noise, predictions);
wganXml.Predict(noise, xmlPredictions);
wganText.Predict(noise, textPredictions);
wganBinary.Predict(noise, binaryPredictions);
CheckMatrices(orgPredictions, predictions);
CheckMatrices(orgPredictions, xmlPredictions);
CheckMatrices(orgPredictions, textPredictions);
CheckMatrices(orgPredictions, binaryPredictions);
}
/*
@@ -206,7 +239,7 @@ BOOST_AUTO_TEST_CASE(WGANGPMNISTTest)
<< trainData.n_cols << ")" << std::endl;
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
// Create the Discriminator network
// Create the Discriminator network.
FFN<EarthMoverDistance<> > discriminator;
discriminator.Add<Convolution<> >(1, dNumKernels, 4, 4, 2, 2, 1, 1, 28, 28);
discriminator.Add<LeakyReLU<> >(0.2);
@@ -223,47 +256,46 @@ BOOST_AUTO_TEST_CASE(WGANGPMNISTTest)
1, 1, 2, 2);
discriminator.Add<SigmoidLayer<> >();
// Create the Generator network
// Create the Generator network.
FFN<EarthMoverDistance<> > generator;
generator.Add<TransposedConvolution<> >(noiseDim, 8 * dNumKernels, 2, 2,
1, 1, 1, 1, 1, 1);
1, 1, 0, 0, 1, 1, 2, 2);
generator.Add<BatchNorm<> >(1024);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(8 * dNumKernels, 4 * dNumKernels,
2, 2, 1, 1, 0, 0, 2, 2);
2, 2, 1, 1, 0, 0, 2, 2, 3, 3);
generator.Add<BatchNorm<> >(1152);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(4 * dNumKernels, 2 * dNumKernels,
5, 5, 2, 2, 1, 1, 3, 3);
5, 5, 2, 2, 1, 1, 3, 3, 7, 7);
generator.Add<BatchNorm<> >(3136);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 8, 8,
1, 1, 1, 1, 7, 7);
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 4, 4,
2, 2, 1, 1, 7, 7, 14, 14);
generator.Add<BatchNorm<> >(6272);
generator.Add<ReLULayer<> >();
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 15, 15, 1, 1, 1, 1,
14, 14);
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 4, 4, 2, 2, 1, 1,
14, 14, 28, 28);
generator.Add<TanHLayer<> >();
// Create WGANGP
// Create WGANGP.
GaussianInitialization gaussian(0, 1);
ens::Adam optimizer(stepSize, batchSize, 0.9, 0.999, eps, numIterations,
tolerance, shuffle);
std::function<double()> noiseFunction = [] () {
return math::RandNormal(0, 1);};
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGANGP > wganGP(trainData, generator,
discriminator, gaussian, noiseFunction, noiseDim, batchSize,
generatorUpdateStep, discriminatorPreTrain, multiplier, clippingParameter,
lambda);
std::function<double()>, WGANGP> wganGP(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier, clippingParameter, lambda);
Log::Info << "Training..." << std::endl;
double objVal = wganGP.Train(optimizer);
double objVal = wganGP.Train(trainData, optimizer);
// Test that objective value returned by GAN::Train() is finite.
BOOST_REQUIRE_EQUAL(std::isfinite(objVal), true);
// Generate samples
// Generate samples.
Log::Info << "Sampling..." << std::endl;
arma::mat noise(noiseDim, batchSize);
size_t dim = std::sqrt(trainData.n_rows);
@@ -289,6 +321,38 @@ BOOST_AUTO_TEST_CASE(WGANGPMNISTTest)
}
Log::Info << "Output generated!" << std::endl;
// Check that Serialization is working correctly.
arma::mat orgPredictions;
wganGP.Predict(noise, orgPredictions);
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGANGP> wganGPText(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGANGP> wganGPXml(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
GAN<FFN<EarthMoverDistance<> >, GaussianInitialization,
std::function<double()>, WGANGP> wganGPBinary(generator, discriminator,
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
discriminatorPreTrain, multiplier);
SerializeObjectAll(wganGP, wganGPXml, wganGPText, wganGPBinary);
arma::mat predictions, xmlPredictions, textPredictions, binaryPredictions;
wganGP.Predict(noise, predictions);
wganGPXml.Predict(noise, xmlPredictions);
wganGPText.Predict(noise, textPredictions);
wganGPBinary.Predict(noise, binaryPredictions);
CheckMatrices(orgPredictions, predictions);
CheckMatrices(orgPredictions, xmlPredictions);
CheckMatrices(orgPredictions, textPredictions);
CheckMatrices(orgPredictions, binaryPredictions);
}
BOOST_AUTO_TEST_SUITE_END();