Merge branch 'master' into subviewMat
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
### mlpack ?.?.?
|
||||
###### ????-??-??
|
||||
* Fix Visual Studio compilation issue (#1443).
|
||||
|
||||
### mlpack 3.0.2
|
||||
###### 2018-06-08
|
||||
|
||||
@@ -16,6 +16,19 @@
|
||||
namespace mlpack {
|
||||
namespace math {
|
||||
|
||||
/**
|
||||
* Make an alias of a dense cube. If strict is true, then the alias cannot be
|
||||
* resized or pointed at new memory.
|
||||
*/
|
||||
template<typename ElemType>
|
||||
arma::Cube<ElemType> MakeAlias(arma::Cube<ElemType>& input,
|
||||
const bool strict = true)
|
||||
{
|
||||
// Use the advanced constructor.
|
||||
return arma::Cube<ElemType>(input.memptr(), input.n_rows, input.n_cols,
|
||||
input.n_slices, false, strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an alias of a dense matrix. If strict is true, then the alias cannot be
|
||||
* resized or pointed at new memory.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "visitor/reset_visitor.hpp"
|
||||
#include "visitor/weight_size_visitor.hpp"
|
||||
#include "visitor/copy_visitor.hpp"
|
||||
#include "visitor/loss_visitor.hpp"
|
||||
|
||||
#include "init_rules/network_init.hpp"
|
||||
|
||||
@@ -371,6 +372,9 @@ class FFN
|
||||
//! Locally-stored output height visitor.
|
||||
OutputHeightVisitor outputHeightVisitor;
|
||||
|
||||
//! Locally-stored loss visitor
|
||||
LossVisitor lossVisitor;
|
||||
|
||||
//! Locally-stored reset visitor.
|
||||
ResetVisitor resetVisitor;
|
||||
|
||||
|
||||
@@ -132,6 +132,11 @@ double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Backward(
|
||||
double res = outputLayer.Forward(std::move(boost::apply_visitor(
|
||||
outputParameterVisitor, network.back())), std::move(targets));
|
||||
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
res += boost::apply_visitor(lossVisitor, network[i]);
|
||||
}
|
||||
|
||||
outputLayer.Backward(std::move(boost::apply_visitor(outputParameterVisitor,
|
||||
network.back())), std::move(targets), std::move(error));
|
||||
|
||||
@@ -212,6 +217,11 @@ double FFN<OutputLayerType, InitializationRuleType, CustomLayers...>::Evaluate(
|
||||
std::move(boost::apply_visitor(outputParameterVisitor, network.back())),
|
||||
std::move(responses.cols(begin, begin + batchSize - 1)));
|
||||
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
res += boost::apply_visitor(lossVisitor, network[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,19 +60,21 @@ GAN<Model, InitializationRuleType, Noise>::GAN(
|
||||
responses.set_size(1, predictors.n_cols);
|
||||
responses.ones();
|
||||
|
||||
discriminator.predictors.set_size(predictors.n_rows, predictors.n_cols + 1);
|
||||
discriminator.predictors.set_size(predictors.n_rows,
|
||||
predictors.n_cols + batchSize);
|
||||
discriminator.predictors.cols(0, predictors.n_cols - 1) = predictors;
|
||||
|
||||
discriminator.responses.set_size(1, predictors.n_cols + 1);
|
||||
discriminator.responses.set_size(1, predictors.n_cols + batchSize);
|
||||
discriminator.responses.ones();
|
||||
discriminator.responses(predictors.n_cols) = 0;
|
||||
discriminator.responses.cols(predictors.n_cols,
|
||||
predictors.n_cols + batchSize - 1) = arma::zeros(1, batchSize);
|
||||
|
||||
numFunctions = predictors.n_cols;
|
||||
|
||||
noise.set_size(noiseDim, 1);
|
||||
noise.set_size(noiseDim, batchSize);
|
||||
|
||||
generator.predictors.set_size(noiseDim, 1);
|
||||
generator.responses.set_size(predictors.n_rows, 1);
|
||||
generator.predictors.set_size(noiseDim, batchSize);
|
||||
generator.responses.set_size(predictors.n_rows, batchSize);
|
||||
}
|
||||
|
||||
template<typename Model, typename InitializationRuleType, typename Noise>
|
||||
@@ -98,7 +100,7 @@ void GAN<Model, InitializationRuleType, Noise>::Reset()
|
||||
generator.Parameters() = arma::mat(parameter.memptr(), genWeights, 1, false,
|
||||
false);
|
||||
discriminator.Parameters() = arma::mat(parameter.memptr() + genWeights,
|
||||
discWeights, 1 , false, false);
|
||||
discWeights, 1, false, false);
|
||||
|
||||
// Initialize the parameters generator
|
||||
networkInit.Initialize(generator.network, parameter);
|
||||
@@ -127,8 +129,10 @@ double GAN<Model, InitializationRuleType, Noise>::Evaluate(
|
||||
if (!reset)
|
||||
Reset();
|
||||
|
||||
currentInput = this->predictors.unsafe_col(i);
|
||||
currentTarget = this->responses.unsafe_col(i);
|
||||
currentInput = arma::mat(predictors.memptr() + (i * predictors.n_rows),
|
||||
predictors.n_rows, batchSize, false, false);
|
||||
currentTarget = arma::mat(responses.memptr() + i, 1, batchSize, false,
|
||||
false);
|
||||
|
||||
discriminator.Forward(std::move(currentInput));
|
||||
double res = discriminator.outputLayer.Forward(
|
||||
@@ -139,12 +143,15 @@ double GAN<Model, InitializationRuleType, Noise>::Evaluate(
|
||||
noise.imbue( [&]() { return noiseFunction();} );
|
||||
generator.Forward(std::move(noise));
|
||||
|
||||
discriminator.predictors.col(numFunctions) = boost::apply_visitor(
|
||||
outputParameterVisitor, generator.network.back());;
|
||||
discriminator.Forward(std::move(discriminator.predictors.col(numFunctions)));
|
||||
discriminator.responses(numFunctions) = 0;
|
||||
discriminator.predictors.cols(numFunctions, numFunctions + batchSize - 1) =
|
||||
boost::apply_visitor(outputParameterVisitor, generator.network.back());
|
||||
discriminator.Forward(std::move(discriminator.predictors.cols(numFunctions,
|
||||
numFunctions + batchSize - 1)));
|
||||
discriminator.responses.cols(numFunctions, numFunctions + batchSize - 1) =
|
||||
arma::zeros(1, batchSize);
|
||||
|
||||
currentTarget = discriminator.responses.unsafe_col(numFunctions);
|
||||
currentTarget = arma::mat(discriminator.responses.memptr() + numFunctions,
|
||||
1, batchSize, false, false);
|
||||
res += discriminator.outputLayer.Forward(
|
||||
std::move(boost::apply_visitor(
|
||||
outputParameterVisitor,
|
||||
@@ -190,13 +197,13 @@ Gradient(const arma::mat& /*parameters*/, const size_t i, arma::mat& gradient,
|
||||
// Get the gradients of the Discriminator.
|
||||
discriminator.Gradient(discriminator.parameter, i, gradientDiscriminator,
|
||||
batchSize);
|
||||
|
||||
noise.imbue( [&]() { return noiseFunction();} );
|
||||
generator.Forward(std::move(noise));
|
||||
discriminator.predictors.col(numFunctions) = boost::apply_visitor(
|
||||
outputParameterVisitor, generator.network.back());
|
||||
discriminator.predictors.cols(numFunctions, numFunctions + batchSize - 1) =
|
||||
boost::apply_visitor(outputParameterVisitor, generator.network.back());
|
||||
|
||||
discriminator.responses(numFunctions) = 0;
|
||||
discriminator.responses.cols(numFunctions, numFunctions + batchSize - 1) =
|
||||
arma::zeros(1, batchSize);
|
||||
discriminator.Gradient(discriminator.parameter, numFunctions,
|
||||
noiseGradientDiscriminator, batchSize);
|
||||
gradientDiscriminator += noiseGradientDiscriminator;
|
||||
@@ -205,7 +212,8 @@ Gradient(const arma::mat& /*parameters*/, const size_t i, arma::mat& gradient,
|
||||
{
|
||||
// Minimize -log(D(G(noise))).
|
||||
// Pass the error from Discriminator to Generator.
|
||||
discriminator.responses(numFunctions) = 1;
|
||||
discriminator.responses.cols(numFunctions, numFunctions + batchSize - 1) =
|
||||
arma::ones(1, batchSize);
|
||||
discriminator.Gradient(discriminator.parameter, numFunctions,
|
||||
noiseGradientDiscriminator, batchSize);
|
||||
generator.error = boost::apply_visitor(deltaVisitor,
|
||||
@@ -213,7 +221,7 @@ Gradient(const arma::mat& /*parameters*/, const size_t i, arma::mat& gradient,
|
||||
|
||||
generator.Predictors() = noise;
|
||||
generator.ResetGradients(gradientGenerator);
|
||||
generator.Gradient(generator.parameter, 0, gradientGenerator, noise.n_cols);
|
||||
generator.Gradient(generator.parameter, 0, gradientGenerator, batchSize);
|
||||
|
||||
gradientGenerator *= multiplier;
|
||||
}
|
||||
|
||||
@@ -65,8 +65,6 @@ set(SOURCES
|
||||
multiply_constant_impl.hpp
|
||||
multiply_merge.hpp
|
||||
multiply_merge_impl.hpp
|
||||
negative_log_likelihood.hpp
|
||||
negative_log_likelihood_impl.hpp
|
||||
parametric_relu.hpp
|
||||
parametric_relu_impl.hpp
|
||||
recurrent.hpp
|
||||
@@ -75,6 +73,8 @@ set(SOURCES
|
||||
recurrent_attention_impl.hpp
|
||||
reinforce_normal.hpp
|
||||
reinforce_normal_impl.hpp
|
||||
reparametrization.hpp
|
||||
reparametrization_impl.hpp
|
||||
select.hpp
|
||||
select_impl.hpp
|
||||
sequential.hpp
|
||||
|
||||
@@ -82,11 +82,6 @@ class Add
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -121,9 +116,6 @@ class Add
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class Add
|
||||
|
||||
@@ -46,8 +46,9 @@ class AddMerge
|
||||
* Create the AddMerge object using the specified parameters.
|
||||
*
|
||||
* @param model Expose all the network modules.
|
||||
* @param run Call the Forward/Backward method before the output is merged.
|
||||
*/
|
||||
AddMerge(const bool model = false);
|
||||
AddMerge(const bool model = false, const bool run = true);
|
||||
|
||||
//! Destructor to release allocated memory.
|
||||
~AddMerge();
|
||||
@@ -60,7 +61,7 @@ class AddMerge
|
||||
* @param output Resulting output activation.
|
||||
*/
|
||||
template<typename InputType, typename OutputType>
|
||||
void Forward(const InputType&& /* input */, OutputType&& output);
|
||||
void Forward(InputType&& /* input */, OutputType&& output);
|
||||
|
||||
/**
|
||||
* Ordinary feed backward pass of a neural network, calculating the function
|
||||
@@ -77,19 +78,16 @@ class AddMerge
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
* Calculate the gradient using the output delta and the input activation.
|
||||
*
|
||||
* @param layer The Layer to be added to the model.
|
||||
* @param input The input parameter used for calculating the gradient.
|
||||
* @param error The calculated error.
|
||||
* @param gradient The calculated gradient.
|
||||
*/
|
||||
void Add(LayerTypes<CustomLayers...> layer) { network.push_back(layer); }
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
*
|
||||
* @param layer The Layer to be added to the model.
|
||||
*/
|
||||
template<typename LayerType>
|
||||
void Add(const LayerType& layer) { network.push_back(new LayerType(layer)); }
|
||||
template<typename eT>
|
||||
void Gradient(arma::Mat<eT>&& input,
|
||||
arma::Mat<eT>&& error,
|
||||
arma::Mat<eT>&& gradient);
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
@@ -99,6 +97,13 @@ class AddMerge
|
||||
template <class LayerType, class... Args>
|
||||
void Add(Args... args) { network.push_back(new LayerType(args...)); }
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
*
|
||||
* @param layer The Layer to be added to the model.
|
||||
*/
|
||||
void Add(LayerTypes<CustomLayers...> layer) { network.push_back(layer); }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
@@ -125,6 +130,11 @@ class AddMerge
|
||||
return empty;
|
||||
}
|
||||
|
||||
//! Get the parameters.
|
||||
OutputDataType const& Parameters() const { return weights; }
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
@@ -135,6 +145,10 @@ class AddMerge
|
||||
//! Parameter which indicates if the modules should be exposed.
|
||||
bool model;
|
||||
|
||||
//! Parameter which indicates if the Forward/Backward method should be called
|
||||
//! before merging the output.
|
||||
bool run;
|
||||
|
||||
//! We need this to know whether we should delete the layer in the destructor.
|
||||
bool ownsLayer;
|
||||
|
||||
@@ -156,11 +170,17 @@ class AddMerge
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
//! Locally-stored weight object.
|
||||
OutputDataType weights;
|
||||
}; // class AddMerge
|
||||
|
||||
} // namespace ann
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
// In case it hasn't yet been included.
|
||||
#include "add_merge.hpp"
|
||||
|
||||
#include "../visitor/forward_visitor.hpp"
|
||||
#include "../visitor/backward_visitor.hpp"
|
||||
#include "../visitor/gradient_visitor.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
AddMerge<InputDataType, OutputDataType, CustomLayers...>::AddMerge(
|
||||
const bool model) : model(model), ownsLayer(!model)
|
||||
const bool model, const bool run) :
|
||||
model(model), run(run), ownsLayer(!model)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -42,10 +47,19 @@ template <typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
template<typename InputType, typename OutputType>
|
||||
void AddMerge<InputDataType, OutputDataType, CustomLayers...>::Forward(
|
||||
const InputType&& /* input */, OutputType&& output)
|
||||
InputType&& input, OutputType&& output)
|
||||
{
|
||||
output = boost::apply_visitor(outputParameterVisitor, network.front());
|
||||
if (run)
|
||||
{
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(ForwardVisitor(std::move(input), std::move(
|
||||
boost::apply_visitor(outputParameterVisitor, network[i]))),
|
||||
network[i]);
|
||||
}
|
||||
}
|
||||
|
||||
output = boost::apply_visitor(outputParameterVisitor, network.front());
|
||||
for (size_t i = 1; i < network.size(); ++i)
|
||||
{
|
||||
output += boost::apply_visitor(outputParameterVisitor, network[i]);
|
||||
@@ -58,7 +72,41 @@ template<typename eT>
|
||||
void AddMerge<InputDataType, OutputDataType, CustomLayers...>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
g = gy;
|
||||
if (run)
|
||||
{
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(BackwardVisitor(std::move(boost::apply_visitor(
|
||||
outputParameterVisitor, network[i])), std::move(gy), std::move(
|
||||
boost::apply_visitor(deltaVisitor, network[i]))), network[i]);
|
||||
}
|
||||
|
||||
g = boost::apply_visitor(deltaVisitor, network[0]);
|
||||
for (size_t i = 1; i < network.size(); ++i)
|
||||
{
|
||||
g += boost::apply_visitor(deltaVisitor, network[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
g = gy;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
template<typename eT>
|
||||
void AddMerge<InputDataType, OutputDataType, CustomLayers...>::Gradient(
|
||||
arma::Mat<eT>&& input,
|
||||
arma::Mat<eT>&& error,
|
||||
arma::Mat<eT>&& /* gradient */ )
|
||||
{
|
||||
if (run)
|
||||
{
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(GradientVisitor(std::move(input), std::move(error)),
|
||||
network[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
|
||||
@@ -80,11 +80,6 @@ class AlphaDropout
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -134,9 +129,6 @@ class AlphaDropout
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -130,11 +130,6 @@ class AtrousConvolution
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -272,12 +267,15 @@ class AtrousConvolution
|
||||
}
|
||||
}
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
//! Locally-stored number of input channels.
|
||||
size_t inSize;
|
||||
|
||||
//! Locally-stored number of output units.
|
||||
//! Locally-stored number of output channels.
|
||||
size_t outSize;
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
size_t batchSize;
|
||||
|
||||
//! Locally-stored filter/kernel width.
|
||||
size_t kW;
|
||||
|
||||
@@ -344,9 +342,6 @@ class AtrousConvolution
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class AtrousConvolution
|
||||
|
||||
@@ -118,7 +118,9 @@ void AtrousConvolution<
|
||||
OutputDataType
|
||||
>::Forward(const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
inputTemp = arma::cube(input.memptr(), inputWidth, inputHeight, inSize);
|
||||
batchSize = input.n_cols;
|
||||
inputTemp = arma::cube(const_cast<arma::Mat<eT>&&>(input).memptr(),
|
||||
inputWidth, inputHeight, inSize * batchSize, false, false);
|
||||
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
@@ -128,30 +130,41 @@ void AtrousConvolution<
|
||||
size_t wConv = ConvOutSize(inputWidth, kW, dW, padW, dilationW);
|
||||
size_t hConv = ConvOutSize(inputHeight, kH, dH, padH, dilationH);
|
||||
|
||||
output.set_size(wConv * hConv * outSize, 1);
|
||||
outputTemp = arma::Cube<eT>(output.memptr(), wConv, hConv, outSize,
|
||||
false, false);
|
||||
output.set_size(wConv * hConv * outSize, batchSize);
|
||||
outputTemp = arma::Cube<eT>(output.memptr(), wConv, hConv,
|
||||
outSize * batchSize, false, false);
|
||||
outputTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> convOutput;
|
||||
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
ForwardConvolutionRule::Convolution(inputPaddedTemp.slice(inMap),
|
||||
weight.slice(outMapIdx), convOutput, dW, dH, dilationW, dilationH);
|
||||
ForwardConvolutionRule::Convolution(inputPaddedTemp.slice(inMap +
|
||||
batchCount * inSize), weight.slice(outMapIdx), convOutput, dW, dH,
|
||||
dilationW, dilationH);
|
||||
}
|
||||
else
|
||||
{
|
||||
ForwardConvolutionRule::Convolution(inputTemp.slice(inMap),
|
||||
weight.slice(outMapIdx), convOutput, dW, dH, dilationW, dilationH);
|
||||
ForwardConvolutionRule::Convolution(inputTemp.slice(inMap +
|
||||
batchCount * inSize), weight.slice(outMapIdx), convOutput, dW, dH,
|
||||
dilationW, dilationH);
|
||||
}
|
||||
|
||||
outputTemp.slice(outMap) += convOutput;
|
||||
}
|
||||
|
||||
outputTemp.slice(outMap) += bias(outMap);
|
||||
outputTemp.slice(outMap) += bias(outMap % outSize);
|
||||
}
|
||||
|
||||
outputWidth = outputTemp.n_rows;
|
||||
@@ -175,16 +188,23 @@ void AtrousConvolution<
|
||||
>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight, outSize,
|
||||
false, false);
|
||||
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight,
|
||||
outSize * batchSize, false, false);
|
||||
|
||||
g.set_size(inputTemp.n_rows * inputTemp.n_cols * inputTemp.n_slices, 1);
|
||||
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);
|
||||
gTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> output, rotatedFilter;
|
||||
@@ -195,14 +215,13 @@ void AtrousConvolution<
|
||||
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
gTemp.slice(inMap) += output.submat(rotatedFilter.n_rows / 2,
|
||||
rotatedFilter.n_cols / 2,
|
||||
rotatedFilter.n_rows / 2 + gTemp.n_rows - 1,
|
||||
rotatedFilter.n_cols / 2 + gTemp.n_cols - 1);
|
||||
gTemp.slice(inMap + batchCount * inSize) += output.submat(padW, padH,
|
||||
padW + gTemp.n_rows - 1,
|
||||
padH + gTemp.n_cols - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gTemp.slice(inMap) += output;
|
||||
gTemp.slice(inMap + batchCount * inSize) += output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,81 +250,75 @@ void AtrousConvolution<
|
||||
if (padW != 0 && padH != 0)
|
||||
{
|
||||
mappedError = arma::cube(error.memptr(), outputWidth / padW,
|
||||
outputHeight / padH, outSize);
|
||||
outputHeight / padH, outSize * batchSize, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mappedError = arma::cube(error.memptr(), outputWidth,
|
||||
outputHeight, outSize);
|
||||
outputHeight, outSize * batchSize, false, false);
|
||||
}
|
||||
|
||||
gradient.set_size(weights.n_elem, 1);
|
||||
gradientTemp = arma::Cube<eT>(gradient.memptr(), weight.n_rows, weight.n_cols,
|
||||
weight.n_slices, false, false);
|
||||
gradientTemp = arma::Cube<eT>(gradient.memptr(), weight.n_rows,
|
||||
weight.n_cols, weight.n_slices, false, false);
|
||||
gradientTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
for (size_t inMap = 0, s = outMap; inMap < inSize; inMap++, outMapIdx++,
|
||||
s += outSize)
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
arma::Cube<eT> inputSlices;
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> inputSlice;
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
inputSlices = inputPaddedTemp.slices(inMap, inMap);
|
||||
inputSlice = inputPaddedTemp.slice(inMap + batchCount * inSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
inputSlices = inputTemp.slices(inMap, inMap);
|
||||
inputSlice = inputTemp.slice(inMap + batchCount * inSize);
|
||||
}
|
||||
|
||||
arma::Cube<eT> deltaSlices = mappedError.slices(outMap, outMap);
|
||||
arma::Mat<eT> deltaSlice = mappedError.slice(outMap);
|
||||
|
||||
arma::Cube<eT> output, reducedOutput;
|
||||
GradientConvolutionRule::Convolution(inputSlices, deltaSlices,
|
||||
arma::Mat<eT> output;
|
||||
GradientConvolutionRule::Convolution(inputSlice, deltaSlice,
|
||||
output, dW, dH, 1, 1);
|
||||
arma::Mat<eT> reducedMat;
|
||||
reducedOutput = arma::zeros<arma::Cube<eT> >(kW, kH, output.n_slices);
|
||||
|
||||
for (size_t j = 0; j < output.n_slices; j++)
|
||||
if (dilationH > 1)
|
||||
{
|
||||
reducedMat = output.slice(j);
|
||||
if (dilationH > 1)
|
||||
{
|
||||
for (size_t i = 1; i < reducedMat.n_cols; i++){
|
||||
reducedMat.shed_cols(i, i + dilationH - 2);
|
||||
}
|
||||
for (size_t i = 1; i < output.n_cols; i++){
|
||||
output.shed_cols(i, i + dilationH - 2);
|
||||
}
|
||||
if (dilationW > 1)
|
||||
{
|
||||
for (size_t i = 1; i < reducedMat.n_rows; i++){
|
||||
reducedMat.shed_rows(i, i + dilationW - 2);
|
||||
}
|
||||
}
|
||||
if (dilationW > 1)
|
||||
{
|
||||
for (size_t i = 1; i < output.n_rows; i++){
|
||||
output.shed_rows(i, i + dilationW - 2);
|
||||
}
|
||||
reducedOutput.slice(j) = reducedMat;
|
||||
}
|
||||
|
||||
if ((padW != 0 || padH != 0) &&
|
||||
(gradientTemp.n_rows < reducedOutput.n_rows &&
|
||||
gradientTemp.n_cols < reducedOutput.n_cols))
|
||||
(gradientTemp.n_rows < output.n_rows &&
|
||||
gradientTemp.n_cols < output.n_cols))
|
||||
{
|
||||
for (size_t i = 0; i < reducedOutput.n_slices; i++)
|
||||
{
|
||||
gradientTemp.slice(s) += reducedOutput.slice(i).submat(
|
||||
reducedOutput.n_rows / 2, reducedOutput.n_cols / 2,
|
||||
reducedOutput.n_rows / 2 + gradientTemp.n_rows - 1,
|
||||
reducedOutput.n_cols / 2 + gradientTemp.n_cols - 1);
|
||||
}
|
||||
gradientTemp.slice(outMapIdx) += output.submat(padW, padH,
|
||||
padW + gradientTemp.n_rows - 1,
|
||||
padH + gradientTemp.n_cols - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < reducedOutput.n_slices; i++)
|
||||
gradientTemp.slice(s) += reducedOutput.slice(i);
|
||||
gradientTemp.slice(outMapIdx) += output;
|
||||
}
|
||||
}
|
||||
|
||||
gradient.submat(weight.n_elem + outMap, 0, weight.n_elem + outMap, 0) =
|
||||
arma::accu(mappedError.slices(outMap, outMap));
|
||||
gradient.submat(weight.n_elem + (outMap % outSize), 0, weight.n_elem +
|
||||
(outMap % outSize), 0) = arma::accu(mappedError.slice(outMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,6 +341,7 @@ void AtrousConvolution<
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(inSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(outSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(batchSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(kW);
|
||||
ar & BOOST_SERIALIZATION_NVP(kH);
|
||||
ar & BOOST_SERIALIZATION_NVP(dW);
|
||||
|
||||
@@ -88,11 +88,6 @@ class BaseLayer
|
||||
g = gy % derivative;
|
||||
}
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -116,9 +111,6 @@ class BaseLayer
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class BaseLayer
|
||||
|
||||
@@ -112,11 +112,6 @@ class BatchNorm
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -186,9 +181,6 @@ class BatchNorm
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @file bilinear_interpolation.hpp
|
||||
* @author Kris Singh and Shikhar Jaiswal
|
||||
* @author Kris Singh
|
||||
* @author Shikhar Jaiswal
|
||||
*
|
||||
* 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
|
||||
@@ -82,11 +83,6 @@ class BilinearInterpolation
|
||||
arma::Mat<eT>&& gradient,
|
||||
arma::Mat<eT>&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -114,10 +110,10 @@ class BilinearInterpolation
|
||||
size_t outColSize;
|
||||
//! Locally stored depth of the input.
|
||||
size_t depth;
|
||||
//! Locally stored number of input points.
|
||||
size_t batchSize;
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class BilinearInterpolation
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @file bilinear_interpolation_impl.hpp
|
||||
* @author Kris Singh and Shikhar Jaiswal
|
||||
* @author Kris Singh
|
||||
* @author Shikhar Jaiswal
|
||||
*
|
||||
* Implementation of the bilinear interpolation function as an individual layer.
|
||||
*
|
||||
@@ -26,7 +27,8 @@ BilinearInterpolation():
|
||||
inColSize(0),
|
||||
outRowSize(0),
|
||||
outColSize(0),
|
||||
depth(0)
|
||||
depth(0),
|
||||
batchSize(0)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -43,7 +45,8 @@ BilinearInterpolation(
|
||||
inColSize(inColSize),
|
||||
outRowSize(outRowSize),
|
||||
outColSize(outColSize),
|
||||
depth(depth)
|
||||
depth(depth),
|
||||
batchSize(0)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -53,20 +56,22 @@ template<typename eT>
|
||||
void BilinearInterpolation<InputDataType, OutputDataType>::Forward(
|
||||
const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
batchSize = input.n_cols;
|
||||
if (output.is_empty())
|
||||
output.set_size(outRowSize * outColSize * depth, 1);
|
||||
output.set_size(outRowSize * outColSize * depth, batchSize);
|
||||
else
|
||||
{
|
||||
assert(output.n_rows == outRowSize * outColSize * depth);
|
||||
assert(output.n_cols == 1);
|
||||
assert(output.n_cols == batchSize);
|
||||
}
|
||||
|
||||
assert(inRowSize >= 2);
|
||||
assert(inColSize >= 2);
|
||||
|
||||
arma::cube inputAsCube(input.memptr(), inRowSize, inColSize, depth);
|
||||
arma::cube outputAsCube(output.memptr(), outRowSize, outColSize, depth,
|
||||
false, true);
|
||||
arma::cube inputAsCube(const_cast<arma::Mat<eT>&&>(input).memptr(),
|
||||
inRowSize, inColSize, depth * batchSize, false, false);
|
||||
arma::cube outputAsCube(output.memptr(), outRowSize, outColSize,
|
||||
depth * batchSize, false, true);
|
||||
|
||||
double scaleRow = (double) inRowSize / (double) outRowSize;
|
||||
double scaleCol = (double) inColSize / (double) outColSize;
|
||||
@@ -97,7 +102,7 @@ void BilinearInterpolation<InputDataType, OutputDataType>::Forward(
|
||||
coeffs[2] = (1 - deltaR) * deltaC;
|
||||
coeffs[3] = deltaR * deltaC;
|
||||
|
||||
for (size_t k = 0; k < depth; k++)
|
||||
for (size_t k = 0; k < depth * batchSize; k++)
|
||||
{
|
||||
outputAsCube(i, j, k) = arma::accu(inputAsCube.slice(k).submat(
|
||||
rOrigin, cOrigin, rOrigin + 1, cOrigin + 1) % coeffs);
|
||||
@@ -114,19 +119,20 @@ void BilinearInterpolation<InputDataType, OutputDataType>::Backward(
|
||||
arma::Mat<eT>&& output)
|
||||
{
|
||||
if (output.is_empty())
|
||||
output.set_size(inRowSize * inColSize * depth, 1);
|
||||
output.set_size(inRowSize * inColSize * depth, batchSize);
|
||||
else
|
||||
{
|
||||
assert(output.n_rows == inRowSize * inColSize * depth);
|
||||
assert(output.n_cols == 1);
|
||||
assert(output.n_cols == batchSize);
|
||||
}
|
||||
|
||||
assert(outRowSize >= 2);
|
||||
assert(outColSize >= 2);
|
||||
|
||||
arma::cube gradientAsCube(gradient.memptr(), outRowSize, outColSize, depth);
|
||||
arma::cube outputAsCube(output.memptr(), inRowSize, inColSize, depth, false,
|
||||
true);
|
||||
arma::cube gradientAsCube(gradient.memptr(), outRowSize, outColSize,
|
||||
depth * batchSize, false, false);
|
||||
arma::cube outputAsCube(output.memptr(), inRowSize, inColSize,
|
||||
depth * batchSize, false, true);
|
||||
|
||||
if (gradient.n_elem == output.n_elem)
|
||||
{
|
||||
@@ -157,7 +163,7 @@ void BilinearInterpolation<InputDataType, OutputDataType>::Backward(
|
||||
coeffs[2] = (1 - deltaR) * deltaC;
|
||||
coeffs[3] = deltaR * deltaC;
|
||||
|
||||
for (size_t k = 0; k < depth; k++)
|
||||
for (size_t k = 0; k < depth * batchSize; k++)
|
||||
{
|
||||
outputAsCube(i, j, k) = arma::accu(gradientAsCube.slice(k).submat(
|
||||
rOrigin, cOrigin, rOrigin + 1, cOrigin + 1) % coeffs);
|
||||
|
||||
@@ -72,11 +72,6 @@ class ConcatPerformance
|
||||
const arma::Mat<eT>&& target,
|
||||
arma::Mat<eT>&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -103,9 +98,6 @@ class ConcatPerformance
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class ConcatPerformance
|
||||
|
||||
@@ -66,11 +66,6 @@ class Constant
|
||||
DataType&& /* gy */,
|
||||
DataType&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -100,9 +95,6 @@ class Constant
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class ConstantLayer
|
||||
|
||||
@@ -261,12 +261,15 @@ class Convolution
|
||||
}
|
||||
}
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
//! Locally-stored number of input channels.
|
||||
size_t inSize;
|
||||
|
||||
//! Locally-stored number of output units.
|
||||
//! Locally-stored number of output channels.
|
||||
size_t outSize;
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
size_t batchSize;
|
||||
|
||||
//! Locally-stored filter/kernel width.
|
||||
size_t kW;
|
||||
|
||||
|
||||
@@ -113,7 +113,9 @@ void Convolution<
|
||||
OutputDataType
|
||||
>::Forward(const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
inputTemp = arma::cube(input.memptr(), inputWidth, inputHeight, inSize);
|
||||
batchSize = input.n_cols;
|
||||
inputTemp = arma::cube(const_cast<arma::Mat<eT>&&>(input).memptr(),
|
||||
inputWidth, inputHeight, inSize * batchSize, false, false);
|
||||
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
@@ -123,32 +125,39 @@ void Convolution<
|
||||
size_t wConv = ConvOutSize(inputWidth, kW, dW, padW);
|
||||
size_t hConv = ConvOutSize(inputHeight, kH, dH, padH);
|
||||
|
||||
output.set_size(wConv * hConv * outSize, 1);
|
||||
outputTemp = arma::Cube<eT>(output.memptr(), wConv, hConv, outSize,
|
||||
false, false);
|
||||
output.set_size(wConv * hConv * outSize, batchSize);
|
||||
outputTemp = arma::Cube<eT>(output.memptr(), wConv, hConv,
|
||||
outSize * batchSize, false, false);
|
||||
outputTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> convOutput;
|
||||
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
ForwardConvolutionRule::Convolution(inputPaddedTemp.slice(inMap),
|
||||
weight.slice(outMapIdx), convOutput, dW, dH);
|
||||
ForwardConvolutionRule::Convolution(inputPaddedTemp.slice(inMap +
|
||||
batchCount * inSize), weight.slice(outMapIdx), convOutput, dW, dH);
|
||||
}
|
||||
else
|
||||
{
|
||||
ForwardConvolutionRule::Convolution(inputTemp.slice(inMap),
|
||||
weight.slice(outMapIdx), convOutput, dW, dH);
|
||||
ForwardConvolutionRule::Convolution(inputTemp.slice(inMap +
|
||||
batchCount * inSize), weight.slice(outMapIdx), convOutput, dW, dH);
|
||||
}
|
||||
|
||||
outputTemp.slice(outMap) += convOutput;
|
||||
}
|
||||
|
||||
outputTemp.slice(outMap) += bias(outMap);
|
||||
outputTemp.slice(outMap) += bias(outMap % outSize);
|
||||
}
|
||||
|
||||
outputWidth = outputTemp.n_rows;
|
||||
@@ -172,35 +181,40 @@ void Convolution<
|
||||
>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight, outSize,
|
||||
false, false);
|
||||
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight,
|
||||
outSize * batchSize, false, false);
|
||||
|
||||
g.set_size(inputTemp.n_rows * inputTemp.n_cols * inputTemp.n_slices, 1);
|
||||
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);
|
||||
gTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> rotatedFilter;
|
||||
arma::Mat<eT> output, rotatedFilter;
|
||||
Rotate180(weight.slice(outMapIdx), rotatedFilter);
|
||||
|
||||
arma::Mat<eT> output;
|
||||
BackwardConvolutionRule::Convolution(mappedError.slice(outMap),
|
||||
rotatedFilter, output, dW, dH);
|
||||
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
gTemp.slice(inMap) += output.submat(rotatedFilter.n_rows / 2,
|
||||
rotatedFilter.n_cols / 2,
|
||||
rotatedFilter.n_rows / 2 + gTemp.n_rows - 1,
|
||||
rotatedFilter.n_cols / 2 + gTemp.n_cols - 1);
|
||||
gTemp.slice(inMap + batchCount * inSize) += output.submat(padW, padH,
|
||||
padW + gTemp.n_rows - 1,
|
||||
padH + gTemp.n_cols - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gTemp.slice(inMap) += output;
|
||||
gTemp.slice(inMap + batchCount * inSize) += output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,61 +243,62 @@ void Convolution<
|
||||
if (padW != 0 && padH != 0)
|
||||
{
|
||||
mappedError = arma::cube(error.memptr(), outputWidth / padW,
|
||||
outputHeight / padH, outSize);
|
||||
outputHeight / padH, outSize * batchSize, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mappedError = arma::cube(error.memptr(), outputWidth,
|
||||
outputHeight, outSize);
|
||||
outputHeight, outSize * batchSize, false, false);
|
||||
}
|
||||
|
||||
gradient.set_size(weights.n_elem, 1);
|
||||
gradientTemp = arma::Cube<eT>(gradient.memptr(), weight.n_rows, weight.n_cols,
|
||||
weight.n_slices, false, false);
|
||||
gradientTemp = arma::Cube<eT>(gradient.memptr(), weight.n_rows,
|
||||
weight.n_cols, weight.n_slices, false, false);
|
||||
gradientTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
for (size_t inMap = 0, s = outMap; inMap < inSize; inMap++, outMapIdx++,
|
||||
s += outSize)
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
arma::Cube<eT> inputSlices;
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> inputSlice;
|
||||
if (padW != 0 || padH != 0)
|
||||
{
|
||||
inputSlices = inputPaddedTemp.slices(inMap, inMap);
|
||||
inputSlice = inputPaddedTemp.slice(inMap + batchCount * inSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
inputSlices = inputTemp.slices(inMap, inMap);
|
||||
inputSlice = inputTemp.slice(inMap + batchCount * inSize);
|
||||
}
|
||||
|
||||
arma::Cube<eT> deltaSlices = mappedError.slices(outMap, outMap);
|
||||
arma::Mat<eT> deltaSlice = mappedError.slice(outMap);
|
||||
|
||||
arma::Cube<eT> output;
|
||||
GradientConvolutionRule::Convolution(inputSlices, deltaSlices,
|
||||
arma::Mat<eT> output;
|
||||
GradientConvolutionRule::Convolution(inputSlice, deltaSlice,
|
||||
output, dW, dH);
|
||||
|
||||
if ((padW != 0 || padH != 0) &&
|
||||
(gradientTemp.n_rows < output.n_rows &&
|
||||
gradientTemp.n_cols < output.n_cols))
|
||||
{
|
||||
for (size_t i = 0; i < output.n_slices; i++)
|
||||
{
|
||||
gradientTemp.slice(s) += output.slice(i).submat(output.n_rows / 2,
|
||||
output.n_cols / 2,
|
||||
output.n_rows / 2 + gradientTemp.n_rows - 1,
|
||||
output.n_cols / 2 + gradientTemp.n_cols - 1);
|
||||
}
|
||||
gradientTemp.slice(outMapIdx) += output.submat(padW, padH,
|
||||
padW + gradientTemp.n_rows - 1,
|
||||
padH + gradientTemp.n_cols - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < output.n_slices; i++)
|
||||
gradientTemp.slice(s) += output.slice(i);
|
||||
gradientTemp.slice(outMapIdx) += output;
|
||||
}
|
||||
}
|
||||
|
||||
gradient.submat(weight.n_elem + outMap, 0, weight.n_elem + outMap, 0) =
|
||||
arma::accu(mappedError.slices(outMap, outMap));
|
||||
gradient.submat(weight.n_elem + (outMap % outSize), 0, weight.n_elem +
|
||||
(outMap % outSize), 0) = arma::accu(mappedError.slice(outMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +321,7 @@ void Convolution<
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(inSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(outSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(batchSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(kW);
|
||||
ar & BOOST_SERIALIZATION_NVP(kH);
|
||||
ar & BOOST_SERIALIZATION_NVP(dW);
|
||||
|
||||
@@ -120,11 +120,6 @@ class DropConnect
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return parameters; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -178,9 +173,6 @@ class DropConnect
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -80,11 +80,6 @@ class Dropout
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -120,9 +115,6 @@ class Dropout
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -144,11 +144,6 @@ class ELU
|
||||
template<typename DataType>
|
||||
void Backward(const DataType&& input, DataType&& gy, DataType&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -238,9 +233,6 @@ class ELU
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -138,11 +138,6 @@ class FastLSTM
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -254,9 +249,6 @@ class FastLSTM
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType grad;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -115,11 +115,6 @@ class FlexibleReLU
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return alpha; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -150,9 +145,6 @@ class FlexibleReLU
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -127,11 +127,6 @@ class Glimpse
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const {return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const {return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -393,9 +388,6 @@ class Glimpse
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -135,11 +135,6 @@ class GRU
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -246,9 +241,6 @@ class GRU
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class GRU
|
||||
|
||||
@@ -83,11 +83,6 @@ class HardTanH
|
||||
DataType&& gy,
|
||||
DataType&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -118,9 +113,6 @@ class HardTanH
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -60,11 +60,6 @@ class Join
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -91,9 +86,6 @@ class Join
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class Join
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "fast_lstm.hpp"
|
||||
#include "recurrent.hpp"
|
||||
#include "recurrent_attention.hpp"
|
||||
#include "reparametrization.hpp"
|
||||
#include "sequential.hpp"
|
||||
#include "subview.hpp"
|
||||
#include "concat.hpp"
|
||||
|
||||
@@ -121,11 +121,6 @@ class LayerNorm
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -181,9 +176,6 @@ class LayerNorm
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -104,6 +104,10 @@ HAS_MEM_FUNC(InputHeight, HasInputHeight);
|
||||
// can use with SFINAE to catch when a type has a Rho() function.
|
||||
HAS_MEM_FUNC(Rho, HasRho);
|
||||
|
||||
// This gives us a HasLoss<T, U> type (where U is a function pointer) we
|
||||
// can use with SFINAE to catch when a type has a Loss() function.
|
||||
HAS_MEM_FUNC(Loss, HasLoss);
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
#include <mlpack/methods/ann/layer/log_softmax.hpp>
|
||||
#include <mlpack/methods/ann/layer/lookup.hpp>
|
||||
#include <mlpack/methods/ann/layer/multiply_constant.hpp>
|
||||
#include <mlpack/methods/ann/layer/negative_log_likelihood.hpp>
|
||||
#include <mlpack/methods/ann/layer/max_pooling.hpp>
|
||||
#include <mlpack/methods/ann/layer/mean_pooling.hpp>
|
||||
#include <mlpack/methods/ann/layer/parametric_relu.hpp>
|
||||
#include <mlpack/methods/ann/layer/reinforce_normal.hpp>
|
||||
#include <mlpack/methods/ann/layer/reparametrization.hpp>
|
||||
#include <mlpack/methods/ann/layer/select.hpp>
|
||||
#include <mlpack/methods/ann/layer/subview.hpp>
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
#include <mlpack/methods/ann/convolution_rules/naive_convolution.hpp>
|
||||
#include <mlpack/methods/ann/convolution_rules/fft_convolution.hpp>
|
||||
|
||||
// Loss function modules.
|
||||
#include <mlpack/methods/ann/loss_functions/negative_log_likelihood.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann {
|
||||
|
||||
@@ -58,6 +61,11 @@ template<typename InputDataType, typename OutputDataType> class GRU;
|
||||
template<typename InputDataType, typename OutputDataType> class FastLSTM;
|
||||
template<typename InputDataType, typename OutputDataType> class VRClassReward;
|
||||
|
||||
template<typename InputDataType,
|
||||
typename OutputDataType
|
||||
>
|
||||
class Reparametrization;
|
||||
|
||||
template<typename InputDataType,
|
||||
typename OutputDataType,
|
||||
typename... CustomLayers
|
||||
@@ -179,6 +187,7 @@ using LayerTypes = boost::variant<
|
||||
Recurrent<arma::mat, arma::mat>*,
|
||||
RecurrentAttention<arma::mat, arma::mat>*,
|
||||
ReinforceNormal<arma::mat, arma::mat>*,
|
||||
Reparametrization<arma::mat, arma::mat>*,
|
||||
Select<arma::mat, arma::mat>*,
|
||||
Sequential<arma::mat, arma::mat>*,
|
||||
Subview<arma::mat, arma::mat>*,
|
||||
|
||||
@@ -46,7 +46,7 @@ class LeakyReLU
|
||||
public:
|
||||
/**
|
||||
* Create the LeakyReLU object using the specified parameters.
|
||||
* The non zero gradient can be adjusted by specifying tha parameter
|
||||
* The non zero gradient can be adjusted by specifying the parameter
|
||||
* alpha in the range 0 to 1. Default (alpha = 0.03)
|
||||
*
|
||||
* @param alpha Non zero gradient
|
||||
@@ -75,11 +75,6 @@ class LeakyReLU
|
||||
template<typename DataType>
|
||||
void Backward(const DataType&& input, DataType&& gy, DataType&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -157,9 +152,6 @@ class LeakyReLU
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ class Linear
|
||||
//! Locally-stored weight object.
|
||||
OutputDataType weights;
|
||||
|
||||
//! Locally-stored weight paramters.
|
||||
//! Locally-stored weight parameters.
|
||||
OutputDataType weight;
|
||||
|
||||
//! Locally-stored bias term parameters.
|
||||
|
||||
@@ -65,11 +65,6 @@ class LogSoftMax
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -90,9 +85,6 @@ class LogSoftMax
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class LogSoftmax
|
||||
|
||||
@@ -85,11 +85,6 @@ class Lookup
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -127,9 +122,6 @@ class Lookup
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class Lookup
|
||||
|
||||
@@ -136,11 +136,6 @@ class LSTM
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -206,9 +201,6 @@ class LSTM
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType grad;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -94,11 +94,6 @@ class MaxPooling
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -193,12 +188,15 @@ class MaxPooling
|
||||
}
|
||||
}
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
//! Locally-stored number of input channels.
|
||||
size_t inSize;
|
||||
|
||||
//! Locally-stored number of output units.
|
||||
//! Locally-stored number of output channels.
|
||||
size_t outSize;
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
size_t batchSize;
|
||||
|
||||
//! Locally-stored width of the pooling window.
|
||||
size_t kW;
|
||||
|
||||
@@ -253,9 +251,6 @@ class MaxPooling
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ MaxPooling<InputDataType, OutputDataType>::MaxPooling(
|
||||
inputHeight(0),
|
||||
outputWidth(0),
|
||||
outputHeight(0),
|
||||
batchSize(0),
|
||||
inSize(0),
|
||||
outSize(0),
|
||||
deterministic(false)
|
||||
{
|
||||
// Nothing to do here.
|
||||
@@ -53,8 +56,10 @@ template<typename eT>
|
||||
void MaxPooling<InputDataType, OutputDataType>::Forward(
|
||||
const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
const size_t slices = input.n_elem / (inputWidth * inputHeight);
|
||||
inputTemp = arma::cube(input.memptr(), inputWidth, inputHeight, slices);
|
||||
batchSize = input.n_cols;
|
||||
inSize = input.n_elem / (inputWidth * inputHeight * batchSize);
|
||||
inputTemp = arma::cube(const_cast<arma::Mat<eT>&&>(input).memptr(),
|
||||
inputWidth, inputHeight, batchSize * inSize, false, false);
|
||||
|
||||
if (floor)
|
||||
{
|
||||
@@ -70,7 +75,7 @@ void MaxPooling<InputDataType, OutputDataType>::Forward(
|
||||
}
|
||||
|
||||
outputTemp = arma::zeros<arma::Cube<eT> >(outputWidth, outputHeight,
|
||||
slices);
|
||||
batchSize * inSize);
|
||||
|
||||
if (!deterministic)
|
||||
{
|
||||
@@ -102,11 +107,12 @@ void MaxPooling<InputDataType, OutputDataType>::Forward(
|
||||
}
|
||||
}
|
||||
|
||||
output = arma::Mat<eT>(outputTemp.memptr(), outputTemp.n_elem, 1);
|
||||
output = arma::Mat<eT>(outputTemp.memptr(), outputTemp.n_elem / batchSize,
|
||||
batchSize);
|
||||
|
||||
outputWidth = outputTemp.n_rows;
|
||||
outputHeight = outputTemp.n_cols;
|
||||
outSize = slices;
|
||||
outSize = batchSize * inSize;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
@@ -115,7 +121,7 @@ void MaxPooling<InputDataType, OutputDataType>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
arma::cube mappedError = arma::cube(gy.memptr(), outputWidth,
|
||||
outputHeight, outSize);
|
||||
outputHeight, outSize, false, false);
|
||||
|
||||
gTemp = arma::zeros<arma::cube>(inputTemp.n_rows,
|
||||
inputTemp.n_cols, inputTemp.n_slices);
|
||||
@@ -128,7 +134,7 @@ void MaxPooling<InputDataType, OutputDataType>::Backward(
|
||||
|
||||
poolingIndices.pop_back();
|
||||
|
||||
g = arma::mat(gTemp.memptr(), gTemp.n_elem, 1);
|
||||
g = arma::mat(gTemp.memptr(), gTemp.n_elem / batchSize, batchSize);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
@@ -141,6 +147,7 @@ void MaxPooling<InputDataType, OutputDataType>::serialize(
|
||||
ar & BOOST_SERIALIZATION_NVP(kH);
|
||||
ar & BOOST_SERIALIZATION_NVP(dW);
|
||||
ar & BOOST_SERIALIZATION_NVP(dH);
|
||||
ar & BOOST_SERIALIZATION_NVP(batchSize);
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
|
||||
@@ -74,11 +74,6 @@ class MeanPooling
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -177,12 +172,15 @@ class MeanPooling
|
||||
}
|
||||
}
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
//! Locally-stored number of input channels.
|
||||
size_t inSize;
|
||||
|
||||
//! Locally-stored number of output units.
|
||||
//! Locally-stored number of output channels.
|
||||
size_t outSize;
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
size_t batchSize;
|
||||
|
||||
//! Locally-stored width of the pooling window.
|
||||
size_t kW;
|
||||
|
||||
@@ -234,9 +232,6 @@ class MeanPooling
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class MeanPooling
|
||||
|
||||
@@ -43,7 +43,10 @@ MeanPooling<InputDataType, OutputDataType>::MeanPooling(
|
||||
reset(false),
|
||||
floor(floor),
|
||||
deterministic(false),
|
||||
offset(0)
|
||||
offset(0),
|
||||
batchSize(0),
|
||||
inSize(0),
|
||||
outSize(0)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -53,8 +56,10 @@ template<typename eT>
|
||||
void MeanPooling<InputDataType, OutputDataType>::Forward(
|
||||
const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
size_t slices = input.n_elem / (inputWidth * inputHeight);
|
||||
inputTemp = arma::cube(input.memptr(), inputWidth, inputHeight, slices);
|
||||
batchSize = input.n_cols;
|
||||
inSize = input.n_elem / (inputWidth * inputHeight * batchSize);
|
||||
inputTemp = arma::cube(const_cast<arma::Mat<eT>&&>(input).memptr(),
|
||||
inputWidth, inputHeight, batchSize * inSize, false, false);
|
||||
|
||||
if (floor)
|
||||
{
|
||||
@@ -72,16 +77,17 @@ void MeanPooling<InputDataType, OutputDataType>::Forward(
|
||||
}
|
||||
|
||||
outputTemp = arma::zeros<arma::Cube<eT> >(outputWidth, outputHeight,
|
||||
slices);
|
||||
batchSize * inSize);
|
||||
|
||||
for (size_t s = 0; s < inputTemp.n_slices; s++)
|
||||
Pooling(inputTemp.slice(s), outputTemp.slice(s));
|
||||
|
||||
output = arma::Mat<eT>(outputTemp.memptr(), outputTemp.n_elem, 1);
|
||||
output = arma::Mat<eT>(outputTemp.memptr(), outputTemp.n_elem / batchSize,
|
||||
batchSize);
|
||||
|
||||
outputWidth = outputTemp.n_rows;
|
||||
outputHeight = outputTemp.n_cols;
|
||||
outSize = slices;
|
||||
outSize = batchSize * inSize;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
@@ -92,7 +98,7 @@ void MeanPooling<InputDataType, OutputDataType>::Backward(
|
||||
arma::Mat<eT>&& g)
|
||||
{
|
||||
arma::cube mappedError = arma::cube(gy.memptr(), outputWidth,
|
||||
outputHeight, outSize);
|
||||
outputHeight, outSize, false, false);
|
||||
|
||||
gTemp = arma::zeros<arma::cube>(inputTemp.n_rows,
|
||||
inputTemp.n_cols, inputTemp.n_slices);
|
||||
@@ -102,7 +108,7 @@ void MeanPooling<InputDataType, OutputDataType>::Backward(
|
||||
Unpooling(inputTemp.slice(s), mappedError.slice(s), gTemp.slice(s));
|
||||
}
|
||||
|
||||
g = arma::mat(gTemp.memptr(), gTemp.n_elem, 1);
|
||||
g = arma::mat(gTemp.memptr(), gTemp.n_elem / batchSize, batchSize);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
@@ -115,6 +121,7 @@ void MeanPooling<InputDataType, OutputDataType>::serialize(
|
||||
ar & BOOST_SERIALIZATION_NVP(kH);
|
||||
ar & BOOST_SERIALIZATION_NVP(dW);
|
||||
ar & BOOST_SERIALIZATION_NVP(dH);
|
||||
ar & BOOST_SERIALIZATION_NVP(batchSize);
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
|
||||
@@ -60,11 +60,6 @@ class MultiplyConstant
|
||||
template<typename DataType>
|
||||
void Backward(const DataType&& /* input */, DataType&& gy, DataType&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -88,9 +83,6 @@ class MultiplyConstant
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class MultiplyConstant
|
||||
|
||||
@@ -46,8 +46,9 @@ class MultiplyMerge
|
||||
* Create the MultiplyMerge object using the specified parameters.
|
||||
*
|
||||
* @param model Expose all the network modules.
|
||||
* @param run Call the Forward/Backward method before the output is merged.
|
||||
*/
|
||||
MultiplyMerge(const bool model = false);
|
||||
MultiplyMerge(const bool model = false, const bool run = true);
|
||||
|
||||
//! Destructor to release allocated memory.
|
||||
~MultiplyMerge();
|
||||
@@ -60,7 +61,7 @@ class MultiplyMerge
|
||||
* @param output Resulting output activation.
|
||||
*/
|
||||
template<typename InputType, typename OutputType>
|
||||
void Forward(const InputType&& /* input */, OutputType&& output);
|
||||
void Forward(InputType&& /* input */, OutputType&& output);
|
||||
|
||||
/**
|
||||
* Ordinary feed backward pass of a neural network, calculating the function
|
||||
@@ -77,19 +78,16 @@ class MultiplyMerge
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
* Calculate the gradient using the output delta and the input activation.
|
||||
*
|
||||
* @param layer The Layer to be added to the model.
|
||||
* @param input The input parameter used for calculating the gradient.
|
||||
* @param error The calculated error.
|
||||
* @param gradient The calculated gradient.
|
||||
*/
|
||||
void Add(LayerTypes<CustomLayers...> layer) { network.push_back(layer); }
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
*
|
||||
* @param layer The Layer to be added to the model.
|
||||
*/
|
||||
template<typename LayerType>
|
||||
void Add(const LayerType& layer) { network.push_back(new LayerType(layer)); }
|
||||
template<typename eT>
|
||||
void Gradient(arma::Mat<eT>&& input,
|
||||
arma::Mat<eT>&& error,
|
||||
arma::Mat<eT>&& gradient);
|
||||
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
@@ -99,10 +97,12 @@ class MultiplyMerge
|
||||
template <class LayerType, class... Args>
|
||||
void Add(Args... args) { network.push_back(new LayerType(args...)); }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
/*
|
||||
* Add a new module to the model.
|
||||
*
|
||||
* @param layer The Layer to be added to the model.
|
||||
*/
|
||||
void Add(LayerTypes<CustomLayers...> layer) { network.push_back(layer); }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
@@ -114,6 +114,11 @@ class MultiplyMerge
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
//! Get the gradient.
|
||||
OutputDataType const& Gradient() const { return gradient; }
|
||||
//! Modify the gradient.
|
||||
OutputDataType& Gradient() { return gradient; }
|
||||
|
||||
//! Return the model modules.
|
||||
std::vector<LayerTypes<CustomLayers...> >& Model()
|
||||
{
|
||||
@@ -125,6 +130,11 @@ class MultiplyMerge
|
||||
return empty;
|
||||
}
|
||||
|
||||
//! Get the parameters.
|
||||
OutputDataType const& Parameters() const { return weights; }
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return weights; }
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
@@ -135,6 +145,10 @@ class MultiplyMerge
|
||||
//! Parameter which indicates if the modules should be exposed.
|
||||
bool model;
|
||||
|
||||
//! Parameter which indicates if the Forward/Backward method should be called
|
||||
//! before merging the output.
|
||||
bool run;
|
||||
|
||||
//! We need this to know whether we should delete the layer in the destructor.
|
||||
bool ownsLayer;
|
||||
|
||||
@@ -156,11 +170,14 @@ class MultiplyMerge
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
//! Locally-stored weight object.
|
||||
OutputDataType weights;
|
||||
}; // class MultiplyMerge
|
||||
|
||||
} // namespace ann
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
// In case it hasn't yet been included.
|
||||
#include "multiply_merge.hpp"
|
||||
|
||||
#include "../visitor/forward_visitor.hpp"
|
||||
#include "../visitor/backward_visitor.hpp"
|
||||
#include "../visitor/gradient_visitor.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
MultiplyMerge<InputDataType, OutputDataType, CustomLayers...>::MultiplyMerge(
|
||||
const bool model) : model(model), ownsLayer(!model)
|
||||
const bool model, const bool run) :
|
||||
model(model), run(run), ownsLayer(!model)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -42,10 +47,19 @@ template <typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
template<typename InputType, typename OutputType>
|
||||
void MultiplyMerge<InputDataType, OutputDataType, CustomLayers...>::Forward(
|
||||
const InputType&& /* input */, OutputType&& output)
|
||||
InputType&& input, OutputType&& output)
|
||||
{
|
||||
output = boost::apply_visitor(outputParameterVisitor, network.front());
|
||||
if (run)
|
||||
{
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(ForwardVisitor(std::move(input), std::move(
|
||||
boost::apply_visitor(outputParameterVisitor, network[i]))),
|
||||
network[i]);
|
||||
}
|
||||
}
|
||||
|
||||
output = boost::apply_visitor(outputParameterVisitor, network.front());
|
||||
for (size_t i = 1; i < network.size(); ++i)
|
||||
{
|
||||
output %= boost::apply_visitor(outputParameterVisitor, network[i]);
|
||||
@@ -58,7 +72,41 @@ template<typename eT>
|
||||
void MultiplyMerge<InputDataType, OutputDataType, CustomLayers...>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
g = gy;
|
||||
if (run)
|
||||
{
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(BackwardVisitor(std::move(boost::apply_visitor(
|
||||
outputParameterVisitor, network[i])), std::move(gy), std::move(
|
||||
boost::apply_visitor(deltaVisitor, network[i]))), network[i]);
|
||||
}
|
||||
|
||||
g = boost::apply_visitor(deltaVisitor, network[0]);
|
||||
for (size_t i = 1; i < network.size(); ++i)
|
||||
{
|
||||
g += boost::apply_visitor(deltaVisitor, network[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
g = gy;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
typename... CustomLayers>
|
||||
template<typename eT>
|
||||
void MultiplyMerge<InputDataType, OutputDataType, CustomLayers...>::Gradient(
|
||||
arma::Mat<eT>&& input,
|
||||
arma::Mat<eT>&& error,
|
||||
arma::Mat<eT>&& /* gradient */ )
|
||||
{
|
||||
if (run)
|
||||
{
|
||||
for (size_t i = 0; i < network.size(); ++i)
|
||||
{
|
||||
boost::apply_visitor(GradientVisitor(std::move(input), std::move(error)),
|
||||
network[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType,
|
||||
|
||||
@@ -99,11 +99,6 @@ class PReLU
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return alpha; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -188,9 +183,6 @@ class PReLU
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -121,11 +121,6 @@ class Recurrent
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return parameters; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -215,9 +210,6 @@ class Recurrent
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -124,11 +124,6 @@ class RecurrentAttention
|
||||
//! Modify the parameters.
|
||||
OutputDataType& Parameters() { return parameters; }
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -225,9 +220,6 @@ class RecurrentAttention
|
||||
//! Locally-stored gradient object.
|
||||
OutputDataType gradient;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ Recurrent<InputDataType, OutputDataType, CustomLayers...>::Recurrent(
|
||||
ownsLayer(true)
|
||||
{
|
||||
initialModule = new Sequential<>();
|
||||
mergeModule = new AddMerge<>(false);
|
||||
mergeModule = new AddMerge<>(false, false);
|
||||
recurrentModule = new Sequential<>(false);
|
||||
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(inputModule),
|
||||
@@ -261,7 +261,7 @@ void Recurrent<InputDataType, OutputDataType, CustomLayers...>::serialize(
|
||||
if (Archive::is_loading::value)
|
||||
{
|
||||
initialModule = new Sequential<>();
|
||||
mergeModule = new AddMerge<>(false);
|
||||
mergeModule = new AddMerge<>(false, false);
|
||||
recurrentModule = new Sequential<>(false);
|
||||
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(inputModule),
|
||||
|
||||
@@ -63,11 +63,6 @@ class ReinforceNormal
|
||||
template<typename DataType>
|
||||
void Backward(const DataType&& input, DataType&& /* gy */, DataType&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -104,9 +99,6 @@ class ReinforceNormal
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* @file reparametrization.hpp
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* Definition of the Reparametrization layer class which samples from a gaussian
|
||||
* distribution.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_ANN_LAYER_REPARAMETRIZATION_HPP
|
||||
#define MLPACK_METHODS_ANN_LAYER_REPARAMETRIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
#include "layer_types.hpp"
|
||||
#include "../activation_functions/softplus_function.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
/**
|
||||
* Implementation of the Reparametrization layer class. This layer samples from the
|
||||
* given parameters of a normal distribution.
|
||||
*
|
||||
* @tparam InputDataType Type of the input data (arma::colvec, arma::mat,
|
||||
* arma::sp_mat or arma::cube).
|
||||
* @tparam OutputDataType Type of the output data (arma::colvec, arma::mat,
|
||||
* arma::sp_mat or arma::cube).
|
||||
*/
|
||||
template <
|
||||
typename InputDataType = arma::mat,
|
||||
typename OutputDataType = arma::mat
|
||||
>
|
||||
class Reparametrization
|
||||
{
|
||||
public:
|
||||
//! Create the Reparametrization object.
|
||||
Reparametrization();
|
||||
|
||||
/**
|
||||
* Create the Reparametrization layer object using the specified sample vector size.
|
||||
*
|
||||
* @param layerSize The number of output units.
|
||||
* @param stochastic Whether we want random sample or constant.
|
||||
* @param includeKl Whether we want to include KL loss in backward function.
|
||||
*/
|
||||
Reparametrization(const size_t latentSize,
|
||||
const bool stochastic = true,
|
||||
const bool includeKl = true);
|
||||
|
||||
/**
|
||||
* Ordinary feed forward pass of a neural network, evaluating the function
|
||||
* f(x) by propagating the activity forward through f.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
* @param output Resulting output activation.
|
||||
*/
|
||||
template<typename eT>
|
||||
void Forward(const arma::Mat<eT>&& input, arma::Mat<eT>&& output);
|
||||
|
||||
/**
|
||||
* Ordinary feed backward pass of a neural network, calculating the function
|
||||
* f(x) by propagating x backwards trough f. Using the results from the feed
|
||||
* forward pass.
|
||||
*
|
||||
* @param input The propagated input activation.
|
||||
* @param gy The backpropagated error.
|
||||
* @param g The calculated gradient.
|
||||
*/
|
||||
template<typename eT>
|
||||
void Backward(const arma::Mat<eT>&& input,
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
/**
|
||||
* Ordinary feed forward pass of a neural network, evaluating
|
||||
* Kullback–Leibler divergence between a normal distribution
|
||||
* and the standard normal.
|
||||
*
|
||||
* @param input Input data used for evaluating the specified function.
|
||||
*/
|
||||
template<typename InputType>
|
||||
double klForward(const InputType&& input);
|
||||
|
||||
/**
|
||||
* Ordinary feed backward pass of a neural network, evaluating the backward
|
||||
* pass of Kullback–Leibler divergence. Using the results from the
|
||||
* KL divergence feed forward pass.
|
||||
*
|
||||
* @param output The calculated gradient of KL divergence.
|
||||
*/
|
||||
template<typename OutputType>
|
||||
void klBackward(OutputType&& output);
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
OutputDataType& OutputParameter() { return outputParameter; }
|
||||
|
||||
//! Get the delta.
|
||||
OutputDataType const& Delta() const { return delta; }
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
//! Get the output size.
|
||||
size_t const& OutputSize() const { return latentSize; }
|
||||
//! Modify the output size.
|
||||
size_t& OutputSize() { return latentSize; }
|
||||
|
||||
//! Get the KL divergence with standard normal.
|
||||
double Loss()
|
||||
{
|
||||
OutputDataType input = join_cols(stdDev, mean);
|
||||
return klForward(std::move(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored number of output units.
|
||||
size_t latentSize;
|
||||
|
||||
//! If false, sample will be constant.
|
||||
bool stochastic;
|
||||
|
||||
//! If false, KL error will not be included in Backward function.
|
||||
bool includeKl;
|
||||
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored current gaussian sample.
|
||||
OutputDataType gaussianSample;
|
||||
|
||||
//! Locally-stored current mean.
|
||||
OutputDataType mean;
|
||||
|
||||
//! Locally-stored pre standard deviation.
|
||||
//! After softplus activation gives standard deviation.
|
||||
OutputDataType preStdDev;
|
||||
|
||||
//! Locally-stored current standard deviation.
|
||||
OutputDataType stdDev;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class Reparametrization
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "reparametrization_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @file reparametrization_impl.hpp
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* Implementation of the Reparametrization layer class which samples from a
|
||||
* gaussian distribution.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_ANN_LAYER_REPARAMETRIZATION_IMPL_HPP
|
||||
#define MLPACK_METHODS_ANN_LAYER_REPARAMETRIZATION_IMPL_HPP
|
||||
|
||||
// In case it hasn't yet been included.
|
||||
#include "reparametrization.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann /** Artificial Neural Network. */ {
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
Reparametrization<InputDataType, OutputDataType>::Reparametrization() :
|
||||
latentSize(0),
|
||||
stochastic(true),
|
||||
includeKl(true)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
template <typename InputDataType, typename OutputDataType>
|
||||
Reparametrization<InputDataType, OutputDataType>::Reparametrization(
|
||||
const size_t latentSize,
|
||||
const bool stochastic,
|
||||
const bool includeKl) :
|
||||
latentSize(latentSize),
|
||||
stochastic(stochastic),
|
||||
includeKl(includeKl)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename eT>
|
||||
void Reparametrization<InputDataType, OutputDataType>::Forward(
|
||||
const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
if (input.n_rows != 2 * latentSize)
|
||||
{
|
||||
Log::Fatal << "The output size of layer before the Reparametrization "
|
||||
<< "layer should be 2 * latent size of the Reparametrization layer!"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
mean = input.submat(latentSize, 0, 2 * latentSize - 1, input.n_cols - 1);
|
||||
preStdDev = input.submat(0, 0, latentSize - 1, input.n_cols - 1);
|
||||
|
||||
if (stochastic)
|
||||
gaussianSample = arma::randn<arma::Mat<eT> >(latentSize, input.n_cols);
|
||||
else
|
||||
gaussianSample = arma::ones<arma::Mat<eT> >(latentSize, input.n_cols) * 0.7;
|
||||
|
||||
SoftplusFunction::Fn(preStdDev, stdDev);
|
||||
output = mean + stdDev % gaussianSample;
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename eT>
|
||||
void Reparametrization<InputDataType, OutputDataType>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
SoftplusFunction::Deriv(preStdDev, g);
|
||||
|
||||
if (includeKl)
|
||||
{
|
||||
arma::Mat<eT> klBack;
|
||||
klBackward(std::move(klBack));
|
||||
g = join_cols(gy % std::move(gaussianSample) % g, gy) + std::move(klBack);
|
||||
}
|
||||
else
|
||||
g = join_cols(gy % std::move(gaussianSample) % g, gy);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename InputType>
|
||||
double Reparametrization<InputDataType, OutputDataType>::klForward(
|
||||
const InputType&& input)
|
||||
{
|
||||
stdDev = input.submat(0, 0, latentSize - 1, input.n_cols - 1);
|
||||
mean = input.submat(latentSize, 0, 2 * latentSize - 1, input.n_cols - 1);
|
||||
|
||||
return -0.5 * arma::accu(2 * arma::log(stdDev) -
|
||||
arma::pow(stdDev, 2) - arma::pow(mean, 2) + 1);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename OutputType>
|
||||
void Reparametrization<InputDataType, OutputDataType>::klBackward(
|
||||
OutputType&& output)
|
||||
{
|
||||
SoftplusFunction::Deriv(preStdDev, output);
|
||||
output = join_cols((-1 / stdDev + stdDev) % output, mean);
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename Archive>
|
||||
void Reparametrization<InputDataType, OutputDataType>::serialize(
|
||||
Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(latentSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(stochastic);
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -64,11 +64,6 @@ class Select
|
||||
arma::Mat<eT>&& gy,
|
||||
arma::Mat<eT>&& g);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -95,9 +90,6 @@ class Select
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class Select
|
||||
|
||||
@@ -124,6 +124,7 @@ class Sequential
|
||||
//! Modify the initial point for the optimization.
|
||||
arma::mat& Parameters() { return parameters; }
|
||||
|
||||
//! Get the input parameter.
|
||||
arma::mat const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
arma::mat& InputParameter() { return inputParameter; }
|
||||
|
||||
@@ -119,11 +119,6 @@ class Subview
|
||||
g = gy;
|
||||
}
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType const& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType const& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -166,9 +161,6 @@ class Subview
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class Subview
|
||||
|
||||
@@ -263,12 +263,15 @@ class TransposedConvolution
|
||||
}
|
||||
}
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
//! Locally-stored number of input channels.
|
||||
size_t inSize;
|
||||
|
||||
//! Locally-stored number of output units.
|
||||
//! Locally-stored number of output channels.
|
||||
size_t outSize;
|
||||
|
||||
//! Locally-stored number of input units.
|
||||
size_t batchSize;
|
||||
|
||||
//! Locally-stored filter/kernel width.
|
||||
size_t kW;
|
||||
|
||||
|
||||
@@ -114,30 +114,39 @@ void TransposedConvolution<
|
||||
OutputDataType
|
||||
>::Forward(const arma::Mat<eT>&& input, arma::Mat<eT>&& output)
|
||||
{
|
||||
inputTemp = arma::cube(input.memptr(), inputWidth, inputHeight, inSize);
|
||||
batchSize = input.n_cols;
|
||||
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);
|
||||
|
||||
output.set_size(outputWidth * outputHeight * outSize, 1);
|
||||
output.set_size(outputWidth * outputHeight * outSize, batchSize);
|
||||
outputTemp = arma::Cube<eT>(output.memptr(), outputWidth, outputHeight,
|
||||
outSize, false, false);
|
||||
outSize * batchSize, false, false);
|
||||
outputTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> convOutput, rotatedFilter;
|
||||
Rotate180(weight.slice(outMapIdx), rotatedFilter);
|
||||
|
||||
BackwardConvolutionRule::Convolution(inputTemp.slice(inMap),
|
||||
rotatedFilter, convOutput, 1, 1);
|
||||
BackwardConvolutionRule::Convolution(inputTemp.slice(inMap +
|
||||
batchCount * inSize), rotatedFilter, convOutput, 1, 1);
|
||||
|
||||
outputTemp.slice(outMap) += convOutput;
|
||||
}
|
||||
|
||||
outputTemp.slice(outMap) += bias(outMap);
|
||||
outputTemp.slice(outMap) += bias(outMap % outSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,16 +167,23 @@ void TransposedConvolution<
|
||||
>::Backward(
|
||||
const arma::Mat<eT>&& /* input */, arma::Mat<eT>&& gy, arma::Mat<eT>&& g)
|
||||
{
|
||||
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight, outSize,
|
||||
false, false);
|
||||
|
||||
g.set_size(inputTemp.n_rows * inputTemp.n_cols * inputTemp.n_slices, 1);
|
||||
arma::cube mappedError(gy.memptr(), outputWidth, outputHeight,
|
||||
outSize * batchSize, false, false);
|
||||
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);
|
||||
|
||||
gTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
for (size_t inMap = 0; inMap < inSize; inMap++, outMapIdx++)
|
||||
{
|
||||
arma::Mat<eT> output;
|
||||
@@ -175,7 +191,7 @@ void TransposedConvolution<
|
||||
ForwardConvolutionRule::Convolution(mappedError.slice(outMap),
|
||||
weight.slice(outMapIdx), output, 1, 1);
|
||||
|
||||
gTemp.slice(inMap) += output;
|
||||
gTemp.slice(inMap + batchCount * inSize) += output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,31 +216,36 @@ void TransposedConvolution<
|
||||
arma::Mat<eT>&& gradient)
|
||||
{
|
||||
arma::cube mappedError(error.memptr(), outputWidth,
|
||||
outputHeight, outSize, false, false);
|
||||
outputHeight, outSize * batchSize, false, false);
|
||||
|
||||
gradient.set_size(weights.n_elem, 1);
|
||||
gradientTemp = arma::Cube<eT>(gradient.memptr(), weight.n_rows, weight.n_cols,
|
||||
weight.n_slices, false, false);
|
||||
gradientTemp = arma::Cube<eT>(gradient.memptr(), weight.n_rows,
|
||||
weight.n_cols, weight.n_slices, false, false);
|
||||
gradientTemp.zeros();
|
||||
|
||||
for (size_t outMap = 0, outMapIdx = 0; outMap < outSize; outMap++)
|
||||
for (size_t outMap = 0, outMapIdx = 0, batchCount = 0; outMap <
|
||||
outSize * batchSize; outMap++)
|
||||
{
|
||||
for (size_t inMap = 0, s = outMap; inMap < inSize; inMap++, outMapIdx++,
|
||||
s += outSize)
|
||||
if (outMap != 0 && outMap % outSize == 0)
|
||||
{
|
||||
arma::Cube<eT> inputSlices, output;
|
||||
inputSlices = inputTemp.slices(inMap, inMap);
|
||||
arma::Cube<eT> deltaSlices = mappedError.slices(outMap, outMap);
|
||||
|
||||
GradientConvolutionRule::Convolution(deltaSlices, inputSlices,
|
||||
output, 1, 1);
|
||||
|
||||
for (size_t i = 0; i < output.n_slices; i++)
|
||||
gradientTemp.slice(s) += output.slice(i);
|
||||
batchCount++;
|
||||
outMapIdx = 0;
|
||||
}
|
||||
|
||||
gradient.submat(weight.n_elem + outMap, 0, weight.n_elem + outMap, 0) =
|
||||
arma::accu(mappedError.slices(outMap, 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);
|
||||
|
||||
GradientConvolutionRule::Convolution(deltaSlice, inputSlice,
|
||||
output, 1, 1);
|
||||
|
||||
gradientTemp.slice(outMapIdx) += output;
|
||||
}
|
||||
|
||||
gradient.submat(weight.n_elem + (outMap % outSize), 0, weight.n_elem +
|
||||
(outMap % outSize), 0) = arma::accu(mappedError.slices(outMap, outMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +268,7 @@ void TransposedConvolution<
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(inSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(outSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(batchSize);
|
||||
ar & BOOST_SERIALIZATION_NVP(kW);
|
||||
ar & BOOST_SERIALIZATION_NVP(kH);
|
||||
ar & BOOST_SERIALIZATION_NVP(dW);
|
||||
|
||||
@@ -73,11 +73,6 @@ class VRClassReward
|
||||
const TargetType&& target,
|
||||
OutputType&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const {return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const {return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
@@ -127,9 +122,6 @@ class VRClassReward
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ set(SOURCES
|
||||
kl_divergence_impl.hpp
|
||||
mean_squared_error.hpp
|
||||
mean_squared_error_impl.hpp
|
||||
negative_log_likelihood.hpp
|
||||
negative_log_likelihood_impl.hpp
|
||||
sigmoid_cross_entropy_error.hpp
|
||||
sigmoid_cross_entropy_error_impl.hpp
|
||||
)
|
||||
|
||||
@@ -62,21 +62,11 @@ class CrossEntropyError
|
||||
const TargetType&& target,
|
||||
OutputType&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
OutputDataType& OutputParameter() { return outputParameter; }
|
||||
|
||||
//! Get the delta.
|
||||
OutputDataType& Delta() const { return delta; }
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
//! Get the epsilon.
|
||||
double Eps() const { return eps; }
|
||||
//! Modify the epsilon.
|
||||
@@ -89,12 +79,6 @@ class CrossEntropyError
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -74,21 +74,11 @@ class KLDivergence
|
||||
const TargetType&& target,
|
||||
OutputType&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
OutputDataType& OutputParameter() { return outputParameter; }
|
||||
|
||||
//! Get the delta.
|
||||
OutputDataType& Delta() const { return delta; }
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
//! Get the value of takeMean.
|
||||
bool TakeMean() const { return takeMean; }
|
||||
//! Modify the value of takeMean.
|
||||
@@ -101,12 +91,6 @@ class KLDivergence
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
|
||||
@@ -59,21 +59,11 @@ class MeanSquaredError
|
||||
const TargetType&& target,
|
||||
OutputType&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
OutputDataType& OutputParameter() { return outputParameter; }
|
||||
|
||||
//! Get the delta.
|
||||
OutputDataType& Delta() const { return delta; }
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
/**
|
||||
* Serialize the layer
|
||||
*/
|
||||
@@ -81,12 +71,6 @@ class MeanSquaredError
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class MeanSquaredError
|
||||
|
||||
@@ -78,21 +78,11 @@ class SigmoidCrossEntropyError
|
||||
const TargetType&& target,
|
||||
OutputType&& output);
|
||||
|
||||
//! Get the input parameter.
|
||||
InputDataType& InputParameter() const { return inputParameter; }
|
||||
//! Modify the input parameter.
|
||||
InputDataType& InputParameter() { return inputParameter; }
|
||||
|
||||
//! Get the output parameter.
|
||||
OutputDataType& OutputParameter() const { return outputParameter; }
|
||||
//! Modify the output parameter.
|
||||
OutputDataType& OutputParameter() { return outputParameter; }
|
||||
|
||||
//! Get the delta.
|
||||
OutputDataType& Delta() const { return delta; }
|
||||
//! Modify the delta.
|
||||
OutputDataType& Delta() { return delta; }
|
||||
|
||||
/**
|
||||
* Serialize the layer.
|
||||
*/
|
||||
@@ -100,12 +90,6 @@ class SigmoidCrossEntropyError
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Locally-stored delta object.
|
||||
OutputDataType delta;
|
||||
|
||||
//! Locally-stored input parameter object.
|
||||
InputDataType inputParameter;
|
||||
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
}; // class SigmoidCrossEntropy
|
||||
|
||||
@@ -25,6 +25,8 @@ set(SOURCES
|
||||
gradient_zero_visitor_impl.hpp
|
||||
load_output_parameter_visitor.hpp
|
||||
load_output_parameter_visitor_impl.hpp
|
||||
loss_visitor.hpp
|
||||
loss_visitor_impl.hpp
|
||||
output_height_visitor.hpp
|
||||
output_height_visitor_impl.hpp
|
||||
output_parameter_visitor.hpp
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @file loss_visitor.hpp
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* This file provides an abstraction for the Loss() function for different
|
||||
* layers and automatically directs any parameter to the right layer type.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_ANN_VISITOR_LOSS_VISITOR_HPP
|
||||
#define MLPACK_METHODS_ANN_VISITOR_LOSS_VISITOR_HPP
|
||||
|
||||
#include <mlpack/methods/ann/layer/layer_traits.hpp>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann {
|
||||
|
||||
/**
|
||||
* LossVisitor exposes the Loss() method of the given module.
|
||||
*/
|
||||
class LossVisitor : public boost::static_visitor<double>
|
||||
{
|
||||
public:
|
||||
//! Return the Loss.
|
||||
template<typename LayerType>
|
||||
double operator()(LayerType* layer) const;
|
||||
|
||||
private:
|
||||
//! Return 0 if the module doesn't implement the Loss() or Model() function.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
!HasLoss<T, double(T::*)()>::value &&
|
||||
!HasModelCheck<T>::value, double>::type
|
||||
LayerLoss(T* layer) const;
|
||||
|
||||
//! Return the output height if the module implements the Loss() function.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
HasLoss<T, double(T::*)()>::value &&
|
||||
!HasModelCheck<T>::value, double>::type
|
||||
LayerLoss(T* layer) const;
|
||||
|
||||
//! Return the loss if the module implements the Model() function.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
!HasLoss<T, double(T::*)()>::value &&
|
||||
HasModelCheck<T>::value, double>::type
|
||||
LayerLoss(T* layer) const;
|
||||
|
||||
//! Return the loss if the module implements the Model() or loss() function.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
HasLoss<T, double(T::*)()>::value &&
|
||||
HasModelCheck<T>::value, double>::type
|
||||
LayerLoss(T* layer) const;
|
||||
};
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "loss_visitor_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @file loss_visitor_impl.hpp
|
||||
* @author Atharva Khandait
|
||||
*
|
||||
* Implementation of the Loss() function layer abstraction.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_ANN_VISITOR_LOSS_VISITOR_IMPL_HPP
|
||||
#define MLPACK_METHODS_ANN_VISITOR_LOSS_VISITOR_IMPL_HPP
|
||||
|
||||
// In case it hasn't been included yet.
|
||||
#include "loss_visitor.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace ann {
|
||||
|
||||
//! LossVisitor visitor class.
|
||||
template<typename LayerType>
|
||||
inline double LossVisitor::operator()(LayerType* layer) const
|
||||
{
|
||||
return LayerLoss(layer);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<
|
||||
!HasLoss<T, double(T::*)()>::value &&
|
||||
!HasModelCheck<T>::value, double>::type
|
||||
LossVisitor::LayerLoss(T* /* layer */) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<
|
||||
HasLoss<T, double(T::*)()>::value &&
|
||||
!HasModelCheck<T>::value, double>::type
|
||||
LossVisitor::LayerLoss(T* layer) const
|
||||
{
|
||||
return layer->Loss();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<
|
||||
!HasLoss<T, double(T::*)()>::value &&
|
||||
HasModelCheck<T>::value, double>::type
|
||||
LossVisitor::LayerLoss(T* layer) const
|
||||
{
|
||||
for (size_t i = 0; i < layer->Model().size(); ++i)
|
||||
{
|
||||
double Loss = boost::apply_visitor(LossVisitor(),
|
||||
layer->Model()[layer->Model().size() - 1 - i]);
|
||||
|
||||
if (Loss != 0)
|
||||
{
|
||||
return Loss;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<
|
||||
HasLoss<T, double(T::*)()>::value &&
|
||||
HasModelCheck<T>::value, double>::type
|
||||
LossVisitor::LayerLoss(T* layer) const
|
||||
{
|
||||
double Loss = layer->Loss();
|
||||
|
||||
if (Loss == 0)
|
||||
{
|
||||
for (size_t i = 0; i < layer->Model().size(); ++i)
|
||||
{
|
||||
Loss = boost::apply_visitor(LossVisitor(),
|
||||
layer->Model()[layer->Model().size() - 1 - i]);
|
||||
|
||||
if (Loss != 0)
|
||||
{
|
||||
return Loss;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Loss;
|
||||
}
|
||||
|
||||
} // namespace ann
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -22,7 +22,7 @@ namespace mlpack {
|
||||
namespace ann {
|
||||
|
||||
/**
|
||||
* OutputWidthVisitor exposes the OutputHeight() method of the given module.
|
||||
* OutputHeightVisitor exposes the OutputHeight() method of the given module.
|
||||
*/
|
||||
class OutputHeightVisitor : public boost::static_visitor<size_t>
|
||||
{
|
||||
@@ -55,7 +55,7 @@ class OutputHeightVisitor : public boost::static_visitor<size_t>
|
||||
HasModelCheck<T>::value, size_t>::type
|
||||
LayerOutputHeight(T* layer) const;
|
||||
|
||||
//! Return the output height if the module implement the Model() or
|
||||
//! Return the output height if the module implements the Model() or
|
||||
//! InputHeight() function.
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
set(SOURCES
|
||||
cf.hpp
|
||||
cf_impl.hpp
|
||||
cf.cpp
|
||||
svd_wrapper.hpp
|
||||
svd_wrapper_impl.hpp
|
||||
)
|
||||
|
||||
add_subdirectory(normalization)
|
||||
add_subdirectory(decomposition_policies)
|
||||
|
||||
# Add directory name to sources.
|
||||
set(DIR_SRCS)
|
||||
foreach(file ${SOURCES})
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
/**
|
||||
* @file cf.cpp
|
||||
* @author Mudit Raj Gupta
|
||||
* @author Sumedh Ghaisas
|
||||
*
|
||||
* Collaborative Filtering.
|
||||
*
|
||||
* Implementation of CF class to perform Collaborative Filtering on the
|
||||
* specified data set.
|
||||
*
|
||||
* 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 "cf.hpp"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
// Default CFType constructor.
|
||||
CFType::CFType(const size_t numUsersForSimilarity,
|
||||
const size_t rank) :
|
||||
numUsersForSimilarity(numUsersForSimilarity),
|
||||
rank(rank)
|
||||
{
|
||||
// Validate neighbourhood size.
|
||||
if (numUsersForSimilarity < 1)
|
||||
{
|
||||
Log::Warn << "CFType::CFType(): neighbourhood size should be > 0 ("
|
||||
<< numUsersForSimilarity << " given). Setting value to 5.\n";
|
||||
// Set default value of 5.
|
||||
this->numUsersForSimilarity = 5;
|
||||
}
|
||||
}
|
||||
|
||||
void CFType::GetRecommendations(const size_t numRecs,
|
||||
arma::Mat<size_t>& recommendations)
|
||||
{
|
||||
// Generate list of users. Maybe it would be more efficient to pass an empty
|
||||
// users list, and then have the other overload of GetRecommendations() assume
|
||||
// that if users is empty, then recommendations should be generated for all
|
||||
// users?
|
||||
arma::Col<size_t> users = arma::linspace<arma::Col<size_t> >(0,
|
||||
cleanedData.n_cols - 1, cleanedData.n_cols);
|
||||
|
||||
// Call the main overload for recommendations.
|
||||
GetRecommendations(numRecs, recommendations, users);
|
||||
}
|
||||
|
||||
void CFType::GetRecommendations(const size_t numRecs,
|
||||
arma::Mat<size_t>& recommendations,
|
||||
const arma::Col<size_t>& users)
|
||||
{
|
||||
// We want to avoid calculating the full rating matrix, so we will do nearest
|
||||
// neighbor search only on the H matrix, using the observation that if the
|
||||
// rating matrix X = W*H, then d(X.col(i), X.col(j)) = d(W H.col(i), W
|
||||
// H.col(j)). This can be seen as nearest neighbor search on the H matrix
|
||||
// with the Mahalanobis distance where M^{-1} = W^T W. So, we'll decompose
|
||||
// M^{-1} = L L^T (the Cholesky decomposition), and then multiply H by L^T.
|
||||
// Then we can perform nearest neighbor search.
|
||||
arma::mat l = arma::chol(w.t() * w);
|
||||
arma::mat stretchedH = l * h; // Due to the Armadillo API, l is L^T.
|
||||
|
||||
// Now, we will use the decomposed w and h matrices to estimate what the user
|
||||
// would have rated items as, and then pick the best items.
|
||||
|
||||
// Temporarily store feature vector of queried users.
|
||||
arma::mat query(stretchedH.n_rows, users.n_elem);
|
||||
|
||||
// Select feature vectors of queried users.
|
||||
for (size_t i = 0; i < users.n_elem; i++)
|
||||
query.col(i) = stretchedH.col(users(i));
|
||||
|
||||
// Temporary storage for neighborhood of the queried users.
|
||||
arma::Mat<size_t> neighborhood;
|
||||
|
||||
// Calculate the neighborhood of the queried users. Note that the query user
|
||||
// is part of the neighborhood---this is intentional. We want to use an
|
||||
// average of both the query user and the local neighborhood of the query
|
||||
// user.
|
||||
// The neighbor search technique should be a template parameter.
|
||||
neighbor::KNN a(stretchedH);
|
||||
arma::mat resultingDistances; // Temporary storage.
|
||||
a.Search(query, numUsersForSimilarity, neighborhood, resultingDistances);
|
||||
|
||||
// Generate recommendations for each query user by finding the maximum numRecs
|
||||
// elements in the averages matrix.
|
||||
recommendations.set_size(numRecs, users.n_elem);
|
||||
arma::mat values(numRecs, users.n_elem);
|
||||
recommendations.fill(SIZE_MAX);
|
||||
values.fill(DBL_MAX);
|
||||
|
||||
for (size_t i = 0; i < users.n_elem; i++)
|
||||
{
|
||||
// First, calculate average of neighborhood values.
|
||||
arma::vec averages;
|
||||
averages.zeros(cleanedData.n_rows);
|
||||
|
||||
for (size_t j = 0; j < neighborhood.n_rows; ++j)
|
||||
averages += w * h.col(neighborhood(j, i));
|
||||
averages /= neighborhood.n_rows;
|
||||
|
||||
// Let's build the list of candidate recomendations for the given user.
|
||||
// Default candidate: the smallest possible value and invalid item number.
|
||||
const Candidate def = std::make_pair(-DBL_MAX, cleanedData.n_rows);
|
||||
std::vector<Candidate> vect(numRecs, def);
|
||||
typedef std::priority_queue<Candidate, std::vector<Candidate>, CandidateCmp>
|
||||
CandidateList;
|
||||
CandidateList pqueue(CandidateCmp(), std::move(vect));
|
||||
|
||||
// Look through the averages column corresponding to the current user.
|
||||
for (size_t j = 0; j < averages.n_rows; ++j)
|
||||
{
|
||||
// Ensure that the user hasn't already rated the item.
|
||||
if (cleanedData(j, users(i)) != 0.0)
|
||||
continue; // The user already rated the item.
|
||||
|
||||
// Is the estimated value better than the worst candidate?
|
||||
if (averages[j] > pqueue.top().first)
|
||||
{
|
||||
Candidate c = std::make_pair(averages[j], j);
|
||||
pqueue.pop();
|
||||
pqueue.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t p = 1; p <= numRecs; p++)
|
||||
{
|
||||
recommendations(numRecs - p, i) = pqueue.top().second;
|
||||
values(numRecs - p, i) = pqueue.top().first;
|
||||
pqueue.pop();
|
||||
}
|
||||
|
||||
// If we were not able to come up with enough recommendations, issue a
|
||||
// warning.
|
||||
if (recommendations(numRecs - 1, i) == def.second)
|
||||
Log::Warn << "Could not provide " << numRecs << " recommendations "
|
||||
<< "for user " << users(i) << " (not enough un-rated items)!"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Predict the rating for a single user/item combination.
|
||||
double CFType::Predict(const size_t user, const size_t item) const
|
||||
{
|
||||
// First, we need to find the nearest neighbors of the given user.
|
||||
// We'll use the same technique as for GetRecommendations().
|
||||
|
||||
// We want to avoid calculating the full rating matrix, so we will do nearest
|
||||
// neighbor search only on the H matrix, using the observation that if the
|
||||
// rating matrix X = W*H, then d(X.col(i), X.col(j)) = d(W H.col(i), W
|
||||
// H.col(j)). This can be seen as nearest neighbor search on the H matrix
|
||||
// with the Mahalanobis distance where M^{-1} = W^T W. So, we'll decompose
|
||||
// M^{-1} = L L^T (the Cholesky decomposition), and then multiply H by L^T.
|
||||
// Then we can perform nearest neighbor search.
|
||||
arma::mat l = arma::chol(w.t() * w);
|
||||
arma::mat stretchedH = l * h; // Due to the Armadillo API, l is L^T.
|
||||
|
||||
// Now, we will use the decomposed w and h matrices to estimate what the user
|
||||
// would have rated items as, and then pick the best items.
|
||||
|
||||
// Temporarily store feature vector of queried users.
|
||||
arma::mat query = stretchedH.col(user);
|
||||
|
||||
// Temporary storage for neighborhood of the queried users.
|
||||
arma::Mat<size_t> neighborhood;
|
||||
|
||||
// Calculate the neighborhood of the queried users.
|
||||
// This should be a templatized option.
|
||||
neighbor::KNN a(stretchedH, neighbor::SINGLE_TREE_MODE);
|
||||
arma::mat resultingDistances; // Temporary storage.
|
||||
|
||||
a.Search(query, numUsersForSimilarity, neighborhood, resultingDistances);
|
||||
|
||||
double rating = 0; // We'll take the average of neighborhood values.
|
||||
|
||||
for (size_t j = 0; j < neighborhood.n_rows; ++j)
|
||||
rating += arma::as_scalar(w.row(item) * h.col(neighborhood(j, 0)));
|
||||
rating /= neighborhood.n_rows;
|
||||
|
||||
return rating;
|
||||
}
|
||||
|
||||
// Predict the rating for a group of user/item combinations.
|
||||
void CFType::Predict(const arma::Mat<size_t>& combinations,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
// First, for nearest neighbor search, stretch the H matrix.
|
||||
arma::mat l = arma::chol(w.t() * w);
|
||||
arma::mat stretchedH = l * h; // Due to the Armadillo API, l is L^T.
|
||||
|
||||
// Now, we must determine those query indices we need to find the nearest
|
||||
// neighbors for. This is easiest if we just sort the combinations matrix.
|
||||
arma::Mat<size_t> sortedCombinations(combinations.n_rows,
|
||||
combinations.n_cols);
|
||||
arma::uvec ordering = arma::sort_index(combinations.row(0).t());
|
||||
for (size_t i = 0; i < ordering.n_elem; ++i)
|
||||
sortedCombinations.col(i) = combinations.col(ordering[i]);
|
||||
|
||||
// Now, we have to get the list of unique users we will be searching for.
|
||||
arma::Col<size_t> users = arma::unique(combinations.row(0).t());
|
||||
|
||||
// Assemble our query matrix from the stretchedH matrix.
|
||||
arma::mat queries(stretchedH.n_rows, users.n_elem);
|
||||
for (size_t i = 0; i < queries.n_cols; ++i)
|
||||
queries.col(i) = stretchedH.col(users[i]);
|
||||
|
||||
// Now calculate the neighborhood of these users.
|
||||
neighbor::KNN a(stretchedH);
|
||||
arma::mat distances;
|
||||
arma::Mat<size_t> neighborhood;
|
||||
|
||||
a.Search(queries, numUsersForSimilarity, neighborhood, distances);
|
||||
|
||||
// Now that we have the neighborhoods we need, calculate the predictions.
|
||||
predictions.set_size(combinations.n_cols);
|
||||
|
||||
size_t user = 0; // Cumulative user count, because we are doing it in order.
|
||||
for (size_t i = 0; i < sortedCombinations.n_cols; ++i)
|
||||
{
|
||||
// Could this be made faster by calculating dot products for multiple items
|
||||
// at once?
|
||||
double rating = 0.0;
|
||||
|
||||
// Map the combination's user to the user ID used for kNN.
|
||||
while (users[user] < sortedCombinations(0, i))
|
||||
++user;
|
||||
|
||||
for (size_t j = 0; j < neighborhood.n_rows; ++j)
|
||||
rating += arma::as_scalar(w.row(sortedCombinations(1, i)) *
|
||||
h.col(neighborhood(j, user)));
|
||||
rating /= neighborhood.n_rows;
|
||||
|
||||
predictions(ordering[i]) = rating;
|
||||
}
|
||||
}
|
||||
|
||||
void CFType::CleanData(const arma::mat& data, arma::sp_mat& cleanedData)
|
||||
{
|
||||
// Generate list of locations for batch insert constructor for sparse
|
||||
// matrices.
|
||||
arma::umat locations(2, data.n_cols);
|
||||
arma::vec values(data.n_cols);
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
{
|
||||
// We have to transpose it because items are rows, and users are columns.
|
||||
locations(1, i) = ((arma::uword) data(0, i));
|
||||
locations(0, i) = ((arma::uword) data(1, i));
|
||||
values(i) = data(2, i);
|
||||
if (values(i) == 0)
|
||||
Log::Warn << "User rating of 0 ignored for user " << locations(1, i)
|
||||
<< ", item " << locations(0, i) << "." << std::endl;
|
||||
}
|
||||
|
||||
// Find maximum user and item IDs.
|
||||
const size_t maxItemID = (size_t) max(locations.row(0)) + 1;
|
||||
const size_t maxUserID = (size_t) max(locations.row(1)) + 1;
|
||||
|
||||
// Fill sparse matrix.
|
||||
cleanedData = arma::sp_mat(locations, values, maxItemID, maxUserID);
|
||||
}
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <mlpack/methods/amf/amf.hpp>
|
||||
#include <mlpack/methods/amf/update_rules/nmf_als.hpp>
|
||||
#include <mlpack/methods/amf/termination_policies/simple_residue_termination.hpp>
|
||||
#include <mlpack/methods/cf/normalization/no_normalization.hpp>
|
||||
#include <mlpack/methods/cf/decomposition_policies/nmf_method.hpp>
|
||||
#include <set>
|
||||
#include <map>
|
||||
@@ -40,7 +41,7 @@ namespace cf /** Collaborative filtering. **/ {
|
||||
* extern arma::Col<size_t> users; // users seeking recommendations
|
||||
* arma::Mat<size_t> recommendations; // Recommendations
|
||||
*
|
||||
* CFType cf(data); // Default options.
|
||||
* CFType<> cf(data); // Default options.
|
||||
*
|
||||
* // Generate 10 recommendations for all users.
|
||||
* cf.GetRecommendations(10, recommendations);
|
||||
@@ -56,9 +57,11 @@ namespace cf /** Collaborative filtering. **/ {
|
||||
* are in a matrix that holds doubles, should hold integer (or size_t) values.
|
||||
* The user and item indices are assumed to start at 0.
|
||||
*
|
||||
* @tparam DecompositionPolicy The algorithm to use to decompose
|
||||
* the rating matrix (a W and H matrix).
|
||||
* @tparam NormalizationType The type of normalization performed on raw data.
|
||||
* Data is normalized before calling Train() method. Predicted rating is
|
||||
* denormalized before return.
|
||||
*/
|
||||
template<typename NormalizationType = NoNormalization>
|
||||
class CFType
|
||||
{
|
||||
public:
|
||||
@@ -66,8 +69,7 @@ class CFType
|
||||
* Initialize the CFType object without performing any factorization. Be sure to
|
||||
* call Train() before calling GetRecommendations() or any other functions!
|
||||
*/
|
||||
CFType(const size_t numUsersForSimilarity = 5,
|
||||
const size_t rank = 0);
|
||||
CFType(const size_t numUsersForSimilarity = 5, const size_t rank = 0);
|
||||
|
||||
/**
|
||||
* Initialize the CFType object using any decomposition method, immediately
|
||||
@@ -80,6 +82,12 @@ class CFType
|
||||
* where each column corresponds to a (user, item, rating) entry in the
|
||||
* matrix or a sparse matrix representing (user, item) table.
|
||||
*
|
||||
* @tparam MatType The type of input matrix, which is expected to be either
|
||||
* arma::mat (table of (user, item, rating)) or arma::sp_mat (sparse
|
||||
* rating matrix where row is item and column is user).
|
||||
* @tparam DecompositionPolicy The algorithm to use to decompose
|
||||
* the rating matrix (a W and H matrix).
|
||||
*
|
||||
* @param data Data matrix: dense matrix (coordinate lists)
|
||||
* or sparse matrix(cleaned).
|
||||
* @param decomposition Instantiated DecompositionPolicy object.
|
||||
@@ -103,6 +111,9 @@ class CFType
|
||||
* parameters that have already been set for the model (specifically, the rank
|
||||
* parameter), and optionally, using the given DecompositionPolicy.
|
||||
*
|
||||
* @tparam DecompositionPolicy The algorithm to use to decompose
|
||||
* the rating matrix (a W and H matrix).
|
||||
*
|
||||
* @param data Input dataset; dense matrix (coordinate lists).
|
||||
* @param decomposition Instantiated DecompositionPolicy object.
|
||||
* @param maxIterations Maximum number of iterations.
|
||||
@@ -121,6 +132,9 @@ class CFType
|
||||
* parameters that have already been set for the model (specifically, the
|
||||
* rank parameter), and optionally, using the given DecompositionPolicy.
|
||||
*
|
||||
* @tparam DecompositionPolicy The algorithm to use to decompose
|
||||
* the rating matrix (a W and H matrix).
|
||||
*
|
||||
* @param data Input dataset; sparse matrix (user item table).
|
||||
* @param decomposition Instantiated DecompositionPolicy object.
|
||||
* @param maxIterations Maximum number of iterations.
|
||||
@@ -234,6 +248,8 @@ class CFType
|
||||
arma::mat h;
|
||||
//! Cleaned data matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
//! Data normalization object.
|
||||
NormalizationType normalization;
|
||||
|
||||
//! Candidate represents a possible recommendation (value, item).
|
||||
typedef std::pair<double, size_t> Candidate;
|
||||
|
||||
@@ -22,17 +22,35 @@
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
// Default CF constructor.
|
||||
template<typename NormalizationType>
|
||||
CFType<NormalizationType>::CFType(const size_t numUsersForSimilarity,
|
||||
const size_t rank) :
|
||||
numUsersForSimilarity(numUsersForSimilarity),
|
||||
rank(rank)
|
||||
{
|
||||
// Validate neighbourhood size.
|
||||
if (numUsersForSimilarity < 1)
|
||||
{
|
||||
Log::Warn << "CFType::CFType(): neighbourhood size should be > 0 ("
|
||||
<< numUsersForSimilarity << " given). Setting value to 5.\n";
|
||||
// Set default value of 5.
|
||||
this->numUsersForSimilarity = 5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the CF object using an instantiated decomposition policy.
|
||||
*/
|
||||
template<typename NormalizationType>
|
||||
template<typename MatType, typename DecompositionPolicy>
|
||||
CFType::CFType(const MatType& data,
|
||||
DecompositionPolicy& decomposition,
|
||||
const size_t numUsersForSimilarity,
|
||||
const size_t rank,
|
||||
const size_t maxIterations,
|
||||
const double minResidue,
|
||||
const bool mit) :
|
||||
CFType<NormalizationType>::CFType(const MatType& data,
|
||||
DecompositionPolicy& decomposition,
|
||||
const size_t numUsersForSimilarity,
|
||||
const size_t rank,
|
||||
const size_t maxIterations,
|
||||
const double minResidue,
|
||||
const bool mit) :
|
||||
numUsersForSimilarity(numUsersForSimilarity),
|
||||
rank(rank)
|
||||
{
|
||||
@@ -49,14 +67,18 @@ CFType::CFType(const MatType& data,
|
||||
}
|
||||
|
||||
// Train when data is given in dense matrix form.
|
||||
template<typename NormalizationType>
|
||||
template<typename DecompositionPolicy>
|
||||
void CFType::Train(const arma::mat& data,
|
||||
DecompositionPolicy& decomposition,
|
||||
const size_t maxIterations,
|
||||
const double minResidue,
|
||||
const bool mit)
|
||||
void CFType<NormalizationType>::Train(const arma::mat& data,
|
||||
DecompositionPolicy& decomposition,
|
||||
const size_t maxIterations,
|
||||
const double minResidue,
|
||||
const bool mit)
|
||||
{
|
||||
CleanData(data, cleanedData);
|
||||
// Make a copy of data before performing normalization.
|
||||
arma::mat normalizedData(data);
|
||||
normalization.Normalize(normalizedData);
|
||||
CleanData(normalizedData, cleanedData);
|
||||
|
||||
// Check if the user wanted us to choose a rank for them.
|
||||
if (rank == 0)
|
||||
@@ -76,20 +98,24 @@ void CFType::Train(const arma::mat& data,
|
||||
// Decompose the data matrix (which is in coordinate list form) to user and
|
||||
// data matrices.
|
||||
Timer::Start("cf_factorization");
|
||||
decomposition.Apply(data, cleanedData, rank, w,
|
||||
decomposition.Apply(normalizedData, cleanedData, rank, w,
|
||||
h, maxIterations, minResidue, mit);
|
||||
Timer::Stop("cf_factorization");
|
||||
}
|
||||
|
||||
// Train when data is given as sparse matrix of user item table.
|
||||
template<typename NormalizationType>
|
||||
template<typename DecompositionPolicy>
|
||||
void CFType::Train(const arma::sp_mat& data,
|
||||
DecompositionPolicy& decomposition,
|
||||
const size_t maxIterations,
|
||||
const double minResidue,
|
||||
const bool mit)
|
||||
void CFType<NormalizationType>::Train(const arma::sp_mat& data,
|
||||
DecompositionPolicy& decomposition,
|
||||
const size_t maxIterations,
|
||||
const double minResidue,
|
||||
const bool mit)
|
||||
{
|
||||
// data is not used in the following decomposition.Apply() method, so we only
|
||||
// need to Normalize cleanedData.
|
||||
cleanedData = data;
|
||||
normalization.Normalize(cleanedData);
|
||||
|
||||
// Check if the user wanted us to choose a rank for them.
|
||||
if (rank == 0)
|
||||
@@ -114,9 +140,261 @@ void CFType::Train(const arma::sp_mat& data,
|
||||
Timer::Stop("cf_factorization");
|
||||
}
|
||||
|
||||
template<typename NormalizationType>
|
||||
void CFType<NormalizationType>::GetRecommendations(
|
||||
const size_t numRecs,
|
||||
arma::Mat<size_t>& recommendations)
|
||||
{
|
||||
// Generate list of users. Maybe it would be more efficient to pass an empty
|
||||
// users list, and then have the other overload of GetRecommendations() assume
|
||||
// that if users is empty, then recommendations should be generated for all
|
||||
// users?
|
||||
arma::Col<size_t> users = arma::linspace<arma::Col<size_t> >(0,
|
||||
cleanedData.n_cols - 1, cleanedData.n_cols);
|
||||
|
||||
// Call the main overload for recommendations.
|
||||
GetRecommendations(numRecs, recommendations, users);
|
||||
}
|
||||
|
||||
template<typename NormalizationType>
|
||||
void CFType<NormalizationType>::GetRecommendations(
|
||||
const size_t numRecs,
|
||||
arma::Mat<size_t>& recommendations,
|
||||
const arma::Col<size_t>& users)
|
||||
{
|
||||
// We want to avoid calculating the full rating matrix, so we will do nearest
|
||||
// neighbor search only on the H matrix, using the observation that if the
|
||||
// rating matrix X = W*H, then d(X.col(i), X.col(j)) = d(W H.col(i), W
|
||||
// H.col(j)). This can be seen as nearest neighbor search on the H matrix
|
||||
// with the Mahalanobis distance where M^{-1} = W^T W. So, we'll decompose
|
||||
// M^{-1} = L L^T (the Cholesky decomposition), and then multiply H by L^T.
|
||||
// Then we can perform nearest neighbor search.
|
||||
arma::mat l = arma::chol(w.t() * w);
|
||||
arma::mat stretchedH = l * h; // Due to the Armadillo API, l is L^T.
|
||||
|
||||
// Now, we will use the decomposed w and h matrices to estimate what the user
|
||||
// would have rated items as, and then pick the best items.
|
||||
|
||||
// Temporarily store feature vector of queried users.
|
||||
arma::mat query(stretchedH.n_rows, users.n_elem);
|
||||
|
||||
// Select feature vectors of queried users.
|
||||
for (size_t i = 0; i < users.n_elem; i++)
|
||||
query.col(i) = stretchedH.col(users(i));
|
||||
|
||||
// Temporary storage for neighborhood of the queried users.
|
||||
arma::Mat<size_t> neighborhood;
|
||||
|
||||
// Calculate the neighborhood of the queried users. Note that the query user
|
||||
// is part of the neighborhood---this is intentional. We want to use an
|
||||
// average of both the query user and the local neighborhood of the query
|
||||
// user.
|
||||
// The neighbor search technique should be a template parameter.
|
||||
neighbor::KNN a(stretchedH);
|
||||
arma::mat resultingDistances; // Temporary storage.
|
||||
a.Search(query, numUsersForSimilarity, neighborhood, resultingDistances);
|
||||
|
||||
// Generate recommendations for each query user by finding the maximum numRecs
|
||||
// elements in the averages matrix.
|
||||
recommendations.set_size(numRecs, users.n_elem);
|
||||
arma::mat values(numRecs, users.n_elem);
|
||||
recommendations.fill(SIZE_MAX);
|
||||
values.fill(DBL_MAX);
|
||||
|
||||
for (size_t i = 0; i < users.n_elem; i++)
|
||||
{
|
||||
// First, calculate average of neighborhood values.
|
||||
arma::vec averages;
|
||||
averages.zeros(cleanedData.n_rows);
|
||||
|
||||
for (size_t j = 0; j < neighborhood.n_rows; ++j)
|
||||
averages += w * h.col(neighborhood(j, i));
|
||||
averages /= neighborhood.n_rows;
|
||||
|
||||
// Let's build the list of candidate recomendations for the given user.
|
||||
// Default candidate: the smallest possible value and invalid item number.
|
||||
const Candidate def = std::make_pair(-DBL_MAX, cleanedData.n_rows);
|
||||
std::vector<Candidate> vect(numRecs, def);
|
||||
typedef std::priority_queue<Candidate, std::vector<Candidate>, CandidateCmp>
|
||||
CandidateList;
|
||||
CandidateList pqueue(CandidateCmp(), std::move(vect));
|
||||
|
||||
// Look through the averages column corresponding to the current user.
|
||||
for (size_t j = 0; j < averages.n_rows; ++j)
|
||||
{
|
||||
// Ensure that the user hasn't already rated the item.
|
||||
// The algorithm omits rating of zero. Thus, when normalizing original
|
||||
// ratings in Normalize(), if normalized rating equals zero, it is set
|
||||
// to the smallest positive double value.
|
||||
if (cleanedData(j, users(i)) != 0.0)
|
||||
continue; // The user already rated the item.
|
||||
|
||||
// Is the estimated value better than the worst candidate?
|
||||
// Denormalize rating before comparison.
|
||||
double realRating = normalization.Denormalize(users(i), j, averages[j]);
|
||||
if (realRating > pqueue.top().first)
|
||||
{
|
||||
Candidate c = std::make_pair(realRating, j);
|
||||
pqueue.pop();
|
||||
pqueue.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t p = 1; p <= numRecs; p++)
|
||||
{
|
||||
recommendations(numRecs - p, i) = pqueue.top().second;
|
||||
values(numRecs - p, i) = pqueue.top().first;
|
||||
pqueue.pop();
|
||||
}
|
||||
|
||||
// If we were not able to come up with enough recommendations, issue a
|
||||
// warning.
|
||||
if (recommendations(numRecs - 1, i) == def.second)
|
||||
Log::Warn << "Could not provide " << numRecs << " recommendations "
|
||||
<< "for user " << users(i) << " (not enough un-rated items)!"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Predict the rating for a single user/item combination.
|
||||
template<typename NormalizationType>
|
||||
double CFType<NormalizationType>::Predict(const size_t user,
|
||||
const size_t item) const
|
||||
{
|
||||
// First, we need to find the nearest neighbors of the given user.
|
||||
// We'll use the same technique as for GetRecommendations().
|
||||
|
||||
// We want to avoid calculating the full rating matrix, so we will do nearest
|
||||
// neighbor search only on the H matrix, using the observation that if the
|
||||
// rating matrix X = W*H, then d(X.col(i), X.col(j)) = d(W H.col(i), W
|
||||
// H.col(j)). This can be seen as nearest neighbor search on the H matrix
|
||||
// with the Mahalanobis distance where M^{-1} = W^T W. So, we'll decompose
|
||||
// M^{-1} = L L^T (the Cholesky decomposition), and then multiply H by L^T.
|
||||
// Then we can perform nearest neighbor search.
|
||||
arma::mat l = arma::chol(w.t() * w);
|
||||
arma::mat stretchedH = l * h; // Due to the Armadillo API, l is L^T.
|
||||
|
||||
// Now, we will use the decomposed w and h matrices to estimate what the user
|
||||
// would have rated items as, and then pick the best items.
|
||||
|
||||
// Temporarily store feature vector of queried users.
|
||||
arma::mat query = stretchedH.col(user);
|
||||
|
||||
// Temporary storage for neighborhood of the queried users.
|
||||
arma::Mat<size_t> neighborhood;
|
||||
|
||||
// Calculate the neighborhood of the queried users.
|
||||
// This should be a templatized option.
|
||||
neighbor::KNN a(stretchedH, neighbor::SINGLE_TREE_MODE);
|
||||
arma::mat resultingDistances; // Temporary storage.
|
||||
|
||||
a.Search(query, numUsersForSimilarity, neighborhood, resultingDistances);
|
||||
|
||||
double rating = 0; // We'll take the average of neighborhood values.
|
||||
|
||||
for (size_t j = 0; j < neighborhood.n_rows; ++j)
|
||||
rating += arma::as_scalar(w.row(item) * h.col(neighborhood(j, 0)));
|
||||
rating /= neighborhood.n_rows;
|
||||
|
||||
// Denormalize rating and return.
|
||||
double realRating = normalization.Denormalize(user, item, rating);
|
||||
return realRating;
|
||||
}
|
||||
|
||||
// Predict the rating for a group of user/item combinations.
|
||||
template<typename NormalizationType>
|
||||
void CFType<NormalizationType>::Predict(const arma::Mat<size_t>& combinations,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
// First, for nearest neighbor search, stretch the H matrix.
|
||||
arma::mat l = arma::chol(w.t() * w);
|
||||
arma::mat stretchedH = l * h; // Due to the Armadillo API, l is L^T.
|
||||
|
||||
// Now, we must determine those query indices we need to find the nearest
|
||||
// neighbors for. This is easiest if we just sort the combinations matrix.
|
||||
arma::Mat<size_t> sortedCombinations(combinations.n_rows,
|
||||
combinations.n_cols);
|
||||
arma::uvec ordering = arma::sort_index(combinations.row(0).t());
|
||||
for (size_t i = 0; i < ordering.n_elem; ++i)
|
||||
sortedCombinations.col(i) = combinations.col(ordering[i]);
|
||||
|
||||
// Now, we have to get the list of unique users we will be searching for.
|
||||
arma::Col<size_t> users = arma::unique(combinations.row(0).t());
|
||||
|
||||
// Assemble our query matrix from the stretchedH matrix.
|
||||
arma::mat queries(stretchedH.n_rows, users.n_elem);
|
||||
for (size_t i = 0; i < queries.n_cols; ++i)
|
||||
queries.col(i) = stretchedH.col(users[i]);
|
||||
|
||||
// Now calculate the neighborhood of these users.
|
||||
neighbor::KNN a(stretchedH);
|
||||
arma::mat distances;
|
||||
arma::Mat<size_t> neighborhood;
|
||||
|
||||
a.Search(queries, numUsersForSimilarity, neighborhood, distances);
|
||||
|
||||
// Now that we have the neighborhoods we need, calculate the predictions.
|
||||
predictions.set_size(combinations.n_cols);
|
||||
|
||||
size_t user = 0; // Cumulative user count, because we are doing it in order.
|
||||
for (size_t i = 0; i < sortedCombinations.n_cols; ++i)
|
||||
{
|
||||
// Could this be made faster by calculating dot products for multiple items
|
||||
// at once?
|
||||
double rating = 0.0;
|
||||
|
||||
// Map the combination's user to the user ID used for kNN.
|
||||
while (users[user] < sortedCombinations(0, i))
|
||||
++user;
|
||||
|
||||
for (size_t j = 0; j < neighborhood.n_rows; ++j)
|
||||
rating += arma::as_scalar(w.row(sortedCombinations(1, i)) *
|
||||
h.col(neighborhood(j, user)));
|
||||
rating /= neighborhood.n_rows;
|
||||
|
||||
predictions(ordering[i]) = rating;
|
||||
}
|
||||
|
||||
// Denormalize ratings.
|
||||
normalization.Denormalize(combinations, predictions);
|
||||
}
|
||||
|
||||
template<typename NormalizationType>
|
||||
void CFType<NormalizationType>::CleanData(const arma::mat& data,
|
||||
arma::sp_mat& cleanedData)
|
||||
{
|
||||
// Generate list of locations for batch insert constructor for sparse
|
||||
// matrices.
|
||||
arma::umat locations(2, data.n_cols);
|
||||
arma::vec values(data.n_cols);
|
||||
for (size_t i = 0; i < data.n_cols; ++i)
|
||||
{
|
||||
// We have to transpose it because items are rows, and users are columns.
|
||||
locations(1, i) = ((arma::uword) data(0, i));
|
||||
locations(0, i) = ((arma::uword) data(1, i));
|
||||
values(i) = data(2, i);
|
||||
|
||||
// The algorithm omits rating of zero. Thus, when normalizing original
|
||||
// ratings in Normalize(), if normalized rating equals zero, it is set
|
||||
// to the smallest positive double value.
|
||||
if (values(i) == 0)
|
||||
Log::Warn << "User rating of 0 ignored for user " << locations(1, i)
|
||||
<< ", item " << locations(0, i) << "." << std::endl;
|
||||
}
|
||||
|
||||
// Find maximum user and item IDs.
|
||||
const size_t maxItemID = (size_t) max(locations.row(0)) + 1;
|
||||
const size_t maxUserID = (size_t) max(locations.row(1)) + 1;
|
||||
|
||||
// Fill sparse matrix.
|
||||
cleanedData = arma::sp_mat(locations, values, maxItemID, maxUserID);
|
||||
}
|
||||
|
||||
//! Serialize the model.
|
||||
template<typename NormalizationType>
|
||||
template<typename Archive>
|
||||
void CFType::serialize(Archive& ar, const unsigned int /* version */)
|
||||
void CFType<NormalizationType>::serialize(Archive& ar,
|
||||
const unsigned int /* version */)
|
||||
{
|
||||
// This model is simple; just serialize all the members. No special handling
|
||||
// required.
|
||||
@@ -125,6 +403,7 @@ void CFType::serialize(Archive& ar, const unsigned int /* version */)
|
||||
ar & BOOST_SERIALIZATION_NVP(w);
|
||||
ar & BOOST_SERIALIZATION_NVP(h);
|
||||
ar & BOOST_SERIALIZATION_NVP(cleanedData);
|
||||
ar & BOOST_SERIALIZATION_NVP(normalization);
|
||||
}
|
||||
|
||||
} // namespace cf
|
||||
|
||||
@@ -105,8 +105,8 @@ PARAM_DOUBLE_IN("min_residue", "Residue required to terminate the factorization"
|
||||
" (lower values generally mean better fits).", "r", 1e-5);
|
||||
|
||||
// Load/save a model.
|
||||
PARAM_MODEL_IN(CFType, "input_model", "Trained CF model to load.", "m");
|
||||
PARAM_MODEL_OUT(CFType, "output_model", "Output for trained CF model.", "M");
|
||||
PARAM_MODEL_IN(CFType<>, "input_model", "Trained CF model to load.", "m");
|
||||
PARAM_MODEL_OUT(CFType<>, "output_model", "Output for trained CF model.", "M");
|
||||
|
||||
// Query settings.
|
||||
PARAM_UMATRIX_IN("query", "List of query users for which recommendations should"
|
||||
@@ -120,7 +120,7 @@ PARAM_INT_IN("recommendations", "Number of recommendations to generate for each"
|
||||
|
||||
PARAM_INT_IN("seed", "Set the random seed (0 uses std::time(NULL)).", "s", 0);
|
||||
|
||||
void ComputeRecommendations(CFType* cf,
|
||||
void ComputeRecommendations(CFType<>* cf,
|
||||
const size_t numRecs,
|
||||
arma::Mat<size_t>& recommendations)
|
||||
{
|
||||
@@ -146,7 +146,7 @@ void ComputeRecommendations(CFType* cf,
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeRMSE(CFType* cf)
|
||||
void ComputeRMSE(CFType<>* cf)
|
||||
{
|
||||
// Now, compute each test point.
|
||||
arma::mat testData = std::move(CLI::GetParam<arma::mat>("test"));
|
||||
@@ -173,7 +173,7 @@ void ComputeRMSE(CFType* cf)
|
||||
Log::Info << "RMSE is " << rmse << "." << endl;
|
||||
}
|
||||
|
||||
void PerformAction(CFType* c)
|
||||
void PerformAction(CFType<>* c)
|
||||
{
|
||||
if (CLI::HasParam("query") || CLI::HasParam("all_user_recommendations"))
|
||||
{
|
||||
@@ -191,7 +191,7 @@ void PerformAction(CFType* c)
|
||||
if (CLI::HasParam("test"))
|
||||
ComputeRMSE(c);
|
||||
|
||||
CLI::GetParam<CFType*>("output_model") = c;
|
||||
CLI::GetParam<CFType<>*>("output_model") = c;
|
||||
}
|
||||
|
||||
template<typename DecompositionPolicy>
|
||||
@@ -202,7 +202,7 @@ void PerformAction(arma::mat& dataset,
|
||||
DecompositionPolicy& decomposition)
|
||||
{
|
||||
const size_t neighborhood = (size_t) CLI::GetParam<int>("neighborhood");
|
||||
CFType* c = new CFType(dataset, decomposition, neighborhood, rank,
|
||||
CFType<>* c = new CFType<>(dataset, decomposition, neighborhood, rank,
|
||||
maxIterations, minResidue, CLI::HasParam("iteration_only_termination"));
|
||||
|
||||
PerformAction(c);
|
||||
@@ -322,7 +322,7 @@ static void mlpackMain()
|
||||
"test" }, true);
|
||||
|
||||
// Load an input model.
|
||||
CFType* c = std::move(CLI::GetParam<CFType*>("input_model"));
|
||||
CFType<>* c = std::move(CLI::GetParam<CFType<>*>("input_model"));
|
||||
|
||||
PerformAction(c);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Define the files we need to compile
|
||||
# Anything not in this list will not be compiled into mlpack.
|
||||
set(SOURCES
|
||||
no_normalization.hpp
|
||||
overall_mean_normalization.hpp
|
||||
user_mean_normalization.hpp
|
||||
item_mean_normalization.hpp
|
||||
z_score_normalization.hpp
|
||||
combined_normalization.hpp
|
||||
)
|
||||
|
||||
# Add directory name to sources.
|
||||
set(DIR_SRCS)
|
||||
foreach(file ${SOURCES})
|
||||
set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
|
||||
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)
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @file combined_normalization.hpp
|
||||
* @author Wenhao Huang
|
||||
*
|
||||
* CombinedNormalization is a class template for performing a sequence of data
|
||||
* normalization methods which are specified by template parameter.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_CF_NORMALIZATION_COMBINED_NORMALIZATION_HPP
|
||||
#define MLPACK_METHODS_CF_NORMALIZATION_COMBINED_NORMALIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
/**
|
||||
* This normalization class performs a sequence of normalization methods on
|
||||
* raw ratings.
|
||||
*
|
||||
* An example of how to use CombinedNormalization in CF is shown below:
|
||||
*
|
||||
* @code
|
||||
* extern arma::mat data; // data is a (user, item, rating) table.
|
||||
* // Users for whom recommendations are generated.
|
||||
* extern arma::Col<size_t> users;
|
||||
* arma::Mat<size_t> recommendations; // Resulting recommendations.
|
||||
*
|
||||
* CFType<CombinedNormalization<
|
||||
* OverallMeanNormalization,
|
||||
* UserMeanNormalization,
|
||||
* ItemMeanNormalization>> cf(data);
|
||||
*
|
||||
* // Generate 10 recommendations for all users.
|
||||
* cf.GetRecommendations(10, recommendations);
|
||||
* @endcode
|
||||
*/
|
||||
template<typename... NormalizationTypes>
|
||||
class CombinedNormalization
|
||||
{
|
||||
public:
|
||||
using TupleType = std::tuple<NormalizationTypes...>;
|
||||
|
||||
// Empty constructor.
|
||||
CombinedNormalization() { }
|
||||
|
||||
/**
|
||||
* Normalize the data by calling Normalize() in each normalization object.
|
||||
*
|
||||
* @param data Input dataset.
|
||||
*/
|
||||
template<typename MatType>
|
||||
void Normalize(MatType& data)
|
||||
{
|
||||
SequenceNormalize<0>(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize rating by calling Denormalize() in each normalization object.
|
||||
* Note that the order of objects calling Denormalize() should be the
|
||||
* reversed order of objects calling Normalize().
|
||||
*
|
||||
* @param user User ID.
|
||||
* @param item Item ID.
|
||||
* @param rating Computed rating before denormalization.
|
||||
*/
|
||||
double Denormalize(const size_t user,
|
||||
const size_t item,
|
||||
const double rating) const
|
||||
{
|
||||
return SequenceDenormalize<0>(user, item, rating);
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize rating by calling Denormalize() in each normalization object.
|
||||
* Note that the order of objects calling Denormalize() should be the
|
||||
* reversed order of objects calling Normalize().
|
||||
*
|
||||
* @param combinations User/Item combinations.
|
||||
* @param predictions Predicted ratings for each user/item combination.
|
||||
*/
|
||||
void Denormalize(const arma::Mat<size_t>& combinations,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
SequenceDenormalize<0>(combinations, predictions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return normalizations tuple.
|
||||
*/
|
||||
TupleType Normalizations() const
|
||||
{
|
||||
return normalizations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int version)
|
||||
{
|
||||
SequenceSerialize<0, Archive>(ar, version);
|
||||
}
|
||||
|
||||
private:
|
||||
//! A tuple of all normalization objects.
|
||||
TupleType normalizations;
|
||||
|
||||
//! Unpack normalizations tuple to normalize data.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to use */
|
||||
typename MatType,
|
||||
typename = std::enable_if_t<(I < std::tuple_size<TupleType>::value)>>
|
||||
void SequenceNormalize(MatType& data)
|
||||
{
|
||||
std::get<I>(normalizations).Normalize(data);
|
||||
SequenceNormalize<I+1>(data);
|
||||
}
|
||||
|
||||
//! End of tuple unpacking.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to use */
|
||||
typename MatType,
|
||||
typename = std::enable_if_t<(I >= std::tuple_size<TupleType>::value)>,
|
||||
typename = void>
|
||||
void SequenceNormalize(MatType& /* data */) { }
|
||||
|
||||
//! Unpack normalizations tuple to denormalize.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to use */
|
||||
typename = std::enable_if_t<(I < std::tuple_size<TupleType>::value)>>
|
||||
double SequenceDenormalize(const size_t user,
|
||||
const size_t item,
|
||||
const double rating) const
|
||||
{
|
||||
// The order of denormalization should be the reversed order
|
||||
// of normalization.
|
||||
double realRating = SequenceDenormalize<I+1>(user, item, rating);
|
||||
realRating =
|
||||
std::get<I>(normalizations).Denormalize(user, item, realRating);
|
||||
return realRating;
|
||||
}
|
||||
|
||||
//! End of tuple unpacking.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to use */
|
||||
typename = std::enable_if_t<(I >= std::tuple_size<TupleType>::value)>,
|
||||
typename = void>
|
||||
double SequenceDenormalize(const size_t /* user */,
|
||||
const size_t /* item */,
|
||||
const double rating) const
|
||||
{
|
||||
return rating;
|
||||
}
|
||||
|
||||
//! Unpack normalizations tuple to denormalize.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to use */
|
||||
typename = std::enable_if_t<(I < std::tuple_size<TupleType>::value)>>
|
||||
void SequenceDenormalize(const arma::Mat<size_t>& combinations,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
// The order of denormalization should be the reversed order
|
||||
// of normalization.
|
||||
SequenceDenormalize<I+1>(combinations, predictions);
|
||||
std::get<I>(normalizations).Denormalize(combinations, predictions);
|
||||
}
|
||||
|
||||
//! End of tuple unpacking.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to use */
|
||||
typename = std::enable_if_t<(I >= std::tuple_size<TupleType>::value)>,
|
||||
typename = void>
|
||||
void SequenceDenormalize(const arma::Mat<size_t>& /* combinations */,
|
||||
arma::vec& /* predictions */) const { }
|
||||
|
||||
//! Unpack normalizations tuple to serialize.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to serialize */
|
||||
typename Archive,
|
||||
typename = std::enable_if_t<(I < std::tuple_size<TupleType>::value)>>
|
||||
void SequenceSerialize(Archive& ar, const unsigned int version)
|
||||
{
|
||||
std::string tagName = "normalization_";
|
||||
tagName += std::to_string(I);
|
||||
ar & boost::serialization::make_nvp(
|
||||
tagName.c_str(), std::get<I>(normalizations));
|
||||
SequenceSerialize<I+1, Archive>(ar, version);
|
||||
}
|
||||
|
||||
//! End of tuple unpacking.
|
||||
template<
|
||||
int I, /* Which normalization in tuple to serialize */
|
||||
typename Archive,
|
||||
typename = std::enable_if_t<(I >= std::tuple_size<TupleType>::value)>,
|
||||
typename = void>
|
||||
void SequenceSerialize(Archive& /* ar */, const unsigned int /* version */)
|
||||
{ }
|
||||
};
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @file item_mean_normalization.hpp
|
||||
* @author Wenhao Huang
|
||||
*
|
||||
* This class performs item mean normalization on raw ratings. In another
|
||||
* word, this class is used to remove global effect of item mean.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_CF_NORMALIZATION_ITEM_MEAN_NORMALIZATION_HPP
|
||||
#define MLPACK_METHODS_CF_NORMALIZATION_ITEM_MEAN_NORMALIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
/**
|
||||
* This normalization class performs item mean normalization on raw ratings.
|
||||
*
|
||||
* An example of how to use ItemMeanNormalization in CF is shown below:
|
||||
*
|
||||
* @code
|
||||
* extern arma::mat data; // data is a (user, item, rating) table.
|
||||
* // Users for whom recommendations are generated.
|
||||
* extern arma::Col<size_t> users;
|
||||
* arma::Mat<size_t> recommendations; // Resulting recommendations.
|
||||
*
|
||||
* // Use ItemMeanNormalization as normalization method.
|
||||
* CFType<ItemMeanNormalization> cf(data);
|
||||
*
|
||||
* // Generate 10 recommendations for all users.
|
||||
* cf.GetRecommendations(10, recommendations);
|
||||
* @endcode
|
||||
*/
|
||||
class ItemMeanNormalization
|
||||
{
|
||||
public:
|
||||
// Empty constructor.
|
||||
ItemMeanNormalization() { }
|
||||
|
||||
/**
|
||||
* Normalize the data by subtracting item mean from each of existing ratings.
|
||||
*
|
||||
* @param data Input dataset in the form of coordinate list.
|
||||
*/
|
||||
void Normalize(arma::mat& data)
|
||||
{
|
||||
const size_t itemNum = arma::max(data.row(1)) + 1;
|
||||
itemMean = arma::vec(itemNum, arma::fill::zeros);
|
||||
// Number of ratings for each item.
|
||||
arma::Row<size_t> ratingNum(itemNum, arma::fill::zeros);
|
||||
|
||||
// Sum ratings for each item.
|
||||
data.each_col([&](arma::vec& datapoint)
|
||||
{
|
||||
const size_t item = (size_t) datapoint(1);
|
||||
const double rating = datapoint(2);
|
||||
itemMean(item) += rating;
|
||||
ratingNum(item) += 1;
|
||||
});
|
||||
|
||||
// Calculate item mean and subtract item mean from ratings.
|
||||
// Set item mean to 0 if the item has no rating.
|
||||
for (size_t i = 0; i < itemNum; i++)
|
||||
{
|
||||
if (ratingNum(i) != 0)
|
||||
itemMean(i) /= ratingNum(i);
|
||||
}
|
||||
|
||||
data.each_col([&](arma::vec& datapoint)
|
||||
{
|
||||
const size_t item = (size_t) datapoint(1);
|
||||
datapoint(2) -= itemMean(item);
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
if (datapoint(2) == 0)
|
||||
datapoint(2) = std::numeric_limits<double>::min();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the data by subtracting item mean from each of existing ratings.
|
||||
*
|
||||
* @param cleanedData Input data as a sparse matrix.
|
||||
*/
|
||||
void Normalize(arma::sp_mat& cleanedData)
|
||||
{
|
||||
itemMean = arma::vec(arma::mean(cleanedData, 1));
|
||||
|
||||
arma::sp_mat::iterator it = cleanedData.begin();
|
||||
arma::sp_mat::iterator it_end = cleanedData.end();
|
||||
for (; it != it_end; it++)
|
||||
{
|
||||
*it = *it - itemMean(it.row());
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
if (*it == 0)
|
||||
*it = std::numeric_limits<double>::min();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding item mean.
|
||||
*
|
||||
* @param user User ID.
|
||||
* @param item Item ID.
|
||||
* @param rating Computed rating before denormalization.
|
||||
*/
|
||||
double Denormalize(const size_t /* user */,
|
||||
const size_t item,
|
||||
const double rating) const
|
||||
{
|
||||
return rating + itemMean(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding item mean.
|
||||
*
|
||||
* @param combinations User/Item combinations.
|
||||
* @param predictions Predicted ratings for each user/item combination.
|
||||
*/
|
||||
void Denormalize(const arma::Mat<size_t>& combinations,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
for (size_t i = 0; i < predictions.n_elem; i++)
|
||||
{
|
||||
const size_t item = combinations(1, i);
|
||||
predictions(i) += itemMean(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return item mean.
|
||||
*/
|
||||
const arma::vec& Mean() const { return itemMean; }
|
||||
|
||||
/**
|
||||
* Serialization.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(itemMean);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Item mean.
|
||||
arma::vec itemMean;
|
||||
};
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file no_normalization.hpp
|
||||
* @author Wenhao Huang
|
||||
*
|
||||
* This class performs no normalization. It is used as default type of
|
||||
* normalization for CF class.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_CF_NORMALIZATION_NO_NORMALIZATION_HPP
|
||||
#define MLPACK_METHODS_CF_NORMALIZATION_NO_NORMALIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
/**
|
||||
* This normalization class doesn't perform any normalization. It is the default
|
||||
* normalization type for CF class.
|
||||
*/
|
||||
class NoNormalization
|
||||
{
|
||||
public:
|
||||
// Empty constructor.
|
||||
NoNormalization() { }
|
||||
|
||||
/**
|
||||
* Do nothing.
|
||||
*
|
||||
* @param data Input dataset.
|
||||
*/
|
||||
template<typename MatType>
|
||||
inline void Normalize(const MatType& /* data */) const { }
|
||||
|
||||
/**
|
||||
* Do nothing.
|
||||
*
|
||||
* @param user User ID.
|
||||
* @param item Item ID.
|
||||
* @param rating Computed rating before denormalization.
|
||||
*/
|
||||
inline double Denormalize(const size_t /* user */,
|
||||
const size_t /* item */,
|
||||
const double rating) const
|
||||
{
|
||||
return rating;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing.
|
||||
*
|
||||
* @param combinations User/Item combinations.
|
||||
* @param predictions Predicted ratings for each user/item combination.
|
||||
*/
|
||||
inline void Denormalize(const arma::Mat<size_t>& /* combinations */,
|
||||
const arma::vec& /* predictions */) const
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Serialization.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& /* ar */, const unsigned int /* version */) { }
|
||||
};
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @file overall_mean_normalization.hpp
|
||||
* @author Wenhao Huang
|
||||
*
|
||||
* This class performs overall mean normalization on raw ratings. In another
|
||||
* word, this class is used to remove global effect of overall mean.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_CF_NORMALIZATION_OVERALL_MEAN_NORMALIZATION_HPP
|
||||
#define MLPACK_METHODS_CF_NORMALIZATION_OVERALL_MEAN_NORMALIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
/**
|
||||
* This normalization class performs overall mean normalization on raw ratings.
|
||||
*
|
||||
* An example of how to use OverallMeanNormalization in CF is shown below:
|
||||
*
|
||||
* @code
|
||||
* extern arma::mat data; // data is a (user, item, rating) table.
|
||||
* // Users for whom recommendations are generated.
|
||||
* extern arma::Col<size_t> users;
|
||||
* arma::Mat<size_t> recommendations; // Resulting recommendations.
|
||||
*
|
||||
* // Use OverallMeanNormalization as normalization method.
|
||||
* CFType<OverallMeanNormalization> cf(data);
|
||||
*
|
||||
* // Generate 10 recommendations for all users.
|
||||
* cf.GetRecommendations(10, recommendations);
|
||||
* @endcode
|
||||
*/
|
||||
class OverallMeanNormalization
|
||||
{
|
||||
public:
|
||||
// Empty constructor.
|
||||
OverallMeanNormalization() : mean(0) { }
|
||||
|
||||
/**
|
||||
* Normalize the data by subtracting the mean of all existing ratings.
|
||||
*
|
||||
* @param data Input dataset in the form of coordinate list.
|
||||
*/
|
||||
void Normalize(arma::mat& data)
|
||||
{
|
||||
mean = arma::mean(data.row(2));
|
||||
data.row(2) -= mean;
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
data.row(2).for_each([](double& x)
|
||||
{
|
||||
if (x == 0)
|
||||
x = std::numeric_limits<double>::min();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the data by subtracting the mean of all existing ratings.
|
||||
*
|
||||
* @param cleanedData Input data as a sparse matrix.
|
||||
*/
|
||||
void Normalize(arma::sp_mat& cleanedData)
|
||||
{
|
||||
// Caculate mean of all non zero ratings.
|
||||
if (cleanedData.n_nonzero != 0)
|
||||
{
|
||||
mean = arma::accu(cleanedData) / cleanedData.n_nonzero;
|
||||
// Subtract mean from all non zero ratings.
|
||||
arma::sp_mat::iterator it = cleanedData.begin();
|
||||
arma::sp_mat::iterator it_end = cleanedData.end();
|
||||
for (; it != it_end; it++)
|
||||
{
|
||||
*it = *it - mean;
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
if (*it == 0)
|
||||
*it = std::numeric_limits<double>::min();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mean = 0;
|
||||
// cleanedData remains the same when mean == 0.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding mean.
|
||||
*
|
||||
* @param user User ID.
|
||||
* @param item Item ID.
|
||||
* @param rating Computed rating before denormalization.
|
||||
*/
|
||||
double Denormalize(const size_t /* user */,
|
||||
const size_t /* item */,
|
||||
const double rating) const
|
||||
{
|
||||
return rating + mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding mean.
|
||||
*
|
||||
* @param combinations User/Item combinations.
|
||||
* @param predictions Predicted ratings for each user/item combination.
|
||||
*/
|
||||
void Denormalize(const arma::Mat<size_t>& /* combinations */,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
predictions += mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return mean.
|
||||
*/
|
||||
double Mean() const
|
||||
{
|
||||
return mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(mean);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Mean of all existing ratings.
|
||||
double mean;
|
||||
};
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @file user_mean_normalization.hpp
|
||||
* @author Wenhao Huang
|
||||
*
|
||||
* This class performs user mean normalization on raw ratings. In another
|
||||
* word, this class is used to remove global effect of user mean.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_CF_NORMALIZATION_USER_MEAN_NORMALIZATION_HPP
|
||||
#define MLPACK_METHODS_CF_NORMALIZATION_USER_MEAN_NORMALIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
/**
|
||||
* This normalization class performs user mean normalization on raw ratings.
|
||||
*
|
||||
* An example of how to use UserMeanNormalization in CF is shown below:
|
||||
*
|
||||
* @code
|
||||
* extern arma::mat data; // data is a (user, item, rating) table.
|
||||
* // Users for whom recommendations are generated.
|
||||
* extern arma::Col<size_t> users;
|
||||
* arma::Mat<size_t> recommendations; // Resulting recommendations.
|
||||
*
|
||||
* // Use UserMeanNormalization as normalization method.
|
||||
* CFType<UserMeanNormalization> cf(data);
|
||||
*
|
||||
* // Generate 10 recommendations for all users.
|
||||
* cf.GetRecommendations(10, recommendations);
|
||||
* @endcode
|
||||
*/
|
||||
class UserMeanNormalization
|
||||
{
|
||||
public:
|
||||
// Empty constructor.
|
||||
UserMeanNormalization() { }
|
||||
|
||||
/**
|
||||
* Normalize the data by subtracting user mean from each of existing ratings.
|
||||
*
|
||||
* @param data Input dataset in the form of coordinate list.
|
||||
*/
|
||||
void Normalize(arma::mat& data)
|
||||
{
|
||||
const size_t userNum = arma::max(data.row(0)) + 1;
|
||||
userMean = arma::rowvec(userNum, arma::fill::zeros);
|
||||
// Number of ratings for each user.
|
||||
arma::Row<size_t> ratingNum(userNum, arma::fill::zeros);
|
||||
|
||||
// Sum ratings for each user.
|
||||
data.each_col([&](arma::vec& datapoint)
|
||||
{
|
||||
const size_t user = (size_t) datapoint(0);
|
||||
const double rating = datapoint(2);
|
||||
userMean(user) += rating;
|
||||
ratingNum(user) += 1;
|
||||
});
|
||||
|
||||
// Calculate user mean and subtract user mean from ratings.
|
||||
// Set user mean to 0 if the user has no rating.
|
||||
for (size_t i = 0; i < userNum; i++)
|
||||
{
|
||||
if (ratingNum(i) != 0)
|
||||
userMean(i) /= ratingNum(i);
|
||||
}
|
||||
|
||||
data.each_col([&](arma::vec& datapoint)
|
||||
{
|
||||
const size_t user = (size_t) datapoint(0);
|
||||
datapoint(2) -= userMean(user);
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
if (datapoint(2) == 0)
|
||||
datapoint(2) = std::numeric_limits<double>::min();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the data by subtracting user mean from each of existing rating.
|
||||
*
|
||||
* @param cleanedData Input data as a sparse matrix.
|
||||
*/
|
||||
void Normalize(arma::sp_mat& cleanedData)
|
||||
{
|
||||
userMean = arma::rowvec(arma::mean(cleanedData, 0));
|
||||
|
||||
arma::sp_mat::iterator it = cleanedData.begin();
|
||||
arma::sp_mat::iterator it_end = cleanedData.end();
|
||||
for (; it != it_end; it++)
|
||||
{
|
||||
*it = *it - userMean(it.col());
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
if (*it == 0)
|
||||
*it = std::numeric_limits<double>::min();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding user mean.
|
||||
*
|
||||
* @param user User ID.
|
||||
* @param item Item ID.
|
||||
* @param rating Computed rating before denormalization.
|
||||
*/
|
||||
double Denormalize(const size_t user,
|
||||
const size_t /* item */,
|
||||
const double rating) const
|
||||
{
|
||||
return rating + userMean(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding user mean.
|
||||
*
|
||||
* @param combinations User/Item combinations.
|
||||
* @param predictions Predicted ratings for each user/item combination.
|
||||
*/
|
||||
void Denormalize(const arma::Mat<size_t>& combinations,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
for (size_t i = 0; i < predictions.n_elem; i++)
|
||||
{
|
||||
const size_t user = combinations(0, i);
|
||||
predictions(i) += userMean(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user mean.
|
||||
*/
|
||||
const arma::rowvec& Mean() const { return userMean; }
|
||||
|
||||
/**
|
||||
* Serialization.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(userMean);
|
||||
}
|
||||
|
||||
private:
|
||||
//! User mean.
|
||||
arma::rowvec userMean;
|
||||
};
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @file z_score_normalization.hpp
|
||||
* @author Wenhao Huang
|
||||
*
|
||||
* This class performs z-score normalization on raw ratings.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_CF_NORMALIZATION_Z_SCORE_NORMALIZATION_HPP
|
||||
#define MLPACK_METHODS_CF_NORMALIZATION_Z_SCORE_NORMALIZATION_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace cf {
|
||||
|
||||
/**
|
||||
* This normalization class performs z-score normalization on raw ratings.
|
||||
*
|
||||
* An example of how to use ZScoreNormalization in CF is shown below:
|
||||
*
|
||||
* @code
|
||||
* extern arma::mat data; // data is a (user, item, rating) table.
|
||||
* // Users for whom recommendations are generated.
|
||||
* extern arma::Col<size_t> users;
|
||||
* arma::Mat<size_t> recommendations; // Resulting recommendations.
|
||||
*
|
||||
* // Use ZScoreNormalization as normalization method.
|
||||
* CFType<ZScoreNormalization> cf(data);
|
||||
*
|
||||
* // Generate 10 recommendations for all users.
|
||||
* cf.GetRecommendations(10, recommendations);
|
||||
* @endcode
|
||||
*/
|
||||
class ZScoreNormalization
|
||||
{
|
||||
public:
|
||||
// Empty constructor.
|
||||
ZScoreNormalization() : mean(0), stddev(1) { }
|
||||
|
||||
/**
|
||||
* Normalize the data to zero mean and one standard deviation.
|
||||
*
|
||||
* @param data Input dataset in the form of coordinate list.
|
||||
*/
|
||||
void Normalize(arma::mat& data)
|
||||
{
|
||||
mean = arma::mean(data.row(2));
|
||||
stddev = arma::stddev(data.row(2));
|
||||
|
||||
if (std::fabs(stddev) < 1e-14)
|
||||
{
|
||||
Log::Fatal << "Standard deviation of all existing ratings is 0! "
|
||||
<< "This may indicate that all existing ratings are the same."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
data.row(2) = (data.row(2) - mean) / stddev;
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
data.row(2).for_each([](double& x)
|
||||
{
|
||||
if (x == 0)
|
||||
x = std::numeric_limits<double>::min();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the data to zero mean and one standard deviation.
|
||||
*
|
||||
* @param cleanedData Input data as a sparse matrix.
|
||||
*/
|
||||
void Normalize(arma::sp_mat& cleanedData)
|
||||
{
|
||||
// Caculate mean and stdev of all non zero ratings.
|
||||
arma::vec ratings = arma::nonzeros(cleanedData);
|
||||
mean = arma::mean(ratings);
|
||||
stddev = arma::stddev(ratings);
|
||||
|
||||
if (std::fabs(stddev) < 1e-14)
|
||||
{
|
||||
Log::Fatal << "Standard deviation of all existing ratings is 0! "
|
||||
<< "This may indicate that all existing ratings are the same."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Subtract mean from existing rating and divide it by stddev.
|
||||
arma::sp_mat::iterator it = cleanedData.begin();
|
||||
arma::sp_mat::iterator it_end = cleanedData.end();
|
||||
for (; it != it_end; it++)
|
||||
{
|
||||
*it = (*it - mean) / stddev;
|
||||
// The algorithm omits rating of zero. If normalized rating equals zero,
|
||||
// it is set to the smallest positive double value.
|
||||
if (*it == 0)
|
||||
*it = std::numeric_limits<double>::min();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding mean and multiplying stddev.
|
||||
*
|
||||
* @param user User ID.
|
||||
* @param item Item ID.
|
||||
* @param rating Computed rating before denormalization.
|
||||
*/
|
||||
double Denormalize(const size_t /* user */,
|
||||
const size_t /* item */,
|
||||
const double rating) const
|
||||
{
|
||||
return rating * stddev + mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalize computed rating by adding mean and multiplying stddev.
|
||||
*
|
||||
* @param combinations User/Item combinations.
|
||||
* @param predictions Predicted ratings for each user/item combination.
|
||||
*/
|
||||
void Denormalize(const arma::Mat<size_t>& /* combinations */,
|
||||
arma::vec& predictions) const
|
||||
{
|
||||
predictions = predictions * stddev + mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return mean.
|
||||
*/
|
||||
double Mean() const
|
||||
{
|
||||
return mean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stddev.
|
||||
*/
|
||||
double Stddev() const
|
||||
{
|
||||
return stddev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization.
|
||||
*/
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(mean);
|
||||
ar & BOOST_SERIALIZATION_NVP(stddev);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Mean of all existing ratings.
|
||||
double mean;
|
||||
//! Standard deviation of all existing ratings.
|
||||
double stddev;
|
||||
};
|
||||
|
||||
} // namespace cf
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -187,7 +187,7 @@ void mlpack::radical::WhitenFeatureMajorMatrix(const mat& matX,
|
||||
{
|
||||
mat matU, matV;
|
||||
vec s;
|
||||
svd(matU, s, matV, cov(matX));
|
||||
arma::svd(matU, s, matV, cov(matX));
|
||||
matWhitening = matU * diagmat(1 / sqrt(s)) * trans(matV);
|
||||
matXWhitened = matX * matWhitening;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ add_executable(mlpack_test
|
||||
cosine_tree_test.cpp
|
||||
cv_test.cpp
|
||||
dbscan_test.cpp
|
||||
dcgan_test.cpp
|
||||
decision_stump_test.cpp
|
||||
decision_tree_test.cpp
|
||||
det_test.cpp
|
||||
|
||||
@@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE(JacobianAddLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add layer numerically gradient test.
|
||||
* Add layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientAddLayerTest)
|
||||
{
|
||||
@@ -256,7 +256,6 @@ BOOST_AUTO_TEST_CASE(GradientAddLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -535,7 +534,7 @@ BOOST_AUTO_TEST_CASE(JacobianLinearLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear layer numerically gradient test.
|
||||
* Linear layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientLinearLayerTest)
|
||||
{
|
||||
@@ -562,7 +561,6 @@ BOOST_AUTO_TEST_CASE(GradientLinearLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -619,7 +617,7 @@ BOOST_AUTO_TEST_CASE(JacobianLinearNoBiasLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* LinearNoBias layer numerically gradient test.
|
||||
* LinearNoBias layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientLinearNoBiasLayerTest)
|
||||
{
|
||||
@@ -646,7 +644,6 @@ BOOST_AUTO_TEST_CASE(GradientLinearNoBiasLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -721,7 +718,7 @@ BOOST_AUTO_TEST_CASE(JacobianFlexibleReLULayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Flexible ReLU layer numerically gradient test.
|
||||
* Flexible ReLU layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientFlexibleReLULayerTest)
|
||||
{
|
||||
@@ -750,7 +747,6 @@ BOOST_AUTO_TEST_CASE(GradientFlexibleReLULayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -869,7 +865,7 @@ BOOST_AUTO_TEST_CASE(SimpleAddMergeLayerTest)
|
||||
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
{
|
||||
AddMerge<> module;
|
||||
AddMerge<> module(false, false);
|
||||
const size_t numMergeModules = math::RandInt(2, 10);
|
||||
for (size_t m = 0; m < numMergeModules; ++m)
|
||||
{
|
||||
@@ -877,7 +873,7 @@ BOOST_AUTO_TEST_CASE(SimpleAddMergeLayerTest)
|
||||
identityLayer.Forward(std::move(input),
|
||||
std::move(identityLayer.OutputParameter()));
|
||||
|
||||
module.Add(identityLayer);
|
||||
module.Add<IdentityLayer<> >(identityLayer);
|
||||
}
|
||||
|
||||
// Test the Forward function.
|
||||
@@ -957,7 +953,6 @@ BOOST_AUTO_TEST_CASE(GradientLSTMLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -1039,7 +1034,6 @@ BOOST_AUTO_TEST_CASE(GradientFastLSTMLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -1194,7 +1188,7 @@ BOOST_AUTO_TEST_CASE(SimpleConcatLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Concat layer numerically gradient test.
|
||||
* Concat layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientConcatLayerTest)
|
||||
{
|
||||
@@ -1225,7 +1219,6 @@ BOOST_AUTO_TEST_CASE(GradientConcatLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -1394,7 +1387,7 @@ BOOST_AUTO_TEST_CASE(BatchNormTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* BatchNorm layer numerically gradient test.
|
||||
* BatchNorm layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientBatchNormTest)
|
||||
{
|
||||
@@ -1423,7 +1416,6 @@ BOOST_AUTO_TEST_CASE(GradientBatchNormTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 256, false);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 256);
|
||||
return error;
|
||||
@@ -1566,7 +1558,7 @@ BOOST_AUTO_TEST_CASE(SimpleTransposedConvolutionLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transposed Convolution layer numerically gradient test.
|
||||
* Transposed Convolution layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientTransposedConvolutionLayerTest)
|
||||
{
|
||||
@@ -1592,7 +1584,6 @@ BOOST_AUTO_TEST_CASE(GradientTransposedConvolutionLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -1617,7 +1608,7 @@ BOOST_AUTO_TEST_CASE(SimpleMultiplyMergeLayerTest)
|
||||
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
{
|
||||
MultiplyMerge<> module;
|
||||
MultiplyMerge<> module(false, false);
|
||||
const size_t numMergeModules = math::RandInt(2, 10);
|
||||
for (size_t m = 0; m < numMergeModules; ++m)
|
||||
{
|
||||
@@ -1625,7 +1616,7 @@ BOOST_AUTO_TEST_CASE(SimpleMultiplyMergeLayerTest)
|
||||
identityLayer.Forward(std::move(input),
|
||||
std::move(identityLayer.OutputParameter()));
|
||||
|
||||
module.Add(identityLayer);
|
||||
module.Add<IdentityLayer<> >(identityLayer);
|
||||
}
|
||||
|
||||
// Test the Forward function.
|
||||
@@ -1678,7 +1669,7 @@ BOOST_AUTO_TEST_CASE(SimpleAtrousConvolutionLayerTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Atrous Convolution layer numerically gradient test.
|
||||
* Atrous Convolution layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientAtrousConvolutionLayerTest)
|
||||
{
|
||||
@@ -1704,7 +1695,6 @@ BOOST_AUTO_TEST_CASE(GradientAtrousConvolutionLayerTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
@@ -1754,7 +1744,7 @@ BOOST_AUTO_TEST_CASE(LayerNormTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* LayerNorm layer numerically gradient test.
|
||||
* LayerNorm layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientLayerNormTest)
|
||||
{
|
||||
@@ -1783,7 +1773,6 @@ BOOST_AUTO_TEST_CASE(GradientLayerNormTest)
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
arma::mat output;
|
||||
double error = model->Evaluate(model->Parameters(), 0, 256, false);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 256);
|
||||
return error;
|
||||
@@ -1798,6 +1787,70 @@ BOOST_AUTO_TEST_CASE(GradientLayerNormTest)
|
||||
BOOST_REQUIRE_LE(CheckGradient(function), 1e-4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the AddMerge layer is able to forward the
|
||||
* Forward/Backward/Gradient calls.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(AddMergeRunTest)
|
||||
{
|
||||
arma::mat output, input, delta, error;
|
||||
|
||||
AddMerge<> module(true, true);
|
||||
|
||||
Linear<>* linear = new Linear<>(10, 10);
|
||||
module.Add(linear);
|
||||
|
||||
linear->Parameters().randu();
|
||||
linear->Reset();
|
||||
|
||||
input = arma::zeros(10, 1);
|
||||
module.Forward(std::move(input), std::move(output));
|
||||
|
||||
double parameterSum = arma::accu(linear->Parameters().submat(
|
||||
100, 0, linear->Parameters().n_elem - 1, 0));
|
||||
|
||||
// Test the Backward function.
|
||||
module.Backward(std::move(input), std::move(input), std::move(delta));
|
||||
|
||||
// Clean up before we break,
|
||||
delete linear;
|
||||
|
||||
BOOST_REQUIRE_CLOSE(parameterSum, arma::accu(output), 1e-3);
|
||||
BOOST_REQUIRE_EQUAL(arma::accu(delta), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the MultiplyMerge layer is able to forward the
|
||||
* Forward/Backward/Gradient calls.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(MultiplyMergeRunTest)
|
||||
{
|
||||
arma::mat output, input, delta, error;
|
||||
|
||||
MultiplyMerge<> module(true, true);
|
||||
|
||||
Linear<>* linear = new Linear<>(10, 10);
|
||||
module.Add(linear);
|
||||
|
||||
linear->Parameters().randu();
|
||||
linear->Reset();
|
||||
|
||||
input = arma::zeros(10, 1);
|
||||
module.Forward(std::move(input), std::move(output));
|
||||
|
||||
double parameterSum = arma::accu(linear->Parameters().submat(
|
||||
100, 0, linear->Parameters().n_elem - 1, 0));
|
||||
|
||||
// Test the Backward function.
|
||||
module.Backward(std::move(input), std::move(input), std::move(delta));
|
||||
|
||||
// Clean up before we break,
|
||||
delete linear;
|
||||
|
||||
BOOST_REQUIRE_CLOSE(parameterSum, arma::accu(output), 1e-3);
|
||||
BOOST_REQUIRE_EQUAL(arma::accu(delta), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple subview module test.
|
||||
*/
|
||||
@@ -1887,4 +1940,122 @@ BOOST_AUTO_TEST_CASE(SubviewBatchTest)
|
||||
CheckMatrices(outputDef, output);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
/*
|
||||
* Simple Reparametrization module test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SimpleReparametrizationLayerTest)
|
||||
{
|
||||
arma::mat input, output, delta;
|
||||
Reparametrization<> module(5);
|
||||
|
||||
// Test the Forward function.
|
||||
input = join_cols(arma::ones<arma::mat>(5, 1) * -20,
|
||||
arma::zeros<arma::mat>(5, 1));
|
||||
module.Forward(std::move(input), std::move(output));
|
||||
BOOST_REQUIRE_LE(arma::accu(output), 1e-5);
|
||||
|
||||
// Test the Backward function.
|
||||
arma::mat gy = arma::zeros<arma::mat>(5, 1);
|
||||
module.Backward(std::move(input), std::move(gy), std::move(delta));
|
||||
BOOST_REQUIRE(arma::accu(delta) != 0); // klBackward will be added.
|
||||
}
|
||||
|
||||
/**
|
||||
* Reparametrization module stochastic boolean test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(ReparametrizationLayerStochasticTest)
|
||||
{
|
||||
arma::mat input, outputA, outputB;
|
||||
Reparametrization<> module(5, false);
|
||||
|
||||
input = join_cols(arma::ones<arma::mat>(5, 1),
|
||||
arma::zeros<arma::mat>(5, 1));
|
||||
|
||||
// Test if two forward passes generate same output.
|
||||
module.Forward(std::move(input), std::move(outputA));
|
||||
module.Forward(std::move(input), std::move(outputB));
|
||||
|
||||
CheckMatrices(std::move(outputA), std::move(outputB));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reparametrization module includeKl boolean test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(ReparametrizationLayerIncludeKlTest)
|
||||
{
|
||||
arma::mat input, output, gy, delta;
|
||||
Reparametrization<> module(5, true, false);
|
||||
|
||||
input = join_cols(arma::ones<arma::mat>(5, 1),
|
||||
arma::zeros<arma::mat>(5, 1));
|
||||
module.Forward(std::move(input), std::move(output));
|
||||
gy = arma::zeros(output.n_rows, output.n_cols);
|
||||
module.Backward(std::move(output), std::move(gy), std::move(delta));
|
||||
|
||||
BOOST_REQUIRE_EQUAL(arma::accu(std::move(delta)), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jacobian Reparametrization module test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(JacobianReparametrizationLayerTest)
|
||||
{
|
||||
for (size_t i = 0; i < 5; i++)
|
||||
{
|
||||
const size_t inputElementsHalf = math::RandInt(2, 1000);
|
||||
|
||||
arma::mat input;
|
||||
input.set_size(inputElementsHalf * 2, 1);
|
||||
|
||||
Reparametrization<> module(inputElementsHalf, false, false);
|
||||
|
||||
double error = JacobianTest(module, input);
|
||||
BOOST_REQUIRE_LE(error, 1e-5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reparametrization layer numerical gradient test.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GradientReparametrizationLayerTest)
|
||||
{
|
||||
// Linear function gradient instantiation.
|
||||
struct GradientFunction
|
||||
{
|
||||
GradientFunction()
|
||||
{
|
||||
input = arma::randu(10, 1);
|
||||
target = arma::mat("1");
|
||||
|
||||
model = new FFN<NegativeLogLikelihood<>, NguyenWidrowInitialization>();
|
||||
model->Predictors() = input;
|
||||
model->Responses() = target;
|
||||
model->Add<IdentityLayer<> >();
|
||||
model->Add<Linear<> >(10, 6);
|
||||
model->Add<Reparametrization<> >(3, false);
|
||||
model->Add<Linear<> >(3, 2);
|
||||
model->Add<LogSoftMax<> >();
|
||||
}
|
||||
|
||||
~GradientFunction()
|
||||
{
|
||||
delete model;
|
||||
}
|
||||
|
||||
double Gradient(arma::mat& gradient) const
|
||||
{
|
||||
double error = model->Evaluate(model->Parameters(), 0, 1);
|
||||
model->Gradient(model->Parameters(), 0, gradient, 1);
|
||||
return error;
|
||||
}
|
||||
|
||||
arma::mat& Parameters() { return model->Parameters(); }
|
||||
|
||||
FFN<NegativeLogLikelihood<>, NguyenWidrowInitialization>* model;
|
||||
arma::mat input, target;
|
||||
} function;
|
||||
|
||||
BOOST_REQUIRE_LE(CheckGradient(function), 1e-4);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -84,8 +84,6 @@ void CreateLogisticRegressionTestData(arma::mat& data,
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(BBSBBLogisticRegressionTest)
|
||||
{
|
||||
mlpack::math::RandomSeed(time(NULL));
|
||||
|
||||
arma::mat data, testData, shuffledData;
|
||||
arma::Row<size_t> responses, testResponses, shuffledResponses;
|
||||
|
||||
@@ -113,8 +111,6 @@ BOOST_AUTO_TEST_CASE(BBSBBLogisticRegressionTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(BBSArmijoLogisticRegressionTest)
|
||||
{
|
||||
mlpack::math::RandomSeed(time(NULL));
|
||||
|
||||
arma::mat data, testData, shuffledData;
|
||||
arma::Row<size_t> responses, testResponses, shuffledResponses;
|
||||
|
||||
|
||||
+188
-95
@@ -18,6 +18,12 @@
|
||||
#include <mlpack/methods/cf/decomposition_policies/regularized_svd_method.hpp>
|
||||
#include <mlpack/methods/cf/decomposition_policies/svd_complete_method.hpp>
|
||||
#include <mlpack/methods/cf/decomposition_policies/svd_incomplete_method.hpp>
|
||||
#include <mlpack/methods/cf/normalization/no_normalization.hpp>
|
||||
#include <mlpack/methods/cf/normalization/overall_mean_normalization.hpp>
|
||||
#include <mlpack/methods/cf/normalization/user_mean_normalization.hpp>
|
||||
#include <mlpack/methods/cf/normalization/item_mean_normalization.hpp>
|
||||
#include <mlpack/methods/cf/normalization/z_score_normalization.hpp>
|
||||
#include <mlpack/methods/cf/normalization/combined_normalization.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -36,7 +42,7 @@ using namespace std;
|
||||
* set. Default case.
|
||||
*/
|
||||
template<typename DecompositionPolicy>
|
||||
void GetRecommendationsAllUsers(bool cleanData = true)
|
||||
void GetRecommendationsAllUsers()
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
// Dummy number of recommendations.
|
||||
@@ -51,16 +57,7 @@ void GetRecommendationsAllUsers(bool cleanData = true)
|
||||
arma::mat dataset;
|
||||
data::Load("GroupLensSmall.csv", dataset);
|
||||
|
||||
if (cleanData)
|
||||
{
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
|
||||
arma::sp_mat dataset;
|
||||
dataset = cleanedData;
|
||||
}
|
||||
CFType c(dataset, decomposition, 5, 5, 70);
|
||||
CFType<> c(dataset, decomposition, 5, 5, 70);
|
||||
|
||||
// Generate recommendations when query set is not specified.
|
||||
c.GetRecommendations(numRecs, recommendations);
|
||||
@@ -76,7 +73,7 @@ void GetRecommendationsAllUsers(bool cleanData = true)
|
||||
* Make sure that the recommendations are generated for queried users only.
|
||||
*/
|
||||
template<typename DecompositionPolicy>
|
||||
void GetRecommendationsQueriedUser(bool cleanData = true)
|
||||
void GetRecommendationsQueriedUser()
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
// Number of users that we will search for recommendations for.
|
||||
@@ -97,17 +94,7 @@ void GetRecommendationsQueriedUser(bool cleanData = true)
|
||||
arma::mat dataset;
|
||||
data::Load("GroupLensSmall.csv", dataset);
|
||||
|
||||
if (cleanData)
|
||||
{
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
|
||||
arma::sp_mat dataset;
|
||||
dataset = cleanedData;
|
||||
}
|
||||
|
||||
CFType c(dataset, decomposition, 5, 5, 70);
|
||||
CFType<> c(dataset, decomposition, 5, 5, 70);
|
||||
|
||||
// Generate recommendations when query set is specified.
|
||||
c.GetRecommendations(numRecsDefault, recommendations, users);
|
||||
@@ -122,8 +109,9 @@ void GetRecommendationsQueriedUser(bool cleanData = true)
|
||||
/**
|
||||
* Make sure recommendations that are generated are reasonably accurate.
|
||||
*/
|
||||
template<typename DecompositionPolicy>
|
||||
void RecommendationAccuracy(bool cleanData = true)
|
||||
template<typename DecompositionPolicy,
|
||||
typename NormalizationType = NoNormalization>
|
||||
void RecommendationAccuracy()
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
// Load the GroupLens dataset; then, we will remove some values from it.
|
||||
@@ -164,17 +152,7 @@ void RecommendationAccuracy(bool cleanData = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanData)
|
||||
{
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
|
||||
arma::sp_mat dataset;
|
||||
dataset = cleanedData;
|
||||
}
|
||||
|
||||
CFType c(dataset, decomposition, 5, 5, 70);
|
||||
CFType<NormalizationType> c(dataset, decomposition, 5, 5, 70);
|
||||
|
||||
// Obtain 150 recommendations for the users in savedCols, and make sure the
|
||||
// missing item shows up in most of them. First, create the list of users,
|
||||
@@ -223,8 +201,9 @@ void RecommendationAccuracy(bool cleanData = true)
|
||||
}
|
||||
|
||||
// Make sure that Predict() is returning reasonable results.
|
||||
template<typename DecompositionPolicy>
|
||||
void CFPredict(bool cleanData = true)
|
||||
template<typename DecompositionPolicy,
|
||||
typename NormalizationType = NoNormalization>
|
||||
void CFPredict(const double rmseBound = 2.0)
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
// Load the GroupLens dataset; then, we will remove some values from it.
|
||||
@@ -265,17 +244,7 @@ void CFPredict(bool cleanData = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanData)
|
||||
{
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
|
||||
arma::sp_mat dataset;
|
||||
dataset = cleanedData;
|
||||
}
|
||||
|
||||
CFType c(dataset, decomposition, 5, 5, 70);
|
||||
CFType<NormalizationType> c(dataset, decomposition, 5, 5, 70);
|
||||
|
||||
// Now, for each removed rating, make sure the prediction is... reasonably
|
||||
// accurate.
|
||||
@@ -288,17 +257,17 @@ void CFPredict(bool cleanData = true)
|
||||
totalError += error;
|
||||
}
|
||||
|
||||
totalError = std::sqrt(totalError) / savedCols.n_cols;
|
||||
const double rmse = std::sqrt(totalError / savedCols.n_cols);
|
||||
|
||||
// The mean squared error should be less than one.
|
||||
BOOST_REQUIRE_LT(totalError, 0.6);
|
||||
// The root mean square error should be less than ?.
|
||||
BOOST_REQUIRE_LT(rmse, rmseBound);
|
||||
}
|
||||
|
||||
// Do the same thing as the previous test, but ensure that the ratings we
|
||||
// predict with the batch Predict() are the same as the individual Predict()
|
||||
// calls.
|
||||
template<typename DecompositionPolicy>
|
||||
void BatchPredict(bool cleanData = true)
|
||||
void BatchPredict()
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
// Load the GroupLens dataset; then, we will remove some values from it.
|
||||
@@ -339,17 +308,7 @@ void BatchPredict(bool cleanData = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanData)
|
||||
{
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
|
||||
arma::sp_mat dataset;
|
||||
dataset = cleanedData;
|
||||
}
|
||||
|
||||
CFType c(dataset, decomposition, 5, 5, 70);
|
||||
CFType<> c(dataset, decomposition, 5, 5, 70);
|
||||
|
||||
// Get predictions for all user/item pairs we held back.
|
||||
arma::Mat<size_t> combinations(2, savedCols.n_cols);
|
||||
@@ -378,7 +337,7 @@ void Train(DecompositionPolicy& decomposition)
|
||||
// Generate random data.
|
||||
arma::sp_mat randomData;
|
||||
randomData.sprandu(100, 100, 0.3);
|
||||
CFType c(randomData, decomposition, 5, 5, 70);
|
||||
CFType<> c(randomData, decomposition, 5, 5, 70);
|
||||
|
||||
// Now retrain with data we know about.
|
||||
arma::mat dataset;
|
||||
@@ -420,7 +379,7 @@ void Train(DecompositionPolicy& decomposition)
|
||||
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
CFType<>::CleanData(dataset, cleanedData);
|
||||
|
||||
// Now retrain.
|
||||
c.Train(dataset, decomposition, 70);
|
||||
@@ -452,7 +411,7 @@ void Train<>(RegSVDPolicy& decomposition)
|
||||
{
|
||||
arma::mat randomData = arma::zeros(100, 100);
|
||||
randomData.diag().ones();
|
||||
CFType c(randomData, decomposition, 5, 5, 70);
|
||||
CFType<> c(randomData, decomposition, 5, 5, 70);
|
||||
|
||||
// Now retrain with data we know about.
|
||||
arma::mat dataset;
|
||||
@@ -517,11 +476,11 @@ void Train<>(RegSVDPolicy& decomposition)
|
||||
* Make sure we can train a model after using the empty constructor.
|
||||
*/
|
||||
template<typename DecompositionPolicy>
|
||||
void EmptyConstructorTrain(bool cleanData = true)
|
||||
void EmptyConstructorTrain()
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
// Use default constructor.
|
||||
CFType c;
|
||||
CFType<> c;
|
||||
|
||||
// Now retrain with data we know about.
|
||||
arma::mat dataset;
|
||||
@@ -561,16 +520,6 @@ void EmptyConstructorTrain(bool cleanData = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (cleanData)
|
||||
{
|
||||
// Make data into sparse matrix.
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
|
||||
arma::sp_mat dataset;
|
||||
dataset = cleanedData;
|
||||
}
|
||||
|
||||
c.Train(dataset, decomposition, 70);
|
||||
|
||||
// Get predictions for all user/item pairs we held back.
|
||||
@@ -595,7 +544,8 @@ void EmptyConstructorTrain(bool cleanData = true)
|
||||
/**
|
||||
* Ensure we can load and save the CF model.
|
||||
*/
|
||||
template<typename DecompositionPolicy>
|
||||
template<typename DecompositionPolicy,
|
||||
typename NormalizationType = NoNormalization>
|
||||
void Serialization()
|
||||
{
|
||||
DecompositionPolicy decomposition;
|
||||
@@ -604,16 +554,16 @@ void Serialization()
|
||||
data::Load("GroupLensSmall.csv", dataset);
|
||||
|
||||
arma::sp_mat cleanedData;
|
||||
CFType::CleanData(dataset, cleanedData);
|
||||
CFType<NormalizationType>::CleanData(dataset, cleanedData);
|
||||
|
||||
CFType c(cleanedData, decomposition, 5, 5, 70);
|
||||
CFType<NormalizationType> c(cleanedData, decomposition, 5, 5, 70);
|
||||
|
||||
arma::sp_mat randomData;
|
||||
randomData.sprandu(100, 100, 0.3);
|
||||
|
||||
CFType cXml(randomData, decomposition, 5, 5, 70);
|
||||
CFType cBinary;
|
||||
CFType cText(cleanedData, decomposition, 5, 5, 70);
|
||||
CFType<NormalizationType> cXml(randomData, decomposition, 5, 5, 70);
|
||||
CFType<NormalizationType> cBinary;
|
||||
CFType<NormalizationType> cText(cleanedData, decomposition, 5, 5, 70);
|
||||
|
||||
SerializeObjectAll(c, cXml, cText, cBinary);
|
||||
|
||||
@@ -690,7 +640,7 @@ BOOST_AUTO_TEST_CASE(CFGetRecommendationsAllUsersRandSVDTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFGetRecommendationsAllUsersRegSVDTest)
|
||||
{
|
||||
GetRecommendationsAllUsers<RegSVDPolicy>(false);
|
||||
GetRecommendationsAllUsers<RegSVDPolicy>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -745,7 +695,7 @@ BOOST_AUTO_TEST_CASE(CFGetRecommendationsQueriedUserRandSVDTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFGetRecommendationsQueriedUserRegSVDTest)
|
||||
{
|
||||
GetRecommendationsQueriedUser<RegSVDPolicy>(false);
|
||||
GetRecommendationsQueriedUser<RegSVDPolicy>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -799,7 +749,7 @@ BOOST_AUTO_TEST_CASE(RecommendationAccuracyRandSVDTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RecommendationAccuracyRegSVDTest)
|
||||
{
|
||||
RecommendationAccuracy<RegSVDPolicy>(false);
|
||||
RecommendationAccuracy<RegSVDPolicy>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -841,13 +791,13 @@ BOOST_AUTO_TEST_CASE(RecommendationAccuracySVDIncompleteTest)
|
||||
// Make sure that Predict() is returning reasonable results for randomized SVD.
|
||||
BOOST_AUTO_TEST_CASE(CFPredictRandSVDTest)
|
||||
{
|
||||
CFPredict<RandomizedSVDPolicy>();
|
||||
CFPredict<RandomizedSVDPolicy>(4.5);
|
||||
}
|
||||
|
||||
// Make sure that Predict() is returning reasonable results for regularized SVD.
|
||||
BOOST_AUTO_TEST_CASE(CFPredictRegSVDTest)
|
||||
{
|
||||
CFPredict<RegSVDPolicy>(false);
|
||||
CFPredict<RegSVDPolicy>();
|
||||
}
|
||||
|
||||
// Make sure that Predict() is returning reasonable results for batch SVD.
|
||||
@@ -859,7 +809,7 @@ BOOST_AUTO_TEST_CASE(CFPredictBatchSVDTest)
|
||||
// Make sure that Predict() is returning reasonable results for NMF.
|
||||
BOOST_AUTO_TEST_CASE(CFPredictNMFTest)
|
||||
{
|
||||
CFPredict<NMFPolicy>();
|
||||
CFPredict<NMFPolicy>(3.5);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -868,7 +818,7 @@ BOOST_AUTO_TEST_CASE(CFPredictNMFTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictSVDCompleteTest)
|
||||
{
|
||||
CFPredict<SVDCompletePolicy>();
|
||||
CFPredict<SVDCompletePolicy>(3.5);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -877,7 +827,7 @@ BOOST_AUTO_TEST_CASE(CFPredictSVDCompleteTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictSVDIncompleteTest)
|
||||
{
|
||||
CFPredict<SVDIncompletePolicy>();
|
||||
CFPredict<SVDIncompletePolicy>(3.5);
|
||||
}
|
||||
|
||||
// Compare batch Predict() and individual Predict() for randomized SVD.
|
||||
@@ -889,7 +839,7 @@ BOOST_AUTO_TEST_CASE(CFBatchPredictRandSVDTest)
|
||||
// Compare batch Predict() and individual Predict() for regularized SVD.
|
||||
BOOST_AUTO_TEST_CASE(CFBatchPredictRegSVDTest)
|
||||
{
|
||||
BatchPredict<RegSVDPolicy>(false);
|
||||
BatchPredict<RegSVDPolicy>();
|
||||
}
|
||||
|
||||
// Compare batch Predict() and individual Predict() for batch SVD.
|
||||
@@ -993,7 +943,7 @@ BOOST_AUTO_TEST_CASE(EmptyConstructorTrainRandSVDTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EmptyConstructorTrainRegSVDTest)
|
||||
{
|
||||
EmptyConstructorTrain<RegSVDPolicy>(false);
|
||||
EmptyConstructorTrain<RegSVDPolicy>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1072,4 +1022,147 @@ BOOST_AUTO_TEST_CASE(SerializationSVDIncompleteTest)
|
||||
Serialization<SVDIncompletePolicy>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that Predict() is returning reasonable results for NMF and
|
||||
* OverallMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictOverallMeanNormalization)
|
||||
{
|
||||
CFPredict<NMFPolicy, OverallMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that Predict() is returning reasonable results for NMF and
|
||||
* UserMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictUserMeanNormalization)
|
||||
{
|
||||
CFPredict<NMFPolicy, UserMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that Predict() is returning reasonable results for NMF and
|
||||
* ItemMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictItemMeanNormalization)
|
||||
{
|
||||
CFPredict<NMFPolicy, ItemMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that Predict() is returning reasonable results for NMF and
|
||||
* ZScoreNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictZScoreNormalization)
|
||||
{
|
||||
CFPredict<NMFPolicy, ZScoreNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that Predict() is returning reasonable results for NMF and
|
||||
* CombinedNormalization<OverallMeanNormalization, UserMeanNormalization,
|
||||
* ItemMeanNormalization>.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CFPredictCombinedNormalization)
|
||||
{
|
||||
CFPredict<NMFPolicy,
|
||||
CombinedNormalization<
|
||||
OverallMeanNormalization,
|
||||
UserMeanNormalization,
|
||||
ItemMeanNormalization>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure recommendations that are generated are reasonably accurate
|
||||
* for OverallMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RecommendationAccuracyOverallMeanNormalizationTest)
|
||||
{
|
||||
RecommendationAccuracy<NMFPolicy, OverallMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure recommendations that are generated are reasonably accurate
|
||||
* for UserMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RecommendationAccuracyUserMeanNormalizationTest)
|
||||
{
|
||||
RecommendationAccuracy<NMFPolicy, UserMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure recommendations that are generated are reasonably accurate
|
||||
* for ItemMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RecommendationAccuracyItemMeanNormalizationTest)
|
||||
{
|
||||
RecommendationAccuracy<NMFPolicy, ItemMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure recommendations that are generated are reasonably accurate
|
||||
* for ZScoreNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RecommendationAccuracyZScoreNormalizationTest)
|
||||
{
|
||||
RecommendationAccuracy<NMFPolicy, ZScoreNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure recommendations that are generated are reasonably accurate
|
||||
* for CombinedNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RecommendationAccuracyCombinedNormalizationTest)
|
||||
{
|
||||
RecommendationAccuracy<NMFPolicy,
|
||||
CombinedNormalization<
|
||||
OverallMeanNormalization,
|
||||
UserMeanNormalization,
|
||||
ItemMeanNormalization>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we can load and save the CF model using OverallMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationOverallMeanNormalizationTest)
|
||||
{
|
||||
Serialization<NMFPolicy, OverallMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we can load and save the CF model using UserMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationUserMeanNormalizationTest)
|
||||
{
|
||||
Serialization<NMFPolicy, UserMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we can load and save the CF model using ItemMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationItemMeanNormalizationTest)
|
||||
{
|
||||
Serialization<NMFPolicy, ItemMeanNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we can load and save the CF model using ZScoreMeanNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationZScoreNormalizationTest)
|
||||
{
|
||||
Serialization<NMFPolicy, ZScoreNormalization>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we can load and save the CF model using CombinedNormalization.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationCombinedNormalizationTest)
|
||||
{
|
||||
Serialization<NMFPolicy,
|
||||
CombinedNormalization<
|
||||
OverallMeanNormalization,
|
||||
UserMeanNormalization,
|
||||
ItemMeanNormalization>>();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -74,48 +74,59 @@ BOOST_AUTO_TEST_CASE(VanillaNetworkTest)
|
||||
* | | +-+ | +-+ | +-+ | +-+ | | |
|
||||
* +---+ +---+ +---+ +---+ +---+ +---+
|
||||
*/
|
||||
|
||||
FFN<NegativeLogLikelihood<>, RandomInitialization> model;
|
||||
|
||||
model.Add<Convolution<> >(1, 8, 5, 5, 1, 1, 0, 0, 28, 28);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<MaxPooling<> >(8, 8, 2, 2);
|
||||
model.Add<Convolution<> >(8, 12, 2, 2);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<MaxPooling<> >(2, 2, 2, 2);
|
||||
model.Add<Linear<> >(192, 20);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<Linear<> >(20, 10);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<Linear<> >(10, 2);
|
||||
model.Add<LogSoftMax<> >();
|
||||
|
||||
// Train for only 8 epochs.
|
||||
RMSProp opt(0.001, 1, 0.88, 1e-8, 8 * nPoints, -1);
|
||||
|
||||
model.Train(X, Y, opt);
|
||||
|
||||
arma::mat predictionTemp;
|
||||
model.Predict(X, predictionTemp);
|
||||
arma::mat prediction = arma::zeros<arma::mat>(1, predictionTemp.n_cols);
|
||||
|
||||
for (size_t i = 0; i < predictionTemp.n_cols; ++i)
|
||||
// It isn't guaranteed that the network will converge in the specified number
|
||||
// of iterations using random weights. If this works 1 of 5 times, I'm fine
|
||||
// with that. All I want to know is that the network is able to escape from
|
||||
// local minima and to solve the task.
|
||||
bool success = false;
|
||||
for (size_t trial = 0; trial < 5; ++trial)
|
||||
{
|
||||
prediction(i) = arma::as_scalar(arma::find(
|
||||
arma::max(predictionTemp.col(i)) == predictionTemp.col(i), 1)) + 1;
|
||||
}
|
||||
FFN<NegativeLogLikelihood<>, RandomInitialization> model;
|
||||
|
||||
size_t correct = 0;
|
||||
for (size_t i = 0; i < X.n_cols; i++)
|
||||
{
|
||||
if (prediction(i) == Y(i))
|
||||
model.Add<Convolution<> >(1, 8, 5, 5, 1, 1, 0, 0, 28, 28);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<MaxPooling<> >(8, 8, 2, 2);
|
||||
model.Add<Convolution<> >(8, 12, 2, 2);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<MaxPooling<> >(2, 2, 2, 2);
|
||||
model.Add<Linear<> >(192, 20);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<Linear<> >(20, 10);
|
||||
model.Add<ReLULayer<> >();
|
||||
model.Add<Linear<> >(10, 2);
|
||||
model.Add<LogSoftMax<> >();
|
||||
|
||||
// Train for only 8 epochs.
|
||||
RMSProp opt(0.001, 1, 0.88, 1e-8, 8 * nPoints, -1);
|
||||
|
||||
model.Train(X, Y, opt);
|
||||
|
||||
arma::mat predictionTemp;
|
||||
model.Predict(X, predictionTemp);
|
||||
arma::mat prediction = arma::zeros<arma::mat>(1, predictionTemp.n_cols);
|
||||
|
||||
for (size_t i = 0; i < predictionTemp.n_cols; ++i)
|
||||
{
|
||||
correct++;
|
||||
prediction(i) = arma::as_scalar(arma::find(
|
||||
arma::max(predictionTemp.col(i)) == predictionTemp.col(i), 1)) + 1;
|
||||
}
|
||||
|
||||
size_t correct = 0;
|
||||
for (size_t i = 0; i < X.n_cols; i++)
|
||||
{
|
||||
if (prediction(i) == Y(i))
|
||||
correct++;
|
||||
}
|
||||
|
||||
double classificationError = 1 - double(correct) / X.n_cols;
|
||||
if (classificationError <= 0.25)
|
||||
{
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double classificationError = 1 - double(correct) / X.n_cols;
|
||||
BOOST_REQUIRE_LE(classificationError, 0.25);
|
||||
BOOST_REQUIRE_EQUAL(success, true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* @file dcgan_network_test.cpp
|
||||
* @author Shikhar Jaiswal
|
||||
*
|
||||
* Tests the DCGAN network.
|
||||
*
|
||||
* 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/init_rules/gaussian_init.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/cross_entropy_error.hpp>
|
||||
#include <mlpack/methods/ann/loss_functions/sigmoid_cross_entropy_error.hpp>
|
||||
#include <mlpack/methods/ann/gan.hpp>
|
||||
#include <mlpack/methods/ann/ffn.hpp>
|
||||
#include <mlpack/methods/ann/layer/layer.hpp>
|
||||
#include <mlpack/methods/softmax_regression/softmax_regression.hpp>
|
||||
#include <mlpack/core/optimizers/adam/adam.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_tools.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::ann;
|
||||
using namespace mlpack::math;
|
||||
using namespace mlpack::optimization;
|
||||
using namespace mlpack::regression;
|
||||
using namespace std::placeholders;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(DCGANNetworkTest);
|
||||
|
||||
/*
|
||||
* Tests the DCGAN implementation on the MNIST dataset.
|
||||
* It's not viable to train on bigger parameters due to time constraints.
|
||||
* Please refer mlpack/models repository for the tutorial.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(DCGANMNISTTest)
|
||||
{
|
||||
size_t dNumKernels = 32;
|
||||
size_t discriminatorPreTrain = 5;
|
||||
size_t batchSize = 5;
|
||||
size_t noiseDim = 100;
|
||||
size_t generatorUpdateStep = 1;
|
||||
size_t numSamples = 10;
|
||||
double stepSize = 0.0003;
|
||||
double eps = 1e-8;
|
||||
size_t numEpoches = 1;
|
||||
double tolerance = 1e-5;
|
||||
int datasetMaxCols = 10;
|
||||
bool shuffle = true;
|
||||
double multiplier = 10;
|
||||
|
||||
Log::Info << std::boolalpha
|
||||
<< " batchSize = " << batchSize << std::endl
|
||||
<< " generatorUpdateStep = " << generatorUpdateStep << std::endl
|
||||
<< " noiseDim = " << noiseDim << std::endl
|
||||
<< " numSamples = " << numSamples << std::endl
|
||||
<< " stepSize = " << stepSize << std::endl
|
||||
<< " numEpoches = " << numEpoches << std::endl
|
||||
<< " tolerance = " << tolerance << std::endl
|
||||
<< " shuffle = " << shuffle << std::endl;
|
||||
|
||||
arma::mat trainData;
|
||||
trainData.load("mnist_first250_training_4s_and_9s.arm");
|
||||
Log::Info << arma::size(trainData) << std::endl;
|
||||
|
||||
if (datasetMaxCols > 0)
|
||||
trainData = trainData.cols(0, datasetMaxCols - 1);
|
||||
|
||||
size_t numIterations = trainData.n_cols * numEpoches;
|
||||
numIterations /= batchSize;
|
||||
|
||||
Log::Info << "Dataset loaded (" << trainData.n_rows << ", "
|
||||
<< trainData.n_cols << ")" << std::endl;
|
||||
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
|
||||
|
||||
// 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);
|
||||
discriminator.Add<Convolution<> >(dNumKernels, 2 * dNumKernels, 4, 4, 2, 2,
|
||||
1, 1, 14, 14);
|
||||
discriminator.Add<LeakyReLU<> >(0.2);
|
||||
discriminator.Add<Convolution<> >(2 * dNumKernels, 4 * dNumKernels, 4, 4,
|
||||
2, 2, 1, 1, 7, 7);
|
||||
discriminator.Add<LeakyReLU<> >(0.2);
|
||||
discriminator.Add<Convolution<> >(4 * dNumKernels, 8 * dNumKernels, 4, 4,
|
||||
2, 2, 2, 2, 3, 3);
|
||||
discriminator.Add<LeakyReLU<> >(0.2);
|
||||
discriminator.Add<Convolution<> >(8 * dNumKernels, 1, 4, 4, 1, 1,
|
||||
1, 1, 2, 2);
|
||||
discriminator.Add<SigmoidLayer<> >();
|
||||
|
||||
// Create the Generator network
|
||||
FFN<SigmoidCrossEntropyError<> > generator;
|
||||
generator.Add<TransposedConvolution<> >(noiseDim, 8 * dNumKernels, 2, 2,
|
||||
1, 1, 1, 1, 1, 1);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(8 * dNumKernels, 4 * dNumKernels,
|
||||
2, 2, 1, 1, 0, 0, 2, 2);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(4 * dNumKernels, 2 * dNumKernels,
|
||||
5, 5, 2, 2, 1, 1, 3, 3);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 8, 8,
|
||||
1, 1, 1, 1, 7, 7);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(dNumKernels, 1, 15, 15, 1, 1, 1, 1,
|
||||
14, 14);
|
||||
generator.Add<TanHLayer<> >();
|
||||
|
||||
// Create GAN
|
||||
GaussianInitialization gaussian(0, 1);
|
||||
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,
|
||||
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
|
||||
discriminatorPreTrain, multiplier);
|
||||
|
||||
Log::Info << "Training..." << std::endl;
|
||||
gan.Train(optimizer);
|
||||
|
||||
// Generate samples
|
||||
Log::Info << "Sampling..." << std::endl;
|
||||
arma::mat noise(noiseDim, 1);
|
||||
size_t dim = std::sqrt(trainData.n_rows);
|
||||
arma::mat generatedData(2 * dim, dim * numSamples);
|
||||
|
||||
for (size_t i = 0; i < numSamples; i++)
|
||||
{
|
||||
arma::mat samples;
|
||||
noise.imbue( [&]() { return noiseFunction(); } );
|
||||
|
||||
generator.Forward(noise, samples);
|
||||
samples.reshape(dim, dim);
|
||||
samples = samples.t();
|
||||
|
||||
generatedData.submat(0, i * dim, dim - 1, i * dim + dim - 1) = samples;
|
||||
|
||||
samples = trainData.col(math::RandInt(0, trainData.n_cols));
|
||||
samples.reshape(dim, dim);
|
||||
samples = samples.t();
|
||||
|
||||
generatedData.submat(dim,
|
||||
i * dim, 2 * dim - 1, i * dim + dim - 1) = samples;
|
||||
}
|
||||
|
||||
Log::Info << "Output generated!" << std::endl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests the DCGAN implementation on the CelebA dataset.
|
||||
* It's currently not possible to run this every time due to time constraints.
|
||||
* Please refer mlpack/models repository for the tutorial.
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DCGANCelebATest)
|
||||
{
|
||||
size_t dNumKernels = 64;
|
||||
size_t discriminatorPreTrain = 300;
|
||||
size_t batchSize = 1;
|
||||
size_t noiseDim = 100;
|
||||
size_t generatorUpdateStep = 1;
|
||||
size_t numSamples = 10;
|
||||
double stepSize = 0.0003;
|
||||
double eps = 1e-8;
|
||||
size_t numEpoches = 20;
|
||||
double tolerance = 1e-5;
|
||||
int datasetMaxCols = -1;
|
||||
bool shuffle = true;
|
||||
double multiplier = 10;
|
||||
|
||||
Log::Info << std::boolalpha
|
||||
<< " batchSize = " << batchSize << std::endl
|
||||
<< " generatorUpdateStep = " << generatorUpdateStep << std::endl
|
||||
<< " noiseDim = " << noiseDim << std::endl
|
||||
<< " numSamples = " << numSamples << std::endl
|
||||
<< " stepSize = " << stepSize << std::endl
|
||||
<< " numEpoches = " << numEpoches << std::endl
|
||||
<< " tolerance = " << tolerance << std::endl
|
||||
<< " shuffle = " << shuffle << std::endl;
|
||||
|
||||
arma::mat trainData;
|
||||
trainData.load("celeba.csv");
|
||||
Log::Info << arma::size(trainData) << std::endl;
|
||||
|
||||
if (datasetMaxCols > 0)
|
||||
trainData = trainData.cols(0, datasetMaxCols - 1);
|
||||
|
||||
size_t numIterations = trainData.n_cols * numEpoches;
|
||||
numIterations /= batchSize;
|
||||
|
||||
Log::Info << "Dataset loaded (" << trainData.n_rows << ", "
|
||||
<< trainData.n_cols << ")" << std::endl;
|
||||
Log::Info << trainData.n_rows << "--------" << trainData.n_cols << std::endl;
|
||||
|
||||
// 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);
|
||||
discriminator.Add<Convolution<> >(dNumKernels, 2 * dNumKernels, 4, 4, 2, 2,
|
||||
1, 1, 32, 32);
|
||||
discriminator.Add<LeakyReLU<> >(0.2);
|
||||
discriminator.Add<Convolution<> >(2 * dNumKernels, 4 * dNumKernels, 4, 4,
|
||||
2, 2, 1, 1, 16, 16);
|
||||
discriminator.Add<LeakyReLU<> >(0.2);
|
||||
discriminator.Add<Convolution<> >(4 * dNumKernels, 8 * dNumKernels, 4, 4,
|
||||
2, 2, 1, 1, 8, 8);
|
||||
discriminator.Add<LeakyReLU<> >(0.2);
|
||||
discriminator.Add<Convolution<> >(8 * dNumKernels, 1, 4, 4, 1, 1,
|
||||
0, 0, 4, 4);
|
||||
discriminator.Add<SigmoidLayer<> >();
|
||||
|
||||
// Create the Generator network
|
||||
FFN<SigmoidCrossEntropyError<> > generator;
|
||||
generator.Add<TransposedConvolution<> >(noiseDim, 8 * dNumKernels, 4, 4,
|
||||
1, 1, 2, 2, 1, 1);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(8 * dNumKernels, 4 * dNumKernels,
|
||||
5, 5, 1, 1, 1, 1, 4, 4);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(4 * dNumKernels, 2 * dNumKernels,
|
||||
9, 9, 1, 1, 1, 1, 8, 8);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(2 * dNumKernels, dNumKernels, 17, 17,
|
||||
1, 1, 1, 1, 16, 16);
|
||||
generator.Add<ReLULayer<> >();
|
||||
generator.Add<TransposedConvolution<> >(dNumKernels, 3, 33, 33, 1, 1, 1, 1,
|
||||
32, 32);
|
||||
generator.Add<TanHLayer<> >();
|
||||
|
||||
// Create GAN
|
||||
GaussianInitialization gaussian(0, 1);
|
||||
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,
|
||||
gaussian, noiseFunction, noiseDim, batchSize, generatorUpdateStep,
|
||||
discriminatorPreTrain, multiplier);
|
||||
|
||||
Log::Info << "Training..." << std::endl;
|
||||
gan.Train(optimizer);
|
||||
|
||||
// Generate samples
|
||||
Log::Info << "Sampling..." << std::endl;
|
||||
arma::mat noise(noiseDim, 1);
|
||||
size_t dim = std::sqrt(trainData.n_rows);
|
||||
arma::mat generatedData(2 * dim, dim * numSamples);
|
||||
|
||||
for (size_t i = 0; i < numSamples; i++)
|
||||
{
|
||||
arma::mat samples;
|
||||
noise.imbue( [&]() { return noiseFunction(); } );
|
||||
|
||||
generator.Forward(noise, samples);
|
||||
samples.reshape(dim, dim);
|
||||
samples = samples.t();
|
||||
|
||||
generatedData.submat(0, i * dim, dim - 1, i * dim + dim - 1) = samples;
|
||||
|
||||
samples = trainData.col(math::RandInt(0, trainData.n_cols));
|
||||
samples.reshape(dim, dim);
|
||||
samples = samples.t();
|
||||
|
||||
generatedData.submat(dim,
|
||||
i * dim, 2 * dim - 1, i * dim + dim - 1) = samples;
|
||||
}
|
||||
|
||||
Log::Info << "Output generated!" << std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -132,28 +132,26 @@ BOOST_AUTO_TEST_CASE(GANTest)
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests the GAN implementation on the O'Reilly Test on the MNIST dataset.
|
||||
* It's currently not possible to run this every time due to time constraints.
|
||||
* Tests the GAN implementation of the O'Reilly Test on the MNIST dataset.
|
||||
* It's not viable to train on bigger parameters due to time constraints.
|
||||
* Please refer mlpack/models repository for the tutorial.
|
||||
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GANMNISTTest)
|
||||
{
|
||||
size_t dNumKernels = 32;
|
||||
size_t discriminatorPreTrain = 300;
|
||||
size_t batchSize = 1;
|
||||
size_t discriminatorPreTrain = 5;
|
||||
size_t batchSize = 5;
|
||||
size_t noiseDim = 100;
|
||||
size_t generatorUpdateStep = 1;
|
||||
size_t numSamples = 10;
|
||||
double stepSize = 0.0003;
|
||||
double eps = 1e-8;
|
||||
size_t numEpoches = 10;
|
||||
size_t numEpoches = 1;
|
||||
double tolerance = 1e-5;
|
||||
int datasetMaxCols = -1;
|
||||
int datasetMaxCols = 10;
|
||||
bool shuffle = true;
|
||||
double multiplier = 10;
|
||||
std::string output_dataset = "output_mnist.csv";
|
||||
|
||||
Log::Info << "output_dataset = '" << output_dataset << "'" << std::endl;
|
||||
Log::Info << std::boolalpha
|
||||
<< " batchSize = " << batchSize << std::endl
|
||||
<< " generatorUpdateStep = " << generatorUpdateStep << std::endl
|
||||
@@ -221,7 +219,7 @@ BOOST_AUTO_TEST_CASE(GANMNISTTest)
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -244,10 +242,7 @@ BOOST_AUTO_TEST_CASE(GANMNISTTest)
|
||||
i * dim, 2 * dim - 1, i * dim + dim - 1) = samples;
|
||||
}
|
||||
|
||||
Log::Info << "Saving output to " << output_dataset << "..." << std::endl;
|
||||
generatedData.save(output_dataset, arma::csv_ascii);
|
||||
Log::Info << "Output saved!" << std::endl;
|
||||
Log::Info << "Output generated!" << std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(CFModelReuseTest)
|
||||
SetInputParam("query", std::move(query));
|
||||
SetInputParam("recommendations", recommendations);
|
||||
SetInputParam("input_model",
|
||||
std::move(CLI::GetParam<CFType*>("output_model")));
|
||||
std::move(CLI::GetParam<CFType<>*>("output_model")));
|
||||
|
||||
mlpackMain();
|
||||
|
||||
@@ -262,7 +262,7 @@ BOOST_AUTO_TEST_CASE(CFRankTest)
|
||||
|
||||
mlpackMain();
|
||||
|
||||
const CFType* outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
const CFType<>* outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
|
||||
BOOST_REQUIRE_EQUAL(outputModel->Rank(), rank);
|
||||
}
|
||||
@@ -274,7 +274,7 @@ BOOST_AUTO_TEST_CASE(CFMinResidueTest)
|
||||
{
|
||||
mat dataset;
|
||||
data::Load("GroupLensSmall.csv", dataset);
|
||||
const CFType* outputModel;
|
||||
const CFType<>* outputModel;
|
||||
|
||||
// Set a larger min_residue.
|
||||
SetInputParam("min_residue", double(100));
|
||||
@@ -286,7 +286,7 @@ BOOST_AUTO_TEST_CASE(CFMinResidueTest)
|
||||
mlpack::math::FixedRandomSeed();
|
||||
mlpackMain();
|
||||
|
||||
outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
const mat w1 = outputModel->W();
|
||||
const mat h1 = outputModel->H();
|
||||
|
||||
@@ -302,7 +302,7 @@ BOOST_AUTO_TEST_CASE(CFMinResidueTest)
|
||||
mlpack::math::FixedRandomSeed();
|
||||
mlpackMain();
|
||||
|
||||
outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
const mat w2 = outputModel->W();
|
||||
const mat h2 = outputModel->H();
|
||||
|
||||
@@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(CFIterationOnlyTerminationTest)
|
||||
{
|
||||
mat dataset;
|
||||
data::Load("GroupLensSmall.csv", dataset);
|
||||
const CFType* outputModel;
|
||||
const CFType<>* outputModel;
|
||||
|
||||
// Set iteration_only_termination.
|
||||
SetInputParam("iteration_only_termination", true);
|
||||
@@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(CFIterationOnlyTerminationTest)
|
||||
mlpack::math::FixedRandomSeed();
|
||||
mlpackMain();
|
||||
|
||||
outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
const mat w1 = outputModel->W();
|
||||
const mat h1 = outputModel->H();
|
||||
|
||||
@@ -344,7 +344,7 @@ BOOST_AUTO_TEST_CASE(CFIterationOnlyTerminationTest)
|
||||
mlpack::math::FixedRandomSeed();
|
||||
mlpackMain();
|
||||
|
||||
outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
const mat w2 = outputModel->W();
|
||||
const mat h2 = outputModel->H();
|
||||
|
||||
@@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(CFMaxIterationsTest)
|
||||
{
|
||||
mat dataset;
|
||||
data::Load("GroupLensSmall.csv", dataset);
|
||||
const CFType* outputModel;
|
||||
const CFType<>* outputModel;
|
||||
|
||||
// Set a larger max_iterations.
|
||||
SetInputParam("max_iterations", int(100));
|
||||
@@ -370,7 +370,7 @@ BOOST_AUTO_TEST_CASE(CFMaxIterationsTest)
|
||||
mlpack::math::FixedRandomSeed();
|
||||
mlpackMain();
|
||||
|
||||
outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
const mat w1 = outputModel->W();
|
||||
const mat h1 = outputModel->H();
|
||||
|
||||
@@ -385,7 +385,7 @@ BOOST_AUTO_TEST_CASE(CFMaxIterationsTest)
|
||||
mlpack::math::FixedRandomSeed();
|
||||
mlpackMain();
|
||||
|
||||
outputModel = CLI::GetParam<CFType*>("output_model");
|
||||
outputModel = CLI::GetParam<CFType<>*>("output_model");
|
||||
const mat w2 = outputModel->W();
|
||||
const mat h2 = outputModel->H();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user