diff --git a/data b/data index 0f4598bcb2a..f4f332906bb 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 0f4598bcb2a6c2f481c33b30ab2394833582be25 +Subproject commit f4f332906bb1d29cf745666d4b4d69df3896266f diff --git a/src/shogun/preprocessor/RandomFourierGaussPreproc.cpp b/src/shogun/preprocessor/RandomFourierGaussPreproc.cpp index 586398794f7..6c253468419 100644 --- a/src/shogun/preprocessor/RandomFourierGaussPreproc.cpp +++ b/src/shogun/preprocessor/RandomFourierGaussPreproc.cpp @@ -7,394 +7,87 @@ #include #include -#include #include -#include -#include +#include +#include using namespace shogun; -void RandomFourierGaussPreproc::copy(const RandomFourierGaussPreproc & feats) { - - dim_input_space = feats.dim_input_space; - cur_dim_input_space = feats.cur_dim_input_space; - - dim_feature_space = feats.dim_feature_space; - cur_dim_feature_space=feats.cur_dim_feature_space; - - kernelwidth=feats.kernelwidth; - cur_kernelwidth=feats.cur_kernelwidth; - - if(cur_dim_feature_space>0) - { - if(feats.randomcoeff_additive==NULL) - { - throw ShogunException( - "void CRandomFourierGaussPreproc::copy(...): feats.randomcoeff_additive==NULL && cur_dim_feature_space>0 \n"); - } - - randomcoeff_additive = SG_MALLOC(float64_t, cur_dim_feature_space); - std::copy(feats.randomcoeff_additive,feats.randomcoeff_additive+cur_dim_feature_space,randomcoeff_additive); - } - else - { - randomcoeff_additive = NULL; - } - - if((cur_dim_feature_space>0)&&(cur_dim_input_space>0)) - { - if(feats.randomcoeff_multiplicative==NULL) - { - throw ShogunException( - "void CRandomFourierGaussPreproc::copy(...): feats.randomcoeff_multiplicative==NULL && cur_dim_feature_space>0 &&(cur_dim_input_space>0) \n"); - } - - randomcoeff_multiplicative=SG_MALLOC(float64_t, cur_dim_feature_space*cur_dim_input_space); - std::copy(feats.randomcoeff_multiplicative,feats.randomcoeff_multiplicative+cur_dim_feature_space*cur_dim_input_space,randomcoeff_multiplicative); - } - else - { - randomcoeff_multiplicative = NULL; - } - -} - -RandomFourierGaussPreproc::RandomFourierGaussPreproc() : - RandomMixin> () { - dim_feature_space = 1000; - dim_input_space = 0; - cur_dim_input_space = 0; - cur_dim_feature_space=0; - - randomcoeff_multiplicative=NULL; - randomcoeff_additive=NULL; - - kernelwidth=1; - cur_kernelwidth=kernelwidth; - - SG_ADD(&dim_input_space, "dim_input_space", - "Dimensionality of the input space."); - SG_ADD(&cur_dim_input_space, "cur_dim_input_space", - "Dimensionality of the input space."); - SG_ADD(&dim_feature_space, "dim_feature_space", - "Dimensionality of the feature space."); - SG_ADD(&cur_dim_feature_space, "cur_dim_feature_space", - "Dimensionality of the feature space."); - - SG_ADD(&kernelwidth, "kernelwidth", "Kernel width.", ParameterProperties::HYPER); - SG_ADD(&cur_kernelwidth, "cur_kernelwidth", "Kernel width.", ParameterProperties::HYPER); - - watch_param( - "randomcoeff_additive", &randomcoeff_additive, - &cur_dim_feature_space); - - watch_param( - "randomcoeff_multiplicative", &randomcoeff_multiplicative, - &cur_dim_feature_space, &cur_dim_input_space); - -} - -RandomFourierGaussPreproc::RandomFourierGaussPreproc( - const RandomFourierGaussPreproc & feats) : - RandomMixin> () { - - randomcoeff_multiplicative=NULL; - randomcoeff_additive=NULL; - - SG_ADD(&dim_input_space, "dim_input_space", - "Dimensionality of the input space."); - SG_ADD(&cur_dim_input_space, "cur_dim_input_space", - "Dimensionality of the input space."); - SG_ADD(&dim_feature_space, "dim_feature_space", - "Dimensionality of the feature space."); - SG_ADD(&cur_dim_feature_space, "cur_dim_feature_space", - "Dimensionality of the feature space."); - - SG_ADD(&kernelwidth, "kernelwidth", "Kernel width.", ParameterProperties::HYPER); - SG_ADD(&cur_kernelwidth, "cur_kernelwidth", "Kernel width.", ParameterProperties::HYPER); - - watch_param( - "randomcoeff_additive", &randomcoeff_additive, - &cur_dim_feature_space); - - watch_param( - "randomcoeff_multiplicative", &randomcoeff_multiplicative, - &cur_dim_feature_space, &cur_dim_input_space); - - copy(feats); -} - -RandomFourierGaussPreproc::~RandomFourierGaussPreproc() { - - SG_FREE(randomcoeff_multiplicative); - SG_FREE(randomcoeff_additive); - -} - -EFeatureClass RandomFourierGaussPreproc::get_feature_class() { - return C_DENSE; -} - -EFeatureType RandomFourierGaussPreproc::get_feature_type() { - return F_DREAL; -} - -int32_t RandomFourierGaussPreproc::get_dim_feature_space() const { - return ((int32_t) dim_feature_space); -} - -void RandomFourierGaussPreproc::set_dim_feature_space(const int32_t dim) { - if (dim <= 0) { - throw ShogunException( - "void CRandomFourierGaussPreproc::set_dim_feature_space(const int32 dim): dim<=0 is not allowed"); - } - - dim_feature_space = dim; - -} - -int32_t RandomFourierGaussPreproc::get_dim_input_space() const { - return ((int32_t) dim_input_space); -} - -void RandomFourierGaussPreproc::set_kernelwidth(const float64_t kernelwidth2 ) { - if (kernelwidth2 <= 0) { - throw ShogunException( - "void CRandomFourierGaussPreproc::set_kernelwidth(const float64_t kernelwidth2 ): kernelwidth2 <= 0 is not allowed"); - } - kernelwidth=kernelwidth2; -} - -float64_t RandomFourierGaussPreproc::get_kernelwidth( ) const { - return (kernelwidth); -} - -void RandomFourierGaussPreproc::set_dim_input_space(const int32_t dim) { - if (dim <= 0) { - throw ShogunException( - "void CRandomFourierGaussPreproc::set_dim_input_space(const int32 dim): dim<=0 is not allowed"); - } - - dim_input_space = dim; - -} - -bool RandomFourierGaussPreproc::test_rfinited() const { - - if ((dim_feature_space == cur_dim_feature_space) - && (dim_input_space > 0) && (dim_feature_space > 0)) { - if ((dim_input_space == cur_dim_input_space)&&(Math::abs(kernelwidth-cur_kernelwidth)<1e-5)) { - - // already inited - return true; - } else { - return false; - } - } - - return false; +RandomFourierGaussPreproc::RandomFourierGaussPreproc() +{ + SG_ADD( + &m_dim_output, "dim_output", + "Dimensionality of the output feature space.", + ParameterProperties::HYPER | ParameterProperties::CONSTRAIN, + SG_CONSTRAINT(positive<>())); + SG_ADD( + &m_log_width, "log_width", "Kernel width in log domain", + ParameterProperties::HYPER | ParameterProperties::GRADIENT); + SG_ADD(&m_basis, "basis", "Matrix of basis vectors"); + SG_ADD(&m_offset, "offset", "offset vector"); +} + +RandomFourierGaussPreproc::~RandomFourierGaussPreproc() +{ } -bool RandomFourierGaussPreproc::init_randomcoefficients() { - if (dim_feature_space <= 0) { - throw ShogunException( - "bool CRandomFourierGaussPreproc::init_randomcoefficients(): dim_feature_space<=0 is not allowed\n"); - } - if (dim_input_space <= 0) { - throw ShogunException( - "bool CRandomFourierGaussPreproc::init_randomcoefficients(): dim_input_space<=0 is not allowed\n"); - } - - if (test_rfinited()) { - return false; - } - - - io::info("initializing randomcoefficients "); - - float64_t pi = 3.14159265; - - - SG_FREE(randomcoeff_multiplicative); - randomcoeff_multiplicative=NULL; - SG_FREE(randomcoeff_additive); - randomcoeff_additive=NULL; - - - cur_dim_feature_space=dim_feature_space; - randomcoeff_additive=SG_MALLOC(float64_t, cur_dim_feature_space); - cur_dim_input_space = dim_input_space; - randomcoeff_multiplicative=SG_MALLOC(float64_t, cur_dim_feature_space*cur_dim_input_space); - - cur_kernelwidth=kernelwidth; - - random::fill_array( - randomcoeff_additive, randomcoeff_additive + cur_dim_feature_space, 0.0, - 2 * pi, m_prng); +void RandomFourierGaussPreproc::init_basis(int32_t dim_input_space) +{ + io::info("Creating Fourier Basis Matrix {}x{}", m_dim_output, dim_input_space); - UniformRealDistribution uniform_real_dist(-1.0, 1.0); - for (int32_t i = 0; i < cur_dim_feature_space; ++i) { - for (int32_t k = 0; k < cur_dim_input_space; ++k) { - float64_t x1,x2; - float64_t s = 2; - while ((s >= 1) ) { - // Marsaglia polar for gaussian - x1 = uniform_real_dist(m_prng); - x2 = uniform_real_dist(m_prng); - s=x1*x1+x2*x2; - } - - // = x1/std::sqrt(val)* std::sqrt(-2*std::log(val)); - randomcoeff_multiplicative[i * cur_dim_input_space + k] = - x1 * std::sqrt(-2 * std::log(s) / s) / kernelwidth; - } - } - - io::info("finished: initializing randomcoefficients "); - - return true; -} - -void RandomFourierGaussPreproc::get_randomcoefficients( - float64_t ** randomcoeff_additive2, - float64_t ** randomcoeff_multiplicative2, int32_t *dim_feature_space2, - int32_t *dim_input_space2, float64_t* kernelwidth2) const { - - ASSERT(randomcoeff_additive2) - ASSERT(randomcoeff_multiplicative2) - - if (!test_rfinited()) { - *dim_feature_space2 = 0; - *dim_input_space2 = 0; - *kernelwidth2=1; - *randomcoeff_additive2 = NULL; - *randomcoeff_multiplicative2 = NULL; - return; - } - - *dim_feature_space2 = cur_dim_feature_space; - *dim_input_space2 = cur_dim_input_space; - *kernelwidth2=cur_kernelwidth; - - *randomcoeff_additive2 = SG_MALLOC(float64_t, cur_dim_feature_space); - *randomcoeff_multiplicative2 = SG_MALLOC(float64_t, cur_dim_feature_space*cur_dim_input_space); - - std::copy(randomcoeff_additive, randomcoeff_additive+cur_dim_feature_space, - *randomcoeff_additive2); - std::copy(randomcoeff_multiplicative, randomcoeff_multiplicative+cur_dim_feature_space*cur_dim_input_space, - *randomcoeff_multiplicative2); - - -} - -void RandomFourierGaussPreproc::set_randomcoefficients( - float64_t *randomcoeff_additive2, - float64_t * randomcoeff_multiplicative2, - const int32_t dim_feature_space2, const int32_t dim_input_space2, const float64_t kernelwidth2) { - dim_feature_space = dim_feature_space2; - dim_input_space = dim_input_space2; - kernelwidth=kernelwidth2; - - SG_FREE(randomcoeff_multiplicative); - randomcoeff_multiplicative=NULL; - SG_FREE(randomcoeff_additive); - randomcoeff_additive=NULL; - - cur_dim_feature_space=dim_feature_space; - cur_dim_input_space = dim_input_space; - cur_kernelwidth=kernelwidth; - - if( (dim_feature_space>0) && (dim_input_space>0) ) - { - randomcoeff_additive=SG_MALLOC(float64_t, cur_dim_feature_space); - randomcoeff_multiplicative=SG_MALLOC(float64_t, cur_dim_feature_space*cur_dim_input_space); - - std::copy(randomcoeff_additive2, randomcoeff_additive2 - + dim_feature_space, randomcoeff_additive); - std::copy(randomcoeff_multiplicative2, randomcoeff_multiplicative2 - + cur_dim_feature_space*cur_dim_input_space, randomcoeff_multiplicative); - } + m_basis = sample_spectral_density(dim_input_space); + m_offset = SGVector(m_dim_output); + UniformRealDistribution uniform(0.0, 2.0 * Math::PI); + random::fill_array(m_offset, uniform, m_prng); } void RandomFourierGaussPreproc::fit(std::shared_ptr f) { - if (dim_feature_space <= 0) { - throw ShogunException( - "CRandomFourierGaussPreproc::init (Features *f): dim_feature_space<=0 is not allowed, use void set_dim_feature_space(const int32 dim) before!\n"); - } - - io::info("calling CRandomFourierGaussPreproc::init(...)"); - int32_t num_features = - f->as>()->get_num_features(); - - if (!test_rfinited()) { - dim_input_space = num_features; - init_randomcoefficients(); - ASSERT( test_rfinited()) - } else { - dim_input_space = num_features; - // does not reinit if dimension is the same to avoid overriding a previous call of set_randomcoefficients(...) - init_randomcoefficients(); - } + auto num_features = f->as>()->get_num_features(); + require(num_features > 0, "Input space dimension must be greater than zero"); + + init_basis(num_features); + m_fitted = true; } SGVector RandomFourierGaussPreproc::apply_to_feature_vector(SGVector vector) { - if (!test_rfinited()) { - throw ShogunException( - "float64_t * CRandomFourierGaussPreproc::apply_to_feature_vector(...): test_rfinited()==false: you need to call before CRandomFourierGaussPreproc::init (Features *f) OR 1. set_dim_feature_space(const int32 dim), 2. set_dim_input_space(const int32 dim), 3. init_randomcoefficients() or set_randomcoefficients(...) \n"); - } - - float64_t val = std::sqrt(2.0 / cur_dim_feature_space); - SGVector res(cur_dim_feature_space); - - for (int32_t od = 0; od < cur_dim_feature_space; ++od) { - SGVector wrapper(randomcoeff_multiplicative+od*cur_dim_input_space, cur_dim_input_space, false); - res[od] = val * cos(randomcoeff_additive[od] + linalg::dot(vector, wrapper)); - } - - return res; + return static_cast>( + apply_to_matrix(static_cast>(vector))); } -SGMatrix -RandomFourierGaussPreproc::apply_to_matrix(SGMatrix matrix) +SGMatrix RandomFourierGaussPreproc::apply_to_matrix(SGMatrix matrix) { - // version for case dim_feature_space < dim_input space with direct transformation on feature matrix ?? - auto num_vectors = matrix.num_cols; auto num_features = matrix.num_rows; - io::info("get Feature matrix: {}x{}", num_vectors, num_features); + assert_fitted(); + SG_DEBUG("Got Feature matrix: {}x{}", num_vectors, num_features); - if (num_features != cur_dim_input_space) - { - throw ShogunException( - "float64_t * " - "CRandomFourierGaussPreproc::apply_to_matrix(" - "SGMatrix matrix): matrix.num_rows != " - "cur_dim_input_space is not allowed\n"); - } + SGMatrix projection(m_dim_output, num_vectors); - SGMatrix res(cur_dim_feature_space, num_vectors); + linalg::matrix_prod(m_basis,matrix,projection); + linalg::add_vector(projection, m_offset, projection); + linalg::cos(projection, projection); + linalg::scale(projection, projection, std::sqrt(2.0 / m_dim_output)); - auto val = std::sqrt(2.0 / cur_dim_feature_space); - - for (auto vec : range(num_vectors)) - { - for (auto od : range(cur_dim_feature_space)) - { - SGVector a( - matrix.matrix + vec * num_features, cur_dim_input_space, false); - SGVector b( - randomcoeff_multiplicative + od * cur_dim_input_space, - cur_dim_input_space, false); - res(od, vec) = - val * cos(randomcoeff_additive[od] + linalg::dot(a, b)); - } - } + return projection; +} - return res; +SGMatrix RandomFourierGaussPreproc::sample_spectral_density(int32_t dim_input_space) const +{ + NormalDistribution normal_dist; + auto sampled_kernel = SGMatrix(m_dim_output, dim_input_space); + const auto width = get_width(); + const auto std_dev = std::sqrt(2.0 / width); + std::generate( + sampled_kernel.begin(), sampled_kernel.end(), + [this, &normal_dist, &std_dev]() { + return std_dev * normal_dist(m_prng); + }); + + return sampled_kernel; } void RandomFourierGaussPreproc::cleanup() diff --git a/src/shogun/preprocessor/RandomFourierGaussPreproc.h b/src/shogun/preprocessor/RandomFourierGaussPreproc.h index 7777c25a42c..98cb83717a3 100644 --- a/src/shogun/preprocessor/RandomFourierGaussPreproc.h +++ b/src/shogun/preprocessor/RandomFourierGaussPreproc.h @@ -16,222 +16,97 @@ #include namespace shogun { -/** @brief Preprocessor CRandomFourierGaussPreproc - * implements Random Fourier Features for the Gauss kernel a la Ali Rahimi and Ben Recht Nips2007 - * after preprocessing the features using them in a linear kernel approximates a gaussian kernel - * - * approximation quality depends on dimension of feature space, NOT on number of data. - * - * effectively it requires two parameters for initialization: (A) the dimension of the input features stored in - * dim_input_space - * (B) the dimension of the output feature space - * - * in order to make it work there are two ways: - * (1) if you have already previously computed random fourier features which you want to use together with - * newly computed ones, then you have to take the random coefficients from the previous computation and provide them - * via void set_randomcoefficients(...) for the new computation - * this case is important for example if you compute separately features on training and testing data in two feature objects - * - * in this case you have to set - * 1a) void set_randomcoefficients(...) - * - * (2) if you compute random fourier features from scratch - * in this case you have to set - * 2a) set_kernelwidth(...) - * 2b) void set_dim_feature_space(const int32_t dim); - * 2c) set_dim_input_space(const int32_t dim); - * 2d) init_randomcoefficients() or apply_to_feature_matrix(...) - */ + /** @brief Preprocessor that approximates Gaussian feature map. + * + * Gaussian kernel of form \f$k({\bf x},{\bf x'})= exp(-\frac{||{\bf x}-{\bf x'}||^2}{\tau})\f$ + * is approximated using the formula: + * + * \f[ + * z(x) = \sqrt{2/D}\ cos(wx + b) + * k(x, y) \approx z'(x)z(y) + * \f] + * + * Reference: + * [1] Rahimi, A., & Recht, B. (2008). Random features for large-scale kernel + * machines. In Advances in neural information processing systems (pp. + * 1177-1184). + * + */ class RandomFourierGaussPreproc: public RandomMixin> { public: - /** default constructor */ - RandomFourierGaussPreproc(); - /** alternative constructor */ - RandomFourierGaussPreproc(const RandomFourierGaussPreproc & pr); + RandomFourierGaussPreproc(); - /** default destructor - * takes care for float64_t* randomcoeff_additive,float64_t* randomcoeff_multiplicative; - */ ~RandomFourierGaussPreproc(); + virtual SGVector apply_to_feature_vector(SGVector vector) override; - /** alternative processing routine, inherited from base class - * @param vector the feature vector to be processed - * @return processed feature vector - * in order to work this routine requires the steps described above under cases (1) or two (2) before calling this routine - */ - virtual SGVector apply_to_feature_vector(SGVector vector); - - /** inherited from base class - * @return C_DENSE - */ - virtual EFeatureType get_feature_type(); - - /** inherited from base class - * @return F_DREAL - */ - virtual EFeatureClass get_feature_class(); - - /** fit to features - * calls set_dim_input_space(const int32_t dim); with the proper value - * calls init_randomcoefficients(); this call does NOT override a previous - * call to void set_randomcoefficients(...) IF and ONLY IF - * the dimensions of input AND feature space are equal to the values from - * the previous call to void set_randomcoefficients(...) - * @param f the features to be processed, must be of type - * DenseFeatures - * @return true if new random coefficients were generated, false if old ones - * from a call to set_randomcoefficients(...) are kept - */ - virtual void fit(std::shared_ptr f); - - /** setter for kernel width - * @param width kernel width to be set - */ - void set_kernelwidth(const float64_t width); - - /** getter for kernel width - * @return kernel width - * throws exception if kernelwidth <=0 - */ - float64_t get_kernelwidth( ) const; - - /** getter for the random coefficients - * necessary for creating random fourier features compatible to the current ones - * returns values of internal members randomcoeff_additive - * and randomcoeff_multiplicative - */ - void get_randomcoefficients(float64_t ** randomcoeff_additive2, - float64_t ** randomcoeff_multiplicative2, - int32_t *dim_feature_space2, int32_t *dim_input_space2, float64_t* kernelwidth2 ) const; - - /** setter for the random coefficients - * necessary for creating random fourier features compatible to the previous ones - * sets values of internal members randomcoeff_additive - * and randomcoeff_multiplicative - * simply use as input what you got from get_random_coefficients(...) - */ - void set_randomcoefficients(float64_t *randomcoeff_additive2, - float64_t * randomcoeff_multiplicative2, - const int32_t dim_feature_space2, const int32_t dim_input_space2, const float64_t kernelwidth2); - - /** a setter - * @param dim the value of protected member dim_input_space - * throws a shogun exception if dim<=0 - */ - void set_dim_input_space(const int32_t dim); - - /** a setter - * @param dim the value of protected member dim_feature_space - * throws a shogun exception if dim<=0 - * - */ - void set_dim_feature_space(const int32_t dim); - - /** computes new random coefficients IF test_rfinited() evaluates to false - * test_rfinited() evaluates to TRUE if void set_randomcoefficients(...) - * hase been called and the values set by set_dim_input_space(...) , - * set_dim_feature_space(...) and set_kernelwidth(...) are consistent to the - * call of void set_randomcoefficients(...) - * - * throws shogun exception if dim_feature_space <= 0 or dim_input_space <= 0 - * - * @return returns true if test_rfinited() evaluates to false and new - * coefficients are computed - * returns false if test_rfinited() evaluates to true and old random - * coefficients are kept which were set by a previous call to void - * set_randomcoefficients(...) - * - * this function is useful if you want to use apply_to_feature_vector but - * cannot call before it fit(Features *f) - * - */ - bool init_randomcoefficients(); + virtual EFeatureType get_feature_type() override + { + return F_DREAL; + } - /** a getter - * @return the set value of protected member dim_input_space - */ - int32_t get_dim_input_space() const; + virtual EFeatureClass get_feature_class() override + { + return C_DENSE; + }; - /** a getter - * @return the set value of protected member dim_feature_space - */ - int32_t get_dim_feature_space() const; + virtual void fit(std::shared_ptr f) override; - /** inherited from base class - * does nothing - */ void cleanup(); - /// return the name of the preprocessor - virtual const char* get_name() const { return "RandomFourierGaussPreproc"; } + virtual const char* get_name() const override + { + return "RandomFourierGaussPreproc"; + } - /// return a type of preprocessor - virtual EPreprocessorType get_type() const { return P_RANDOMFOURIERGAUSS; } + virtual EPreprocessorType get_type() const override + { + return P_RANDOMFOURIERGAUSS; + } -protected: - /** default processing routine, inherited from base class - * @param matrix the features matrix to be processed - * @return the processed feature matrix from the DenseFeatures - * class in case (2) (see description above) this routine requires only - * steps 2a) and 2b), the rest is determined automatically - */ - virtual SGMatrix apply_to_matrix(SGMatrix matrix); + SG_FORCED_INLINE float64_t get_width() const + { + return std::exp(m_log_width * 2.0) * 2.0; + } - /** - * helper for copy constructor and assignment operator= - */ - void copy(const RandomFourierGaussPreproc & feats); // helper for two constructors + void set_width(float64_t width) + { + m_log_width = std::log(width/2.0) / 2.0; + } + SG_FORCED_INLINE int32_t get_dim_output() const + { + return m_dim_output; + } - /** dimension of input features - * width of gaussian kernel in the form of exp(-x^2 / (2.0 kernelwidth^2) ) NOTE the 2.0 and the power ^2 ! - */ - float64_t kernelwidth; + void set_dim_output(int32_t dims) + { + m_dim_output = dims; + } - /** dimension of input features - * width of gaussian kernel in the form of exp(-x^2 / (2.0 kernelwidth^2) ) NOTE the 2.0 and the power ^2 ! - */ - float64_t cur_kernelwidth; + protected: - /** desired dimension of input features as set by void set_dim_input_space(const int32_t dim) - * - */ - int32_t dim_input_space; + virtual SGMatrix apply_to_matrix(SGMatrix matrix) override; - /** actual dimension of input features as set by bool init_randomcoefficients() or void set_randomcoefficients - * - */ - int32_t cur_dim_input_space; + virtual SGMatrix sample_spectral_density(int32_t dim_input_space) const; - - /** desired dimension of output features as set by void set_dim_feature_space(const int32_t dim) + /** Helper method which generates random coefficients and stores in the + * internal members. * + * @param dim_input_space input space dimension */ - int32_t dim_feature_space; + virtual void init_basis(int32_t dim_input_space); - /** actual dimension of output features as set by bool init_randomcoefficients() or void set_randomcoefficients - * - */ - int32_t cur_dim_feature_space; + float64_t m_log_width = -0.34657359027997264; - /** - * tests whether rf features have already been initialized - */ - bool test_rfinited() const; + int32_t m_dim_output = 100; - /** - * random coefficient - * length = cur_dim_feature_space - */ - float64_t* randomcoeff_additive; + /** Fourier basis offset */ + SGVector m_offset; - /** - * random coefficient - * length = cur_dim_feature_space* cur_dim_input_space - */ - float64_t* randomcoeff_multiplicative; + /** Fourier basis coefficient */ + SGMatrix m_basis; }; } #endif diff --git a/tests/unit/preprocessor/RandomFourierGaussPreproc_unittest.cc b/tests/unit/preprocessor/RandomFourierGaussPreproc_unittest.cc new file mode 100644 index 00000000000..d1f4fa6f8e5 --- /dev/null +++ b/tests/unit/preprocessor/RandomFourierGaussPreproc_unittest.cc @@ -0,0 +1,84 @@ +/* + * This software is distributed under BSD 3-clause license (see LICENSE file). + * + * Author: Nanubala Gnana Sai + */ + +#include +#include +#include +#include +#include +#include + +using namespace shogun; +using namespace random; + +class RFGPTest : public ::testing::Test +{ +public: + virtual void SetUp() + { + SGMatrix mat(num_features, num_vectors); + linalg::range_fill(mat, 1.0); + auto features = std::make_shared>(mat); + + preproc = std::make_shared(); + preproc->put(kSeed, seed); + preproc->set_width(width); + preproc->set_dim_output(target_dim); + preproc->fit(features); + } + virtual void TearDown() + { + } + +protected: + const int32_t seed = 100; + const index_t num_vectors = 5; + const index_t num_features = 3; + const index_t target_dim = 400; + const float64_t width = 1.5; + const float64_t epsilon = 0.08; + std::shared_ptr preproc; +}; + +TEST_F(RFGPTest, apply) +{ + SGMatrix matrix(num_features, 2); + linalg::range_fill(matrix, 1.0); + auto feats = std::make_shared>(matrix); + auto preprocessed = preproc->transform(feats) + ->as>() + ->get_feature_matrix(); + + auto result_rff = + linalg::dot(preprocessed.get_column(0), preprocessed.get_column(1)); + + auto gauss_kernel = std::make_shared(); + gauss_kernel->set_width(width); + gauss_kernel->init(feats, feats); + + auto result_kernel = gauss_kernel->kernel(0, 1); + EXPECT_NEAR(result_rff, result_kernel, epsilon); +} + +TEST_F(RFGPTest, apply_to_vectors) +{ + SGVector vec1 = {1.0, 2.0, 3.0}; + SGVector vec2 = {4.0, 5.0, 6.0}; + auto mat = SGMatrix(num_features, 2); + linalg::range_fill(mat, 1.0); + + auto processed1 = preproc->apply_to_feature_vector(vec1); + auto processed2 = preproc->apply_to_feature_vector(vec2); + + auto result_rff = linalg::dot(processed1, processed2); + auto gauss_kernel = std::make_shared(); + auto feats = std::make_shared>(mat); + gauss_kernel->set_width(width); + gauss_kernel->init(feats, feats); + + auto result_kernel = gauss_kernel->kernel(0, 1); + EXPECT_NEAR(result_rff, result_kernel, epsilon); +}