1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
/**
* @file cd_test.cpp
* @author Shikhar Bhardwaj
* @author Marcus Edel
* @author Conrad Sanderson
*
* ensmallen 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 ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <ensmallen.hpp>
#include "catch.hpp"
#include "test_function_tools.hpp"
#include "test_types.hpp"
using namespace std;
using namespace ens;
using namespace ens::test;
/**
* Test the correctness of the CD implementation by using a dataset with a
* precalculated minima.
*/
TEMPLATE_TEST_CASE("CD_LogisticRegressionFunction", "[CD]",
ENS_ALL_CPU_TEST_TYPES)
{
typedef typename TestType::elem_type ElemType;
TestType predictors("0 0 0.4; 0 0 0.6; 0 0.3 0; 0.2 0 0; 0.2 -0.5 0;");
arma::Row<size_t> responses("1 1 0;");
LogisticRegressionFunction<TestType> f(predictors, responses, 0.0001);
CD<> s(0.25, 60000, 1e-5);
TestType iterate = f.InitialPoint();
ElemType objective = s.Optimize(f, iterate);
REQUIRE(objective <= 0.055);
}
/**
* Test the correctness of the CD implemenation by using the sparse test
* function, with disjoint features which optimize to a precalculated minima.
*/
TEMPLATE_TEST_CASE("CD_SparseTestFunction", "[CD]", ENS_ALL_CPU_TEST_TYPES,
ENS_SPARSE_TEST_TYPES)
{
// The test function for parallel SGD should work with CD, as the gradients
// of the individual functions are projections into the ith dimension.
CD<> s(0.4);
FunctionTest<SparseTestFunction, TestType>(s,
Tolerances<TestType>::LargeObj, Tolerances<TestType>::LargeCoord);
}
/**
* Test the greedy descent policy.
*/
TEMPLATE_TEST_CASE("CD_GreedyDescent", "[CD]", ENS_TEST_TYPES)
{
// In the sparse test function, the given point has the maximum gradient at
// the feature with index 2.
TestType point("1; 2; 3; 4;");
SparseTestFunction f;
REQUIRE(GreedyDescent::DescentFeature<SparseTestFunction,
TestType,
TestType>(0, point, f) == 2);
// Changing the point under consideration, so that the maximum gradient is at
// index 1.
point(1) = 10;
REQUIRE(GreedyDescent::DescentFeature<SparseTestFunction,
TestType,
TestType>(0, point, f) == 1);
}
/**
* Test the cyclic descent policy.
*/
TEMPLATE_TEST_CASE("CD_CyclicDescent", "[CD]", ENS_TEST_TYPES)
{
const size_t features = 10;
struct DummyFunction
{
static size_t NumFeatures()
{
return features;
}
};
DummyFunction dummy;
for (size_t i = 0; i < 15; ++i)
{
REQUIRE(CyclicDescent::DescentFeature<DummyFunction, TestType, TestType>(
i, TestType(), dummy) == (i % features));
}
}
/**
* Test the random descent policy.
*/
TEMPLATE_TEST_CASE("CD_RandomDescent", "[CD]", ENS_TEST_TYPES)
{
const size_t features = 10;
struct DummyFunction
{
static size_t NumFeatures()
{
return features;
}
};
DummyFunction dummy;
for (size_t i = 0; i < 100; ++i)
{
size_t j = CyclicDescent::DescentFeature<DummyFunction,
TestType,
TestType>(i, TestType(), dummy);
REQUIRE(j < features);
REQUIRE(j >= 0);
}
}
/**
* Test that LogisticRegressionFunction::PartialGradient() works as expected.
*/
TEMPLATE_TEST_CASE("CD_LogisticRegressionFunctionPartialGradient", "[CD]",
ENS_TEST_TYPES)
{
typedef typename TestType::elem_type ElemType;
// Evaluate the gradient and feature gradient and equate.
TestType predictors("0 0 0.4; 0 0 0.6; 0 0.3 0; 0.2 0 0; 0.2 -0.5 0;");
arma::Row<size_t> responses("1 1 0;");
LogisticRegressionFunction<TestType> f(predictors, responses, 0.0001);
TestType testPoint(1, f.NumFeatures(), arma::fill::randu);
TestType testGradient;
f.Gradient(testPoint, testGradient);
for (size_t i = 0; i < f.NumFeatures(); ++i)
{
arma::SpMat<ElemType> fGrad;
f.PartialGradient(testPoint, i, fGrad);
CheckMatrices(TestType(testGradient.col(i)), TestType(fGrad.col(i)));
}
}
/**
* Test that SoftmaxRegressionFunction::PartialGradient() works as expected.
*/
TEMPLATE_TEST_CASE("CD_SoftmaxRegressionFunctionPartialGradient", "[CD]",
ENS_TEST_TYPES)
{
typedef typename TestType::elem_type ElemType;
const size_t points = 1000;
const size_t inputSize = 10;
const size_t numClasses = 5;
// Initialize a random dataset.
TestType data;
data.randu(inputSize, points);
// Create random class labels.
arma::Row<size_t> labels = arma::randi<arma::Row<size_t> >(
points, arma::distr_param(0, numClasses - 1));
// 2 objects for 2 terms in the cost function. Each term contributes towards
// the gradient and thus need to be checked independently.
SoftmaxRegressionFunction<TestType> srf(data, labels, numClasses, 0);
// Create a random set of parameters.
TestType parameters;
parameters.randu(numClasses, inputSize);
// Get gradients for the current parameters.
TestType gradient;
srf.Gradient(parameters, gradient);
// For each parameter.
for (size_t j = 0; j < inputSize; j++)
{
// Get the gradient for this feature.
arma::SpMat<ElemType> fGrad;
srf.PartialGradient(parameters, j, fGrad);
CheckMatrices(TestType(gradient.col(j)), TestType(fGrad.col(j)),
Tolerances<TestType>::Coord);
}
}
|