diff --git a/HISTORY.md b/HISTORY.md index 36232c4eeb..50f3f92efd 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,11 +1,22 @@ ### mlpack ?.?.? ###### ????-??-?? + + * Adapt Softmin layer for new neural network API (#3437). + + * Adapt PReLU layer for new neural network API (#3420). + + * Add CF decomposition methods: `QUIC_SVDPolicy` and `BlockKrylovSVDPolicy` (#3413, #3404). + + * Update outdated code in tutorials (#3398, #3401). + * Bugfix for non-square convolution kernels (#3376). * Fix a few missing includes in `` (#3374). * Fix DBSCAN handling of non-core points (#3346). + * Avoid deprecation warnings in Armadillo 11.4.4+ (#3405). + ### mlpack 4.0.1 ###### 2022-12-23 * Fix mapping of categorical data for Julia bindings (#3305). diff --git a/LICENSE.txt b/LICENSE.txt index e0d5187dbb..cb1cee1aec 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -3,7 +3,7 @@ can redistribute the library and/or modify it under the terms of the 3-clause BSD license. The text of the 3-clause BSD license is contained below. ---- -Copyright (c) 2007-2022, mlpack contributors (see COPYRIGHT.txt) +Copyright (c) 2007-2023, mlpack contributors (see COPYRIGHT.txt) All rights reserved. Redistribution and use of mlpack in source and binary forms, with or without diff --git a/doc/developer/bindings.md b/doc/developer/bindings.md index 53f6e11b02..92cb14c889 100644 --- a/doc/developer/bindings.md +++ b/doc/developer/bindings.md @@ -279,7 +279,7 @@ add_all_bindings(program_dir program_name "category") In this example, this will also add a Markdown binding, which will generate documentation that is typically used to build the website. The `category` parameter should be one of the categories in -`src/mlpack/bindings/markdown/MarkdownCategories.cmake`. +`src/mlpack/bindings/Categories.cmake`. ## How to write mlpack bindings diff --git a/doc/tutorials/amf.md b/doc/tutorials/amf.md index 5579b169b2..7801e5d47d 100644 --- a/doc/tutorials/amf.md +++ b/doc/tutorials/amf.md @@ -1,7 +1,7 @@ # Alternating Matrix Factorization tutorial Alternating matrix factorization decomposes a matrix `V` in the form `V ~ WH` -where `W` is called the basis matrix and `H` is called the encoding matrix.. `V` +where `W` is called the basis matrix and `H` is called the encoding matrix. `V` is taken to be of size `n x m` and the obtained `W` is `n x r` and `H` is `r x m`. The size `r` is called the *rank* of the factorization. Factorization is done by alternately calculating `W` and `H` respectively while holding the other diff --git a/doc/tutorials/approx_kfn.md b/doc/tutorials/approx_kfn.md index 01613b11a0..28d71dc28d 100644 --- a/doc/tutorials/approx_kfn.md +++ b/doc/tutorials/approx_kfn.md @@ -812,7 +812,7 @@ extern arma::mat dataset; QDAFN<> qdafn(dataset, 10, 5); // Print the fifth point of the candidate set. -std::cout << ds.CandidateSet(2).col(4).t(); +std::cout << qdafn.CandidateSet(2).col(4).t(); ``` ### Retraining on a new reference set @@ -896,7 +896,7 @@ extern arma::mat querySet; // Construct the object, performing the default dual-tree search with // approximation level epsilon = 0.05. -KFN kfn(dataset, KFN::DUAL_TREE_MODE, 0.05); +KFN kfn(dataset, DUAL_TREE_MODE, 0.05); // Search for approximate furthest neighbors. arma::Mat neighbors; diff --git a/doc/tutorials/cf.md b/doc/tutorials/cf.md index 9452ebfdd0..5749d99957 100644 --- a/doc/tutorials/cf.md +++ b/doc/tutorials/cf.md @@ -255,7 +255,7 @@ extern size_t rank; // Build the CF object and perform the decomposition. // The constructor takes a default-constructed factorizer, which, by default, // is of type NMFALSFactorizer. -CF cf(data, NMFALSFactorizer(), neighborhood, rank); +CF cf(data, NMFPolicy(), neighborhood, rank); // Store the results in this object. arma::Mat recommendations; @@ -270,12 +270,16 @@ mlpack provides a number of existing factorizers which can be used in place of the default `NMFALSFactorizer` (which is non-negative matrix factorization with alternating least squares update rules). These include: - - `SVDBatchFactorizer` - - `SVDCompleteIncrementalFactorizer` - - `SVDIncompleteIncrementalFactorizer` - - `NMFALSFactorizer` - - `RegularizedSVD` - - `QUIC_SVD` + - `BatchSVDPolicy` + - `SVDCompletePolicy` + - `SVDIncompletePolicy` + - `NMFPolicy` + - `RegSVDPolicy` + - `QuicSVDPolicy` + - `BiasSVDPolicy` + - `SVDPlusPlusPolicy` + - `RandomizedSVDPolicy` + - `BlockKrylovSVDPolicy` The `AMF` class has many other possibilities than those listed here; it is a framework for alternating matrix factorization techniques. See the `AMF` class @@ -297,7 +301,7 @@ extern size_t neighborhood; extern size_t rank; // Build the CF object and perform the decomposition. -CF cf(data, RegularizedSVD(), neighborhood, rank); +CFType cf(data, RegSVDPolicy(), neighborhood, rank); // Store the results in this object. arma::Mat recommendations; @@ -330,7 +334,7 @@ extern size_t rank; // Build the CF object and perform the decomposition. // The constructor takes a default-constructed factorizer, which, by default, // is of type NMFALSFactorizer. -CF cf(data, NMFALSFactorizer(), neighborhood, rank); +CF cf(data, NMFPolicy(), neighborhood, rank); const double prediction = cf.Predict(12, 50); // User 12, item 50. ``` @@ -356,7 +360,7 @@ extern size_t rank; // Build the CF object and perform the decomposition. // The constructor takes a default-constructed factorizer, which, by default, // is of type NMFALSFactorizer. -CF cf(data, NMFALSFactorizer(), neighborhood, rank); +CF cf(data, NMFPolicy(), neighborhood, rank); // References to W and H matrices. const arma::mat& W = cf.W(); diff --git a/doc/tutorials/datasetmapper.md b/doc/tutorials/datasetmapper.md index a841071216..a94854568a 100644 --- a/doc/tutorials/datasetmapper.md +++ b/doc/tutorials/datasetmapper.md @@ -43,7 +43,7 @@ function. using namespace mlpack; arma::mat data; -data::DatasetMapper info; +data::DatasetInfo info; data::Load("dataset.csv", data, info); ``` @@ -155,8 +155,8 @@ std::cout << info.UnmapString(1, 2) << "\n"; This will print: ``` -T -F +True +False ``` ### `UnmapValue()` @@ -168,8 +168,8 @@ The `UnmapValue()` function has the signature `UnmapValue(const std::string - `dimension` is the dimension in which you want to find the mapped value ```c++ -std::cout << info.UnmapValue("T", 2) << "\n"; -std::cout << info.UnmapValue("F", 2) << "\n"; +std::cout << info.UnmapValue("True", 2) << "\n"; +std::cout << info.UnmapValue("False", 2) << "\n"; ``` will produce: diff --git a/doc/tutorials/fastmks.md b/doc/tutorials/fastmks.md index e25fe766fd..21a0d4afb8 100644 --- a/doc/tutorials/fastmks.md +++ b/doc/tutorials/fastmks.md @@ -257,6 +257,9 @@ manually specified. Choices that mlpack provides include: - `HyperbolicTangentKernel` - `LaplacianKernel` - `PSpectrumStringKernel` + - `CauchyKernal` + - `ExampleKernal` + - `SphericalKernal` The following examples use kernels from that list. Writing your own kernel is detailed in the next section. Remember that when you are using the C++ @@ -293,7 +296,7 @@ f.Search(5, indices, products); In this setting we have both a query and reference dataset. We search for 10 maximum kernels. -``` +```c++ #include using namespace mlpack::fastmks; diff --git a/doc/tutorials/image.md b/doc/tutorials/image.md index 01169e7afb..4303a9f9f7 100644 --- a/doc/tutorials/image.md +++ b/doc/tutorials/image.md @@ -103,7 +103,7 @@ bool Load(const std::vector& files, ```c++ data::ImageInfo info; std::vector> files{"test_image1.bmp","test_image2.bmp"}; -data::load(files, matrix, info, false, true); +data::Load(files, matrix, info, false, true); ``` ## Saving diff --git a/doc/tutorials/linear_regression.md b/doc/tutorials/linear_regression.md index 9a0fb67e66..e8c86f16e2 100644 --- a/doc/tutorials/linear_regression.md +++ b/doc/tutorials/linear_regression.md @@ -339,7 +339,7 @@ file. The class provides one method that performs computation: ```c++ -void Predict(const arma::mat& points, arma::vec& predictions); +void Predict(const arma::mat& points, arma::rowvec& predictions); ``` Once you have generated or loaded a model, you can call this method and pass it @@ -355,7 +355,7 @@ corresponding to each row of the points matrix. using namespace mlpack; arma::mat data; // The dataset itself. -arma::vec responses; // The responses, one row for each row in data. +arma::rowvec responses; // The responses, one row for each row in data. // Regress. LinearRegression lr(data, responses); @@ -400,11 +400,11 @@ LinearRegression lr(); // The dataset we want to predict on; each row is a data point. arma::mat points; // This will store the predictions; one row for each point. -arma::vec predictions; +arma::rowvec predictions; lr.Predict(points, predictions); // Predict. -// Now, the vector 'predictions' will contain the predicted values. +// Now, the row vector 'predictions' will contain the predicted values. ``` ### Setting lambda for ridge regression @@ -419,7 +419,7 @@ used to set a value of lambda: using namespace mlpack; arma::mat data; // The dataset itself. -arma::vec responses; // The responses, one row for each row in data. +arma::rowvec responses; // The responses, one row for each row in data. // Regress, with a lambda of 0.5. LinearRegression lr(data, responses, 0.5); diff --git a/src/mlpack/bindings/R/CMakeLists.txt b/src/mlpack/bindings/R/CMakeLists.txt index b8ff0ca988..170b4127a7 100644 --- a/src/mlpack/bindings/R/CMakeLists.txt +++ b/src/mlpack/bindings/R/CMakeLists.txt @@ -290,9 +290,9 @@ if (BUILD_R_BINDINGS) "${CMAKE_CURRENT_BINARY_DIR}/mlpack/") file(COPY - "${CMAKE_CURRENT_SOURCE_DIR}/mlpack/configure" + "${CMAKE_CURRENT_SOURCE_DIR}/mlpack/inst/CITATION" DESTINATION - "${CMAKE_CURRENT_BINARY_DIR}/mlpack/") + "${CMAKE_CURRENT_BINARY_DIR}/mlpack/inst") # Do the actual build. add_custom_target(r_build ALL) diff --git a/src/mlpack/bindings/R/mlpack/configure b/src/mlpack/bindings/R/mlpack/configure deleted file mode 100755 index 608e27d17e..0000000000 --- a/src/mlpack/bindings/R/mlpack/configure +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if test `uname` = "SunOS" ; -then -sed '1 s/$/ -ftrack-macro-expansion=0 -pipe --param ggc-min-expand=10 --param ggc-min-heapsize=8192/' ./src/Makevars > ./src/Makevars.tmp && cat ./src/Makevars.tmp > ./src/Makevars && rm ./src/Makevars.tmp -fi - -exit 0 diff --git a/src/mlpack/bindings/R/mlpack/inst/CITATION b/src/mlpack/bindings/R/mlpack/inst/CITATION new file mode 100644 index 0000000000..e925f71694 --- /dev/null +++ b/src/mlpack/bindings/R/mlpack/inst/CITATION @@ -0,0 +1,49 @@ +bibentry("Manual", + other = unlist(citation(auto = meta), recursive = FALSE)) + +bibentry("Article", + title = "mlpack 4: a fast, header-only C++ machine learning library", + author = c(person("Ryan R.", "Curtin", + comment = c(ORCID = "0000-0002-9903-8214")), + person("Marcus", "Edel", + comment = c(ORCID = "0000-0001-5445-7303")), + person("Omar", "Shrit", + comment = c(ORCID = "0000-0002-8621-3052")), + person("Shubham", "Agrawal", + comment = c(ORCID = "0000-0001-8713-4682")), + person("Suryoday", "Basak", + comment = c(ORCID = "0000-0002-1982-1787")), + person("James J.", "Balamuta", + comment = c(ORCID = "0000-0003-2826-8458")), + person("Ryan", "Birmingham", + comment = c(ORCID = "0000-0002-7943-6346")), + person("Kartik", "Dutt", + comment = c(ORCID = "0000-0003-3877-0142")), + person("Dirk", "Eddelbuettel", + comment = c(ORCID = "0000-0001-6419-907X")), + person("Rishabh", "Garg", + comment = c(ORCID = "0000-0003-0398-0887")), + person("Shikhar", "Jaiswal", + comment = c(ORCID = "0000-0002-3683-3931")), + person("Aakash", "Kaushik", + comment = c(ORCID = "0000-0003-1079-8338")), + person("Sangyeon", "Kim", + comment = c(ORCID = "0000-0003-0717-0240")), + person("Anjishnu", "Mukherjee", + comment = c(ORCID = "0000-0003-4012-8466")), + person("Nanubala Gnana", "Sai", + comment = c(ORCID = "0000-0003-0774-7994")), + person("Nippun", "Sharma", + comment = c(ORCID = "0000-0003-0365-2613")), + person("Yashwant Singh", "Parihar", + comment = c(ORCID = "0000-0003-3492-0377")), + person("Roshan", "Swain", + comment = c(ORCID = "0000-0002-7262-8230")), + person("Conrad", "Sanderson", + comment = c(ORCID = "0000-0002-0049-4501"))), + journal = "Journal of Open Source Software", + year = "2023", + volume = "8", + number = "82", + doi = "10.21105/joss.05026" + ) diff --git a/src/mlpack/bindings/R/mlpack/src/Makevars b/src/mlpack/bindings/R/mlpack/src/Makevars index 489fe04d78..fd44071bab 100644 --- a/src/mlpack/bindings/R/mlpack/src/Makevars +++ b/src/mlpack/bindings/R/mlpack/src/Makevars @@ -1,3 +1,2 @@ PKG_CXXFLAGS = -DBOOST_MATH_PROMOTE_DOUBLE_POLICY=false -I. $(SHLIB_OPENMP_CXXFLAGS) PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -CXX_STD = CXX14 diff --git a/src/mlpack/bindings/R/mlpack/src/Makevars.win b/src/mlpack/bindings/R/mlpack/src/Makevars.win index cb4f589642..0045749a59 100644 --- a/src/mlpack/bindings/R/mlpack/src/Makevars.win +++ b/src/mlpack/bindings/R/mlpack/src/Makevars.win @@ -1,3 +1,3 @@ PKG_CXXFLAGS = -DBOOST_MATH_PROMOTE_DOUBLE_POLICY=false -I. $(SHLIB_OPENMP_CXXFLAGS) -ftrack-macro-expansion=0 -pipe --param ggc-min-expand=10 --param ggc-min-heapsize=8192 PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -CXX_STD = CXX14 +CXX_STD = CXX17 diff --git a/src/mlpack/bindings/markdown/print_param_table.hpp b/src/mlpack/bindings/markdown/print_param_table.hpp index 51d9e9c9cd..dcfa9d3acc 100644 --- a/src/mlpack/bindings/markdown/print_param_table.hpp +++ b/src/mlpack/bindings/markdown/print_param_table.hpp @@ -19,15 +19,15 @@ * Print a table in markdown format that contains * a list of parameters. * - * @param bindingName parameters corresponding to bindingName. - * @param language parameters for a particular language. + * @param bindingName Parameters corresponding to bindingName. + * @param language Parameters for a particular language. * @param params Params object. - * @param headers which headers to print (eg: Name, Default, etc.). - * @param paramsSet to prevent printing a parameter more than once. - * @param onlyHyperParams print only hyper-parameters. - * @param onlyMatrixParams print only matrix-parameters. - * @param onlyInputParams print only input-parameters. - * @param onlyOutputParams print only output-parameters. + * @param headers Which headers to print (eg: Name, Default, etc.). + * @param paramsSet To prevent printing a parameter more than once. + * @param onlyHyperParams If true, print only hyper-parameters. + * @param onlyMatrixParams If true, print only matrix-parameters. + * @param onlyInputParams If true, print only input-parameters. + * @param onlyOutputParams If true, print only output-parameters. */ void PrintParamTable(const std::string& bindingName, const std::string& language, diff --git a/src/mlpack/bindings/python/print_wrapper_py.cpp b/src/mlpack/bindings/python/print_wrapper_py.cpp index 130a23a5d7..3ed5bc2976 100644 --- a/src/mlpack/bindings/python/print_wrapper_py.cpp +++ b/src/mlpack/bindings/python/print_wrapper_py.cpp @@ -64,7 +64,7 @@ void PrintWrapperPY(const std::string& category, } // Import different mlpack programs that are to be wrapped. - for(int i=0; i, ID, DESC, ALIAS, std::vector(), false) @@ -990,17 +727,6 @@ * printing macros like PRINT_PARAM_STRING() or PRINT_DATASET() or others * here---it will cause problems. * @param ALIAS An alias for the parameter (one letter). - * - * @see mlpack::IO, BINDING_USER_NAME(), BINDING_SHORT_DESC(), - * BINDING_LONG_DESC(), BINDING_EXAMPLE() and BINDING_SEE_ALSO(). - * - * @bug - * The __COUNTER__ variable is used in most cases to guarantee a unique global - * identifier for options declared using the PARAM_*() macros. However, not all - * compilers have this support--most notably, gcc < 4.3. In that case, the - * __LINE__ macro is used as an attempt to get a unique global identifier, but - * collisions are still possible, and they produce bizarre error messages. See - * https://github.com/mlpack/mlpack/issues/100 for more information. */ #define PARAM_VECTOR_OUT(T, ID, DESC, ALIAS) \ PARAM_OUT(std::vector, ID, DESC, ALIAS, std::vector(), false) @@ -1030,17 +756,6 @@ * printing macros like PRINT_PARAM_STRING() or PRINT_DATASET() or others * here---it will cause problems. * @param ALIAS One-character string representing the alias of the parameter. - * - * @see mlpack::IO, BINDING_USER_NAME(), BINDING_SHORT_DESC(), - * BINDING_LONG_DESC(), BINDING_EXAMPLE() and BINDING_SEE_ALSO(). - * - * @bug - * The __COUNTER__ variable is used in most cases to guarantee a unique global - * identifier for options declared using the PARAM_*() macros. However, not all - * compilers have this support--most notably, gcc < 4.3. In that case, the - * __LINE__ macro is used as an attempt to get a unique global identifier, but - * collisions are still possible, and they produce bizarre error messages. See - * https://github.com/mlpack/mlpack/issues/100 for more information. */ #define TUPLE_TYPE std::tuple #define PARAM_MATRIX_AND_INFO_IN(ID, DESC, ALIAS) \ @@ -1145,17 +860,6 @@ * printing macros like PRINT_PARAM_STRING() or PRINT_DATASET() or others * here---it will cause problems. * @param ALIAS An alias for the parameter (one letter). - * - * @see mlpack::IO, BINDING_USER_NAME(), BINDING_SHORT_DESC(), - * BINDING_LONG_DESC(), BINDING_EXAMPLE() and BINDING_SEE_ALSO(). - * - * @bug - * The __COUNTER__ variable is used in most cases to guarantee a unique global - * identifier for options declared using the PARAM_*() macros. However, not all - * compilers have this support--most notably, gcc < 4.3. In that case, the - * __LINE__ macro is used as an attempt to get a unique global identifier, but - * collisions are still possible, and they produce bizarre error messages. See - * https://github.com/mlpack/mlpack/issues/100 for more information. */ #define PARAM_INT_IN_REQ(ID, DESC, ALIAS) \ PARAM_IN(int, ID, DESC, ALIAS, 0, true) @@ -1170,17 +874,6 @@ * printing macros like PRINT_PARAM_STRING() or PRINT_DATASET() or others * here---it will cause problems. * @param ALIAS An alias for the parameter (one letter). - * - * @see mlpack::IO, BINDING_USER_NAME(), BINDING_SHORT_DESC(), - * BINDING_LONG_DESC(), BINDING_EXAMPLE() and BINDING_SEE_ALSO(). - * - * @bug - * The __COUNTER__ variable is used in most cases to guarantee a unique global - * identifier for options declared using the PARAM_*() macros. However, not all - * compilers have this support--most notably, gcc < 4.3. In that case, the - * __LINE__ macro is used as an attempt to get a unique global identifier, but - * collisions are still possible, and they produce bizarre error messages. See - * https://github.com/mlpack/mlpack/issues/100 for more information. */ #define PARAM_DOUBLE_IN_REQ(ID, DESC, ALIAS) \ PARAM_IN(double, ID, DESC, ALIAS, 0.0, true) @@ -1195,17 +888,6 @@ * printing macros like PRINT_PARAM_STRING() or PRINT_DATASET() or others * here---it will cause problems. * @param ALIAS An alias for the parameter (one letter). - * - * @see mlpack::IO, BINDING_USER_NAME(), BINDING_SHORT_DESC(), - * BINDING_LONG_DESC(), BINDING_EXAMPLE() and BINDING_SEE_ALSO(). - * - * @bug - * The __COUNTER__ variable is used in most cases to guarantee a unique global - * identifier for options declared using the PARAM_*() macros. However, not all - * compilers have this support--most notably, gcc < 4.3. In that case, the - * __LINE__ macro is used as an attempt to get a unique global identifier, but - * collisions are still possible, and they produce bizarre error messages. See - * https://github.com/mlpack/mlpack/issues/100 for more information. */ #define PARAM_STRING_IN_REQ(ID, DESC, ALIAS) \ PARAM_IN(std::string, ID, DESC, ALIAS, "", true) @@ -1222,17 +904,6 @@ * printing macros like PRINT_PARAM_STRING() or PRINT_DATASET() or others * here---it will cause problems. * @param ALIAS An alias for the parameter (one letter). - * - * @see mlpack::IO, BINDING_USER_NAME(), BINDING_SHORT_DESC(), - * BINDING_LONG_DESC(), BINDING_EXAMPLE() and BINDING_SEE_ALSO(). - * - * @bug - * The __COUNTER__ variable is used in most cases to guarantee a unique global - * identifier for options declared using the PARAM_*() macros. However, not all - * compilers have this support--most notably, gcc < 4.3. In that case, the - * __LINE__ macro is used as an attempt to get a unique global identifier, but - * collisions are still possible, and they produce bizarre error messages. See - * https://github.com/mlpack/mlpack/issues/100 for more information. */ #define PARAM_VECTOR_IN_REQ(T, ID, DESC, ALIAS) \ PARAM_IN(std::vector, ID, DESC, ALIAS, std::vector(), true); diff --git a/src/mlpack/core/util/params_impl.hpp b/src/mlpack/core/util/params_impl.hpp index 53e5f5cfdb..54bc5db1ef 100644 --- a/src/mlpack/core/util/params_impl.hpp +++ b/src/mlpack/core/util/params_impl.hpp @@ -45,7 +45,7 @@ inline Params::Params() /** * Return `true` if the specified parameter was given. * - * @param identifier The name of the parameter in question. + * @param key The name of the parameter in question. */ inline bool Params::Has(const std::string& key) const { diff --git a/src/mlpack/methods/ann/layer/layer_types.hpp b/src/mlpack/methods/ann/layer/layer_types.hpp index a0984f2eee..dc12f47d7f 100644 --- a/src/mlpack/methods/ann/layer/layer_types.hpp +++ b/src/mlpack/methods/ann/layer/layer_types.hpp @@ -43,8 +43,10 @@ #include #include #include +#include #include #include +#include // Convolution modes. #include diff --git a/src/mlpack/methods/ann/layer/not_adapted/parametric_relu_impl.hpp b/src/mlpack/methods/ann/layer/not_adapted/parametric_relu_impl.hpp deleted file mode 100644 index bab4f9563f..0000000000 --- a/src/mlpack/methods/ann/layer/not_adapted/parametric_relu_impl.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @file methods/ann/layer/parametric_relu_impl.hpp - * @author Prasanna Patil - * - * Definition of PReLU layer first introduced in the, - * Kaiming He, Xiangyu Zhang, Shaoqing, Ren Jian Sun, - * "Delving Deep into Rectifiers: - * Surpassing Human-Level Performance on ImageNet Classification", 2014 - * - * 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_PRELU_IMPL_HPP -#define MLPACK_METHODS_ANN_LAYER_PRELU_IMPL_HPP - -// In case it hasn't yet been included. -#include "parametric_relu.hpp" - -namespace mlpack { - -template -PReLUType::PReLUType( - const double userAlpha) : userAlpha(userAlpha) -{ - alpha.set_size(WeightSize(), 1); - alpha(0) = userAlpha; -} - -template -void PReLUType::SetWeights( - typename OutputType::elem_type* weightsPtr) -{ - alpha = arma::mat(weightsPtr, 1, 1, false, false); - - //! Set value of alpha to the one given by user. - // TODO: this doesn't even make any sense. is it trainable or not? - // why is there userAlpha? is that for initialization only? - alpha(0) = userAlpha; -} - -template -void PReLUType::Forward( - const InputType& input, OutputType& output) -{ - // TODO: use transform()? - output = input; - arma::uvec negative = arma::find(input < 0); - output(negative) = input(negative) * alpha(0); -} - -template -void PReLUType::Backward( - const InputType& input, const OutputType& gy, OutputType& g) -{ - OutputType derivative; - derivative.set_size(arma::size(input)); - for (size_t i = 0; i < input.n_elem; ++i) - derivative(i) = (input(i) >= 0) ? 1 : alpha(0); - - g = gy % derivative; -} - -template -void PReLUType::Gradient( - const InputType& input, - const OutputType& error, - OutputType& gradient) -{ - OutputType zeros = arma::zeros(input.n_rows, input.n_cols); - gradient(0) = arma::accu(error % arma::min(zeros, input)) / input.n_cols; -} - -template -template -void PReLUType::serialize( - Archive& ar, - const uint32_t /* version */) -{ - ar(cereal::base_class>(this)); - - ar(CEREAL_NVP(alpha)); -} - -} // namespace mlpack - -#endif diff --git a/src/mlpack/methods/ann/layer/not_adapted/softmin_impl.hpp b/src/mlpack/methods/ann/layer/not_adapted/softmin_impl.hpp deleted file mode 100644 index 6df396abcd..0000000000 --- a/src/mlpack/methods/ann/layer/not_adapted/softmin_impl.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file methods/ann/layer/softmin_impl.hpp - * @author Aakash Kaushik - * - * Implementation of the Softmin 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_ANN_LAYER_SOFTMIN_IMPL_HPP -#define MLPACK_METHODS_ANN_LAYER_SOFTMIN_IMPL_HPP - -// In case it hasn't yet been included. -#include "softmin.hpp" - -namespace mlpack { - -template -SoftminType::SoftminType() -{ - // Nothing to do here. -} - -template -void SoftminType::Forward( - const InputType& input, - OutputType& output) -{ - InputType softminInput = arma::exp(-(input.each_row() - - arma::min(input, 0))); - output = softminInput.each_row() / sum(softminInput, 0); -} - -template -void SoftminType::Backward( - const InputType& input, - const OutputType& gy, - OutputType& g) -{ - g = input % (gy - arma::repmat(arma::sum(gy % input), input.n_rows, 1)); -} - -template -template -void SoftminType::serialize( - Archive& ar, - const uint32_t /* version */) -{ - ar(cereal::base_class>(this)); -} - -} // namespace mlpack - -#endif diff --git a/src/mlpack/methods/ann/layer/not_adapted/parametric_relu.hpp b/src/mlpack/methods/ann/layer/parametric_relu.hpp similarity index 68% rename from src/mlpack/methods/ann/layer/not_adapted/parametric_relu.hpp rename to src/mlpack/methods/ann/layer/parametric_relu.hpp index 948d0722c0..f8f99e3ee5 100644 --- a/src/mlpack/methods/ann/layer/not_adapted/parametric_relu.hpp +++ b/src/mlpack/methods/ann/layer/parametric_relu.hpp @@ -12,8 +12,8 @@ * 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_PReLU_HPP -#define MLPACK_METHODS_ANN_LAYER_PReLU_HPP +#ifndef MLPACK_METHODS_ANN_LAYER_PRELU_HPP +#define MLPACK_METHODS_ANN_LAYER_PRELU_HPP #include @@ -34,14 +34,12 @@ namespace mlpack { * \right. * @f} * - * @tparam InputType The type of the layer's inputs. The layer automatically - * cast inputs to this type (Default: arma::mat). - * @tparam OutputType The type of the computation which also causes the output - * to also be in this type. The type also allows the computation and weight - * type to differ from the input type (Default: arma::mat). + * @tparam MatType Matrix representation to accept as input and allows the + * computation and weight type to differ from the input type + * (Default: arma::mat). */ -template -class PReLUType : public Layer +template +class PReLUType : public Layer { public: /** @@ -57,8 +55,30 @@ class PReLUType : public Layer //! Clone the PReLUType object. This handles polymorphism correctly. PReLUType* Clone() const { return new PReLUType(*this); } + // Virtual destructor. + virtual ~PReLUType() { } + + //! Copy the given PReLUType. + PReLUType(const PReLUType& other); + //! Take ownership of the given PReLUType. + PReLUType(PReLUType&& other); + //! Copy the given PReLUType. + PReLUType& operator=(const PReLUType& other); + //! Take ownership of the given PReLUType. + PReLUType& operator=(PReLUType&& other); + //! Reset the layer parameter. - void SetWeights(typename OutputType::elem_type* weightsPtr); + void SetWeights(typename MatType::elem_type* weightsPtr); + + /** + * Initialize the weight matrix of the layer. + * + * @param W Weight matrix to initialize. + * @param elements Number of elements. + */ + void CustomInitialize( + MatType& W, + const size_t elements); /** * Ordinary feed forward pass of a neural network, evaluating the function @@ -67,7 +87,7 @@ class PReLUType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - void Forward(const InputType& input, OutputType& output); + void Forward(const MatType& input, MatType& output); /** * Ordinary feed backward pass of a neural network, calculating the function @@ -78,7 +98,7 @@ class PReLUType : public Layer * @param gy The backpropagated error. * @param g The calculated gradient. */ - void Backward(const InputType& input, const OutputType& gy, OutputType& g); + void Backward(const MatType& input, const MatType& gy, MatType& g); /** * Calculate the gradient using the output delta and the input activation. @@ -87,14 +107,14 @@ class PReLUType : public Layer * @param error The calculated error. * @param gradient The calculated gradient. */ - void Gradient(const InputType& input, - const OutputType& error, - OutputType& gradient); + void Gradient(const MatType& input, + const MatType& error, + MatType& gradient); //! Get the parameters. - OutputType const& Parameters() const { return alpha; } + MatType const& Parameters() const { return alpha; } //! Modify the parameters. - OutputType& Parameters() { return alpha; } + MatType& Parameters() { return alpha; } //! Get the non zero gradient. double const& Alpha() const { return alpha(0); } @@ -112,7 +132,7 @@ class PReLUType : public Layer private: //! Leakyness Parameter object. - OutputType alpha; + MatType alpha; //! Leakyness Parameter given by user in the range 0 < alpha < 1. double userAlpha; @@ -121,7 +141,7 @@ class PReLUType : public Layer // Convenience typedefs. // Standard PReLU layer. -typedef PReLUType PReLU; +typedef PReLUType PReLU; } // namespace mlpack diff --git a/src/mlpack/methods/ann/layer/parametric_relu_impl.hpp b/src/mlpack/methods/ann/layer/parametric_relu_impl.hpp new file mode 100644 index 0000000000..d0b9917e7b --- /dev/null +++ b/src/mlpack/methods/ann/layer/parametric_relu_impl.hpp @@ -0,0 +1,146 @@ +/** + * @file methods/ann/layer/parametric_relu_impl.hpp + * @author Prasanna Patil + * + * Definition of PReLU layer first introduced in the, + * Kaiming He, Xiangyu Zhang, Shaoqing, Ren Jian Sun, + * "Delving Deep into Rectifiers: + * Surpassing Human-Level Performance on ImageNet Classification", 2014 + * + * 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_PRELU_IMPL_HPP +#define MLPACK_METHODS_ANN_LAYER_PRELU_IMPL_HPP + +// In case it hasn't yet been included. +#include "parametric_relu.hpp" + +namespace mlpack { + +template +PReLUType::PReLUType(const double userAlpha) : + Layer(), + userAlpha(userAlpha) +{ + // Nothing to do here. +} + +template +PReLUType::PReLUType( + const PReLUType& other) : + Layer(other), + userAlpha(other.userAlpha) +{ + // Nothing to do here. +} + +template +PReLUType::PReLUType( + PReLUType&& other) : + Layer(std::move(other)), + userAlpha(std::move(other.userAlpha)) +{ + // Nothing to do here. +} + +template +PReLUType& +PReLUType::operator=(const PReLUType& other) +{ + if (&other != this) + { + Layer::operator=(other); + userAlpha = other.userAlpha; + } + + return *this; +} + +template +PReLUType& +PReLUType::operator=(PReLUType&& other) +{ + if (&other != this) + { + Layer::operator=(std::move(other)); + userAlpha = std::move(other.userAlpha); + } + + return *this; +} + +template +void PReLUType::SetWeights( + typename MatType::elem_type* weightsPtr) +{ + MakeAlias(alpha, weightsPtr, 1, 1); +} + +template +void PReLUType::CustomInitialize( + MatType& W, + const size_t elements) +{ + if (elements != 1) + { + throw std::invalid_argument("PReLUType::CustomInitialize(): wrong " + "elements size!"); + } + + W(0) = userAlpha; +} + +template +void PReLUType::Forward( + const MatType& input, MatType& output) +{ + output = input; + if (this->training) + { + #pragma omp for + for (size_t i = 0; i < input.n_elem; ++i) + output(i) *= (input(i) >= 0) ? 1 : alpha(0); + } +} + +template +void PReLUType::Backward( + const MatType& input, const MatType& gy, MatType& g) +{ + MatType derivative; + derivative.set_size(arma::size(input)); + #pragma omp for + for (size_t i = 0; i < input.n_elem; ++i) + derivative(i) = (input(i) >= 0) ? 1 : alpha(0); + + g = gy % derivative; +} + +template +void PReLUType::Gradient( + const MatType& input, + const MatType& error, + MatType& gradient) +{ + MatType zeros = arma::zeros(input.n_rows, input.n_cols); + gradient.set_size(1, 1); + gradient(0) = arma::accu(error % arma::min(zeros, input)) / input.n_cols; +} + +template +template +void PReLUType::serialize( + Archive& ar, + const uint32_t /* version */) +{ + ar(cereal::base_class>(this)); + + ar(CEREAL_NVP(userAlpha)); +} + +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/ann/layer/serialization.hpp b/src/mlpack/methods/ann/layer/serialization.hpp index 90b785394c..5819a5d604 100644 --- a/src/mlpack/methods/ann/layer/serialization.hpp +++ b/src/mlpack/methods/ann/layer/serialization.hpp @@ -65,8 +65,10 @@ CEREAL_REGISTER_TYPE(mlpack::MeanPoolingType<__VA_ARGS__>); \ CEREAL_REGISTER_TYPE(mlpack::NoisyLinearType<__VA_ARGS__>); \ CEREAL_REGISTER_TYPE(mlpack::PaddingType<__VA_ARGS__>); \ + CEREAL_REGISTER_TYPE(mlpack::PReLUType<__VA_ARGS__>); \ CEREAL_REGISTER_TYPE(mlpack::RBFType<__VA_ARGS__>); \ CEREAL_REGISTER_TYPE(mlpack::SoftmaxType<__VA_ARGS__>); \ + CEREAL_REGISTER_TYPE(mlpack::SoftminType<__VA_ARGS__>); \ CEREAL_REGISTER_MLPACK_LAYERS(arma::mat); diff --git a/src/mlpack/methods/ann/layer/not_adapted/softmin.hpp b/src/mlpack/methods/ann/layer/softmin.hpp similarity index 72% rename from src/mlpack/methods/ann/layer/not_adapted/softmin.hpp rename to src/mlpack/methods/ann/layer/softmin.hpp index 54b78d8e43..39cf1ee0d2 100644 --- a/src/mlpack/methods/ann/layer/not_adapted/softmin.hpp +++ b/src/mlpack/methods/ann/layer/softmin.hpp @@ -24,14 +24,11 @@ namespace mlpack { * a vector of K real numbers, rescaling them so that the elements of the * K-dimensional output vector lie in the range [0, 1] and sum to 1. * - * @tparam InputType The type of the layer's inputs. The layer automatically - * cast inputs to this type (Default: arma::mat). - * @tparam OutputType The type of the computation which also causes the output - * to also be in this type. The type also allows the computation and weight - * type to differ from the input type (Default: arma::mat). + * @tparam MatType Matrix representation to accept as input and use for + * computation. */ -template -class SoftminType : public Layer +template +class SoftminType : public Layer { public: //! Create the Softmin object. @@ -40,6 +37,18 @@ class SoftminType : public Layer //! Clone the SoftminType object. This handles polymorphism correctly. SoftminType* Clone() const { return new SoftminType(*this); } + //! Virtual destructor. + virtual ~SoftminType() { } + + //! Copy the given SoftminType. + SoftminType(const SoftminType& other); + //! Take ownership of the given SoftminType. + SoftminType(SoftminType&& other); + //! Copy the given SoftminType. + SoftminType& operator=(const SoftminType& other); + //! Take ownership of the given SoftminType. + SoftminType& operator=(SoftminType&& other); + /** * Ordinary feed forward pass of a neural network, evaluating the function * f(x) by propagating the activity forward through f. @@ -47,7 +56,7 @@ class SoftminType : public Layer * @param input Input data used for evaluating the specified function. * @param output Resulting output activation. */ - void Forward(const InputType& input, OutputType& output); + void Forward(const MatType& input, MatType& output); /** * Ordinary feed backward pass of a neural network, calculating the function @@ -58,7 +67,7 @@ class SoftminType : public Layer * @param gy The backpropagated error. * @param g The calculated gradient. */ - void Backward(const InputType& input, const OutputType& gy, OutputType& g); + void Backward(const MatType& input, const MatType& gy, MatType& g); //! Serialize the layer. template @@ -68,7 +77,7 @@ class SoftminType : public Layer // Convenience typedefs. // Standard Softmin layer using no regularization. -typedef SoftminType Softmin; +typedef SoftminType Softmin; } // namespace mlpack diff --git a/src/mlpack/methods/ann/layer/softmin_impl.hpp b/src/mlpack/methods/ann/layer/softmin_impl.hpp new file mode 100644 index 0000000000..f668566f4a --- /dev/null +++ b/src/mlpack/methods/ann/layer/softmin_impl.hpp @@ -0,0 +1,90 @@ +/** + * @file methods/ann/layer/softmin_impl.hpp + * @author Aakash Kaushik + * + * Implementation of the Softmin 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_ANN_LAYER_SOFTMIN_IMPL_HPP +#define MLPACK_METHODS_ANN_LAYER_SOFTMIN_IMPL_HPP + +// In case it hasn't yet been included. +#include "softmin.hpp" + +namespace mlpack { + +template +SoftminType::SoftminType() +{ + // Nothing to do here. +} + +template +SoftminType::SoftminType(const SoftminType& other) : + Layer(other) +{ + // Nothing to do here. +} + +template +SoftminType::SoftminType(SoftminType&& other) : + Layer(std::move(other)) +{ + // Nothing to do here. +} + +template +SoftminType& +SoftminType::operator=(const SoftminType& other) +{ + if (this != &other) + Layer::operator=(other); + + return *this; +} + +template +SoftminType& +SoftminType::operator=(SoftminType&& other) +{ + if (this != &other) + Layer::operator=(std::move(other)); + + return *this; +} + +template +void SoftminType::Forward( + const MatType& input, + MatType& output) +{ + MatType softminInput = arma::exp(-(input.each_row() - + arma::min(input, 0))); + output = softminInput.each_row() / sum(softminInput, 0); +} + +template +void SoftminType::Backward( + const MatType& input, + const MatType& gy, + MatType& g) +{ + g = input % (gy - arma::repmat(arma::sum(gy % input), input.n_rows, 1)); +} + +template +template +void SoftminType::serialize( + Archive& ar, + const uint32_t /* version */) +{ + ar(cereal::base_class>(this)); +} + +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/cf/cf.hpp b/src/mlpack/methods/cf/cf.hpp index 90bcb80338..6efb558d12 100644 --- a/src/mlpack/methods/cf/cf.hpp +++ b/src/mlpack/methods/cf/cf.hpp @@ -283,6 +283,8 @@ class CFType }; }; // class CFType +typedef CFType<> CF; + } // namespace mlpack // Include implementation of templated functions. diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp index 1378d49ef6..80107ee03d 100644 --- a/src/mlpack/methods/cf/cf_main.cpp +++ b/src/mlpack/methods/cf/cf_main.cpp @@ -72,6 +72,9 @@ BINDING_LONG_DESC( " - 'SVDCompleteIncremental' -- SVD complete incremental learning\n" " - 'BiasSVD' -- Bias SVD using a SGD optimizer\n" " - 'SVDPP' -- SVD++ using a SGD optimizer\n" + " - 'RandSVD' -- RandomizedSVD learning\n" + " - 'QSVD' -- QuicSVD learning\n" + " - 'BKSVD' -- Block Krylov SVD learning\n" "\n\n" "The following neighbor search algorithms can be specified via" + " the " + PRINT_PARAM_STRING("neighbor_search") + " parameter:" @@ -196,7 +199,7 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& timers) RequireParamInSet(params, "algorithm", { "NMF", "BatchSVD", "SVDIncompleteIncremental", "SVDCompleteIncremental", "RegSVD", - "RandSVD", "BiasSVD", "SVDPP" }, true, "unknown algorithm"); + "RandSVD", "BiasSVD", "SVDPP", "QSVD", "BKSVD" }, true, "unknown algorithm"); ReportIgnoredParam(params, {{ "iteration_only_termination", true }}, "min_residue"); @@ -282,6 +285,18 @@ void BINDING_FUNCTION(util::Params& params, util::Timers& timers) "when max_iterations is reached"); cf->DecompositionType() = CFModel::SVD_PLUS_PLUS; } + else if (algo == "QSVD") + { + ReportIgnoredParam(params, "min_residue", "QSVD terminates only " + "when max_iterations is reached"); + cf->DecompositionType() = CFModel::QUIC_SVD; + } + else if (algo == "BKSVD") + { + ReportIgnoredParam(params, "min_residue", "BKSVD terminates only " + "when max_iterations is reached"); + cf->DecompositionType() = CFModel::BLOCK_KRYLOV_SVD; + } // Perform the factorization and do whatever the user wanted. const size_t neighborhood = (size_t) params.Get("neighborhood"); diff --git a/src/mlpack/methods/cf/cf_model.hpp b/src/mlpack/methods/cf/cf_model.hpp index 30be735ddd..dde86162ae 100644 --- a/src/mlpack/methods/cf/cf_model.hpp +++ b/src/mlpack/methods/cf/cf_model.hpp @@ -170,7 +170,9 @@ class CFModel SVD_COMPLETE, SVD_INCOMPLETE, BIAS_SVD, - SVD_PLUS_PLUS + SVD_PLUS_PLUS, + QUIC_SVD, + BLOCK_KRYLOV_SVD }; enum NormalizationTypes diff --git a/src/mlpack/methods/cf/cf_model_impl.hpp b/src/mlpack/methods/cf/cf_model_impl.hpp index d7f40b1b90..8b1d05e0ca 100644 --- a/src/mlpack/methods/cf/cf_model_impl.hpp +++ b/src/mlpack/methods/cf/cf_model_impl.hpp @@ -14,29 +14,6 @@ #include "cf_model.hpp" -#include "interpolation_policies/average_interpolation.hpp" -#include "interpolation_policies/regression_interpolation.hpp" -#include "interpolation_policies/similarity_interpolation.hpp" - -#include "neighbor_search_policies/cosine_search.hpp" -#include "neighbor_search_policies/lmetric_search.hpp" -#include "neighbor_search_policies/pearson_search.hpp" - -#include "decomposition_policies/batch_svd_method.hpp" -#include "decomposition_policies/bias_svd_method.hpp" -#include "decomposition_policies/nmf_method.hpp" -#include "decomposition_policies/randomized_svd_method.hpp" -#include "decomposition_policies/regularized_svd_method.hpp" -#include "decomposition_policies/svd_complete_method.hpp" -#include "decomposition_policies/svd_incomplete_method.hpp" -#include "decomposition_policies/svdplusplus_method.hpp" - -#include "normalization/no_normalization.hpp" -#include "normalization/overall_mean_normalization.hpp" -#include "normalization/user_mean_normalization.hpp" -#include "normalization/item_mean_normalization.hpp" -#include "normalization/z_score_normalization.hpp" - namespace mlpack { inline CFModel::CFModel() : @@ -361,6 +338,12 @@ inline CFWrapperBase* InitializeModel( case CFModel::SVD_PLUS_PLUS: return InitializeModelHelper(normalizationType); + + case CFModel::QUIC_SVD: + return InitializeModelHelper(normalizationType); + + case CFModel::BLOCK_KRYLOV_SVD: + return InitializeModelHelper(normalizationType); } // This shouldn't ever happen. @@ -473,6 +456,16 @@ inline void CFModel::Train( cf = TrainHelper(SVDPlusPlusPolicy(), normalizationType, data, numUsersForSimilarity, rank, maxIterations, minResidue, mit); break; + + case QUIC_SVD: + cf = TrainHelper(QUIC_SVDPolicy(), normalizationType, data, + numUsersForSimilarity, rank, maxIterations, minResidue, mit); + break; + + case BLOCK_KRYLOV_SVD: + cf = TrainHelper(BlockKrylovSVDPolicy(), normalizationType, data, + numUsersForSimilarity, rank, maxIterations, minResidue, mit); + break; } } @@ -555,6 +548,14 @@ void CFModel::serialize(Archive& ar, const uint32_t /* version */) case SVD_PLUS_PLUS: SerializeHelper(ar, cf, normalizationType); break; + + case QUIC_SVD: + SerializeHelper(ar, cf, normalizationType); + break; + + case BLOCK_KRYLOV_SVD: + SerializeHelper(ar, cf, normalizationType); + break; } } diff --git a/src/mlpack/methods/cf/decomposition_policies/block_krylov_svd_method.hpp b/src/mlpack/methods/cf/decomposition_policies/block_krylov_svd_method.hpp new file mode 100644 index 0000000000..61b3d3f22d --- /dev/null +++ b/src/mlpack/methods/cf/decomposition_policies/block_krylov_svd_method.hpp @@ -0,0 +1,173 @@ +/** + * @file methods/cf/decomposition_policies/block_krylov_svd_method.hpp + * @author Adarsh Santoria + * + * Implementation of the block krylov svd method for use in + * Collaborative Fitlering. + * + * 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_DECOMPOSITION_POLICIES_BLOCK_KRYLOV_SVD_METHOD_HPP +#define MLPACK_METHODS_CF_DECOMPOSITION_POLICIES_BLOCK_KRYLOV_SVD_METHOD_HPP + +#include +#include + +namespace mlpack { + +/** + * Implementation of the Block Krylov SVD policy to act as a wrapper when + * using Block Krylov SVD for the decomposition type of CF. + * + * An example of how to use BlockKrylovSVDPolicy 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 users; + * arma::Mat recommendations; // Resulting recommendations. + * + * CFType cf(data); + * + * // Generate 10 recommendations for all users. + * cf.GetRecommendations(10, recommendations); + * @endcode + */ +class BlockKrylovSVDPolicy +{ + public: + /** + * Create block krylov SVD object to use for collaborative filtering. + */ + BlockKrylovSVDPolicy() + { + /* Nothing to do here */ + } + + /** + * Apply Collaborative Filtering to the provided data set using the + * block krylov SVD. + * + * @param * (data) Data matrix: dense matrix (coordinate lists) + * or sparse matrix(cleaned). + * @param cleanedData item user table in form of sparse matrix. + * @param rank Rank parameter for matrix factorization. + * @param * (maxIterations) Maximum number of iterations. + * @param * (minResidue) Residue required to terminate. + * @param * (mit) Whether to terminate only when maxIterations is reached. + */ + template + void Apply(const MatType& /* data */, + const arma::sp_mat& cleanedData, + const size_t rank, + const size_t /* maxIterations */, + const double /* minResidue */, + const bool /* mit */) + { + arma::vec sigma; + + // Preprocessed data converted to mat format + arma::mat data(cleanedData); + + // Do singular value decomposition using the block krylov SVD algorithm. + RandomizedBlockKrylovSVD blockkrylovsvd; + blockkrylovsvd.Apply(data, w, sigma, h, rank); + + // Sigma matrix is multiplied to w. + w = w * arma::diagmat(sigma); + + // Take transpose of the matrix h as required by CF class. + h = arma::trans(h); + } + + /** + * Return predicted rating given user ID and item ID. + * + * @param user User ID. + * @param item Item ID. + */ + double GetRating(const size_t user, const size_t item) const + { + double rating = arma::as_scalar(w.row(item) * h.col(user)); + return rating; + } + + /** + * Get predicted ratings for a user. + * + * @param user User ID. + * @param rating Resulting rating vector. + */ + void GetRatingOfUser(const size_t user, arma::vec& rating) const + { + rating = w * h.col(user); + } + + /** + * Get the neighborhood and corresponding similarities for a set of users. + * + * @tparam NeighborSearchPolicy The policy to perform neighbor search. + * + * @param users Users whose neighborhood is to be computed. + * @param numUsersForSimilarity The number of neighbors returned for + * each user. + * @param neighborhood Neighbors represented by user IDs. + * @param similarities Similarity between each user and each of its + * neighbors. + */ + template + void GetNeighborhood(const arma::Col& users, + const size_t numUsersForSimilarity, + arma::Mat& neighborhood, + arma::mat& similarities) const + { + // 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. + + // 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)); + + NeighborSearchPolicy neighborSearch(stretchedH); + neighborSearch.Search( + query, numUsersForSimilarity, neighborhood, similarities); + } + + //! Get the Item Matrix. + const arma::mat& W() const { return w; } + //! Get the User Matrix. + const arma::mat& H() const { return h; } + + /** + * Serialization. + */ + template + void serialize(Archive& ar, const uint32_t /* version */) + { + ar(CEREAL_NVP(w)); + ar(CEREAL_NVP(h)); + } + + private: + //! Item matrix. + arma::mat w; + //! User matrix. + arma::mat h; +}; + +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/cf/decomposition_policies/decomposition_policies.hpp b/src/mlpack/methods/cf/decomposition_policies/decomposition_policies.hpp index 731b8db8d1..4f0dab343e 100644 --- a/src/mlpack/methods/cf/decomposition_policies/decomposition_policies.hpp +++ b/src/mlpack/methods/cf/decomposition_policies/decomposition_policies.hpp @@ -20,5 +20,7 @@ #include "svd_complete_method.hpp" #include "svd_incomplete_method.hpp" #include "svdplusplus_method.hpp" +#include "quic_svd_method.hpp" +#include "block_krylov_svd_method.hpp" #endif diff --git a/src/mlpack/methods/cf/decomposition_policies/quic_svd_method.hpp b/src/mlpack/methods/cf/decomposition_policies/quic_svd_method.hpp new file mode 100644 index 0000000000..33d6cc76c3 --- /dev/null +++ b/src/mlpack/methods/cf/decomposition_policies/quic_svd_method.hpp @@ -0,0 +1,173 @@ +/** + * @file methods/cf/decomposition_policies/quic_svd_method.hpp + * @author Adarsh Santoria + * + * Implementation of the quic svd method for use in + * Collaborative Fitlering. + * + * 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_DECOMPOSITION_POLICIES_QUIC_SVD_METHOD_HPP +#define MLPACK_METHODS_CF_DECOMPOSITION_POLICIES_QUIC_SVD_METHOD_HPP + +#include +#include + +namespace mlpack { + +/** + * Implementation of the QUIC-SVD policy to act as a wrapper when + * accessing Quic SVD from within CFType. + * + * An example of how to use QUIC_SVDPolicy 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 users; + * arma::Mat recommendations; // Resulting recommendations. + * + * CFType cf(data); + * + * // Generate 10 recommendations for all users. + * cf.GetRecommendations(10, recommendations); + * @endcode + */ +class QUIC_SVDPolicy +{ + public: + /** + * Use quic SVD method to perform collaborative filtering + */ + QUIC_SVDPolicy() + { + /* Nothing to do here */ + } + + /** + * Apply Collaborative Filtering to the provided data set using the + * quic SVD. + * + * @param * (data) Data matrix: dense matrix (coordinate lists) + * or sparse matrix(cleaned). + * @param cleanedData item user table in form of sparse matrix. + * @param * (rank) Rank parameter for matrix factorization. + * @param * (maxIterations) Maximum number of iterations. + * @param * (minResidue) Residue required to terminate. + * @param * (mit) Whether to terminate only when maxIterations is reached. + */ + template + void Apply(const MatType& /* data */, + const arma::sp_mat& cleanedData, + const size_t /* rank */, + const size_t /* maxIterations */, + const double /* minResidue */, + const bool /* mit */) + { + arma::mat sigma; + + // Preprocessed data converted to mat format + arma::mat data(cleanedData); + + // Do singular value decomposition using the quic SVD algorithm. + QUIC_SVD quicsvd; + quicsvd.Apply(data, w, h, sigma); + + // Sigma matrix is multiplied to w. + w = w * sigma; + + // Take transpose of the matrix h as required by CF class. + h = arma::trans(h); + } + + /** + * Return predicted rating given user ID and item ID. + * + * @param user User ID. + * @param item Item ID. + */ + double GetRating(const size_t user, const size_t item) const + { + double rating = arma::as_scalar(w.row(item) * h.col(user)); + return rating; + } + + /** + * Get predicted ratings for a user. + * + * @param user User ID. + * @param rating Resulting rating vector. + */ + void GetRatingOfUser(const size_t user, arma::vec& rating) const + { + rating = w * h.col(user); + } + + /** + * Get the neighborhood and corresponding similarities for a set of users. + * + * @tparam NeighborSearchPolicy The policy to perform neighbor search. + * + * @param users Users whose neighborhood is to be computed. + * @param numUsersForSimilarity The number of neighbors returned for + * each user. + * @param neighborhood Neighbors represented by user IDs. + * @param similarities Similarity between each user and each of its + * neighbors. + */ + template + void GetNeighborhood(const arma::Col& users, + const size_t numUsersForSimilarity, + arma::Mat& neighborhood, + arma::mat& similarities) const + { + // 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. + + // 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)); + + NeighborSearchPolicy neighborSearch(stretchedH); + neighborSearch.Search( + query, numUsersForSimilarity, neighborhood, similarities); + } + + //! Get the Item Matrix. + const arma::mat& W() const { return w; } + //! Get the User Matrix. + const arma::mat& H() const { return h; } + + /** + * Serialization. + */ + template + void serialize(Archive& ar, const uint32_t /* version */) + { + ar(CEREAL_NVP(w)); + ar(CEREAL_NVP(h)); + } + + private: + //! Item matrix. + arma::mat w; + //! User matrix. + arma::mat h; +}; + +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/quic_svd/quic_svd.hpp b/src/mlpack/methods/quic_svd/quic_svd.hpp index bdf9d0d24a..d3c8d976a5 100644 --- a/src/mlpack/methods/quic_svd/quic_svd.hpp +++ b/src/mlpack/methods/quic_svd/quic_svd.hpp @@ -42,21 +42,20 @@ namespace mlpack { * const double epsilon = 0.01; // Relative error limit of data in subspace. * const double delta = 0.1 // Lower error bound for Monte Carlo estimate. * + * // Make a QuicSVD object. + * QUIC_SVD qSVD(); + * * arma::mat u, v, sigma; // Matrices for the factors. data = u * sigma * v.t() * - * // Get the factorization in the constructor. - * QUIC_SVD(data, u, v, sigma, epsilon, delta); + * // Use the Apply() method to get a factorization. + * qSVD.Apply(data, u, v, sigma, epsilon, delta); * @endcode */ class QUIC_SVD { public: /** - * Constructor which implements the QUIC-SVD algorithm. The function calls the - * CosineTree constructor to create a subspace basis, where the original - * matrix's projection has minimum reconstruction error. The constructor then - * uses the ExtractSVD() function to calculate the SVD of the original dataset - * in that subspace. + * Create object for the randomized SVD method. * * @param dataset Matrix for which SVD is calculated. * @param u First unitary matrix. @@ -72,6 +71,35 @@ class QUIC_SVD const double epsilon = 0.03, const double delta = 0.1); + /** + * Create object for the QUIC-SVD method. + * + * @param epsilon Error tolerance fraction for calculated subspace. + * @param delta Cumulative probability for Monte Carlo error lower bound. + */ + QUIC_SVD(const double epsilon = 0.03, + const double delta = 0.1); + + /** + * The function calls the CosineTree constructor to create a subspace basis, + * where the original matrix's projection has minimum reconstruction error. + * The constructor then uses the ExtractSVD() function to calculate the SVD + * of the original dataset in that subspace. + * + * @param dataset Matrix for which SVD is calculated. + * @param u First unitary matrix. + * @param v Second unitary matrix. + * @param sigma Diagonal matrix of singular values. + * @param epsilon Error tolerance fraction for calculated subspace. + * @param delta Cumulative probability for Monte Carlo error lower bound. + */ + void Apply(const arma::mat& dataset, + arma::mat& u, + arma::mat& v, + arma::mat& sigma, + const double epsilon = 0.03, + const double delta = 0.1); + /** * This function uses the vector subspace created using a cosine tree to * calculate an approximate SVD of the original matrix. @@ -80,11 +108,12 @@ class QUIC_SVD * @param v Second unitary matrix. * @param sigma Diagonal matrix of singular values. */ - void ExtractSVD(arma::mat& u, arma::mat& v, arma::mat& sigma); + void ExtractSVD(const arma::mat& dataset, + arma::mat& u, + arma::mat& v, + arma::mat& sigma); private: - //! Matrix for which cosine tree is constructed. - const arma::mat& dataset; //! Subspace basis of the input dataset. arma::mat basis; }; diff --git a/src/mlpack/methods/quic_svd/quic_svd_impl.hpp b/src/mlpack/methods/quic_svd/quic_svd_impl.hpp index 9d009c9751..eb010c6e16 100644 --- a/src/mlpack/methods/quic_svd/quic_svd_impl.hpp +++ b/src/mlpack/methods/quic_svd/quic_svd_impl.hpp @@ -23,8 +23,25 @@ inline QUIC_SVD::QUIC_SVD( arma::mat& v, arma::mat& sigma, const double epsilon, - const double delta) : - dataset(dataset) + const double delta) +{ + Apply(dataset, u, v, sigma, epsilon, delta); +} + +inline QUIC_SVD::QUIC_SVD( + const double epsilon, + const double delta) +{ + /* Nothing to do here */ +} + +inline void QUIC_SVD::Apply( + const arma::mat& dataset, + arma::mat& u, + arma::mat& v, + arma::mat& sigma, + const double epsilon, + const double delta) { // Since columns are sample in the implementation, the matrix is transposed if // necessary for maximum speedup. @@ -42,10 +59,11 @@ inline QUIC_SVD::QUIC_SVD( // Use the ExtractSVD algorithm mentioned in the paper to extract the SVD of // the original dataset in the obtained subspace. - ExtractSVD(u, v, sigma); + ExtractSVD(dataset, u, v, sigma); } -inline void QUIC_SVD::ExtractSVD(arma::mat& u, +inline void QUIC_SVD::ExtractSVD(const arma::mat& dataset, + arma::mat& u, arma::mat& v, arma::mat& sigma) { diff --git a/src/mlpack/methods/randomized_svd/randomized_svd.hpp b/src/mlpack/methods/randomized_svd/randomized_svd.hpp index 99646bec7c..652fe3032a 100644 --- a/src/mlpack/methods/randomized_svd/randomized_svd.hpp +++ b/src/mlpack/methods/randomized_svd/randomized_svd.hpp @@ -152,83 +152,7 @@ class RandomizedSVD arma::vec& s, arma::mat& v, const size_t rank, - MatType rowMean) - { - if (iteratedPower == 0) - iteratedPower = rank + 2; - - arma::mat R, Q, Qdata; - - // Apply the centered data matrix to a random matrix, obtaining Q. - if (data.n_cols >= data.n_rows) - { - R = arma::randn(data.n_rows, iteratedPower); - Q = (data.t() * R) - arma::repmat(arma::trans(R.t() * rowMean), - data.n_cols, 1); - } - else - { - R = arma::randn(data.n_cols, iteratedPower); - Q = (data * R) - (rowMean * (arma::ones(1, data.n_cols) * R)); - } - - // Form a matrix Q whose columns constitute a - // well-conditioned basis for the columns of the earlier Q. - if (maxIterations == 0) - { - arma::qr_econ(Q, v, Q); - } - else - { - arma::lu(Q, v, Q); - } - - // Perform normalized power iterations. - for (size_t i = 0; i < maxIterations; ++i) - { - if (data.n_cols >= data.n_rows) - { - Q = (data * Q) - rowMean * (arma::ones(1, data.n_cols) * Q); - arma::lu(Q, v, Q); - Q = (data.t() * Q) - arma::repmat(rowMean.t() * Q, data.n_cols, 1); - } - else - { - Q = (data.t() * Q) - arma::repmat(rowMean.t() * Q, data.n_cols, 1); - arma::lu(Q, v, Q); - Q = (data * Q) - (rowMean * (arma::ones(1, data.n_cols) * Q)); - } - - // Computing the LU decomposition is more efficient than computing the QR - // decomposition, so we only use it in the last iteration, a pivoted QR - // decomposition which renormalizes Q, ensuring that the columns of Q are - // orthonormal. - if (i < (maxIterations - 1)) - { - arma::lu(Q, v, Q); - } - else - { - arma::qr_econ(Q, v, Q); - } - } - - // Do economical singular value decomposition and compute only the - // approximations of the left singular vectors by using the centered data - // applied to Q. - if (data.n_cols >= data.n_rows) - { - Qdata = (data * Q) - rowMean * (arma::ones(1, data.n_cols) * Q); - arma::svd_econ(u, s, v, Qdata); - v = Q * v; - } - else - { - Qdata = (Q.t() * data) - arma::repmat(Q.t() * rowMean, 1, data.n_cols); - arma::svd_econ(u, s, v, Qdata); - u = Q * u; - } - } + MatType rowMean); //! Get the size of the normalized power iterations. size_t IteratedPower() const { return iteratedPower; } diff --git a/src/mlpack/methods/randomized_svd/randomized_svd_impl.hpp b/src/mlpack/methods/randomized_svd/randomized_svd_impl.hpp index 6007048ae7..76569a0993 100644 --- a/src/mlpack/methods/randomized_svd/randomized_svd_impl.hpp +++ b/src/mlpack/methods/randomized_svd/randomized_svd_impl.hpp @@ -51,7 +51,6 @@ inline RandomizedSVD::RandomizedSVD( /* Nothing to do here */ } - inline void RandomizedSVD::Apply(const arma::sp_mat& data, arma::mat& u, arma::vec& s, @@ -76,6 +75,90 @@ inline void RandomizedSVD::Apply(const arma::mat& data, Apply(data, u, s, v, rank, rowMean); } +template +inline void RandomizedSVD::Apply(const MatType& data, + arma::mat& u, + arma::vec& s, + arma::mat& v, + const size_t rank, + MatType rowMean) +{ + if (iteratedPower == 0) + iteratedPower = rank + 2; + + arma::mat R, Q, Qdata; + + // Apply the centered data matrix to a random matrix, obtaining Q. + if (data.n_cols >= data.n_rows) + { + R = arma::randn(data.n_rows, iteratedPower); + Q = (data.t() * R) - arma::repmat(arma::trans(R.t() * rowMean), + data.n_cols, 1); + } + else + { + R = arma::randn(data.n_cols, iteratedPower); + Q = (data * R) - (rowMean * (arma::ones(1, data.n_cols) * R)); + } + + // Form a matrix Q whose columns constitute a + // well-conditioned basis for the columns of the earlier Q. + if (maxIterations == 0) + { + arma::qr_econ(Q, v, Q); + } + else + { + arma::lu(Q, v, Q); + } + + // Perform normalized power iterations. + for (size_t i = 0; i < maxIterations; ++i) + { + if (data.n_cols >= data.n_rows) + { + Q = (data * Q) - rowMean * (arma::ones(1, data.n_cols) * Q); + arma::lu(Q, v, Q); + Q = (data.t() * Q) - arma::repmat(rowMean.t() * Q, data.n_cols, 1); + } + else + { + Q = (data.t() * Q) - arma::repmat(rowMean.t() * Q, data.n_cols, 1); + arma::lu(Q, v, Q); + Q = (data * Q) - (rowMean * (arma::ones(1, data.n_cols) * Q)); + } + + // Computing the LU decomposition is more efficient than computing the QR + // decomposition, so we only use it in the last iteration, a pivoted QR + // decomposition which renormalizes Q, ensuring that the columns of Q are + // orthonormal. + if (i < (maxIterations - 1)) + { + arma::lu(Q, v, Q); + } + else + { + arma::qr_econ(Q, v, Q); + } + } + + // Do economical singular value decomposition and compute only the + // approximations of the left singular vectors by using the centered data + // applied to Q. + if (data.n_cols >= data.n_rows) + { + Qdata = (data * Q) - rowMean * (arma::ones(1, data.n_cols) * Q); + arma::svd_econ(u, s, v, Qdata); + v = Q * v; + } + else + { + Qdata = (Q.t() * data) - arma::repmat(Q.t() * rowMean, 1, data.n_cols); + arma::svd_econ(u, s, v, Qdata); + u = Q * u; + } +} + } // namespace mlpack #endif diff --git a/src/mlpack/methods/svdplusplus/svdplusplus.hpp b/src/mlpack/methods/svdplusplus/svdplusplus.hpp index 2bffd02e4c..0d3a7d7bf9 100644 --- a/src/mlpack/methods/svdplusplus/svdplusplus.hpp +++ b/src/mlpack/methods/svdplusplus/svdplusplus.hpp @@ -21,7 +21,7 @@ namespace mlpack { /** - * SVD++ is a matrix decomposition tenique used in collaborative filtering. + * SVD++ is a matrix decomposition technique used in collaborative filtering. * SVD++ is similar to BiasSVD, but it is a more expressive model because * SVD++ also models implicit feedback. SVD++ outputs user/item latent * vectors, user/item bias, and item vectors with regard to implicit feedback. diff --git a/src/mlpack/tests/ann/layer/concat.cpp b/src/mlpack/tests/ann/layer/concat.cpp new file mode 100644 index 0000000000..c20b07bef4 --- /dev/null +++ b/src/mlpack/tests/ann/layer/concat.cpp @@ -0,0 +1,227 @@ +/** + * @file tests/ann/layer/concat.cpp + * @author Marcus Edel + * @author Praveen Ch + * + * Tests the ann layer modules. + * + * 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 +#include + +#include "../../test_catch_tools.hpp" +#include "../../catch.hpp" +#include "../../serialization.hpp" +#include "../ann_test_tools.hpp" + +using namespace mlpack; + +/** + * Simple concat module test. + */ +TEST_CASE("SimpleConcatLayerTest", "[ANNLayerTest]") +{ + arma::mat output, input, delta, error; + + Linear* moduleA = new Linear(10); + moduleA->InputDimensions() = std::vector({ 10 }); + moduleA->ComputeOutputDimensions(); + arma::mat weightsA(moduleA->WeightSize(), 1); + moduleA->SetWeights((double*) weightsA.memptr()); + moduleA->Parameters().randu(); + + Linear* moduleB = new Linear(10); + moduleB->InputDimensions() = std::vector({ 10 }); + moduleB->ComputeOutputDimensions(); + arma::mat weightsB(moduleB->WeightSize(), 1); + moduleB->SetWeights((double*) weightsB.memptr()); + moduleB->Parameters().randu(); + + Concat module; + module.Add(moduleA); + module.Add(moduleB); + module.InputDimensions() = std::vector({ 10 }); + module.ComputeOutputDimensions(); + + // Test the Forward function. + input = arma::zeros(10, 1); + output.set_size(module.OutputSize(), 1); + module.Forward(input, output); + + const double sumModuleA = arma::accu( + moduleA->Parameters().submat( + 100, 0, moduleA->Parameters().n_elem - 1, 0)); + const double sumModuleB = arma::accu( + moduleB->Parameters().submat( + 100, 0, moduleB->Parameters().n_elem - 1, 0)); + REQUIRE(sumModuleA + sumModuleB == + Approx(arma::accu(output.col(0))).epsilon(1e-5)); + + // Test the Backward function. + error = arma::zeros(20, 1); + delta.set_size(input.n_rows, input.n_cols); + module.Backward(input, error, delta); + REQUIRE(arma::accu(delta) == 0); +} + +/** + * Test to check Concat layer along different axes. + */ +TEST_CASE("ConcatAlongAxisTest", "[ANNLayerTest]") +{ + arma::mat output, input, error, outputA, outputB; + size_t inputWidth = 4, inputHeight = 4, inputChannel = 2; + size_t outputWidth, outputHeight, outputChannel = 2; + size_t kW = 3, kH = 3; + size_t batch = 1; + + // Using Convolution<> layer as inout to Concat<> layer. + // Compute the output shape of convolution layer. + outputWidth = (inputWidth - kW) + 1; + outputHeight = (inputHeight - kH) + 1; + + input = arma::ones(inputWidth * inputHeight * inputChannel, batch); + + Convolution* moduleA = new Convolution(outputChannel, kW, kH, 1, 1, 0, 0); + Convolution* moduleB = new Convolution(outputChannel, kW, kH, 1, 1, 0, 0); + + moduleA->InputDimensions() = std::vector({ inputWidth, inputHeight }); + moduleA->ComputeOutputDimensions(); + arma::mat weightsA(moduleA->WeightSize(), 1); + moduleA->SetWeights((double*) weightsA.memptr()); + moduleA->Parameters().randu(); + + moduleB->InputDimensions() = std::vector({ inputWidth, inputHeight }); + moduleB->ComputeOutputDimensions(); + arma::mat weightsB(moduleB->WeightSize(), 1); + moduleB->SetWeights((double*) weightsB.memptr()); + moduleB->Parameters().randu(); + + // Compute output of each layer. + outputA.set_size(moduleA->OutputSize(), 1); + outputB.set_size(moduleB->OutputSize(), 1); + moduleA->Forward(input, outputA); + moduleB->Forward(input, outputB); + + arma::cube A(outputA.memptr(), outputWidth, outputHeight, outputChannel); + arma::cube B(outputB.memptr(), outputWidth, outputHeight, outputChannel); + + error = arma::ones(outputWidth * outputHeight * outputChannel * 2, 1); + + for (size_t axis = 0; axis < 3; ++axis) + { + size_t x = 1, y = 1, z = 1; + arma::cube calculatedOut; + if (axis == 0) + { + calculatedOut.set_size(2 * outputWidth, outputHeight, outputChannel); + for (size_t i = 0; i < A.n_slices; ++i) + { + arma::mat aMat = A.slice(i); + arma::mat bMat = B.slice(i); + calculatedOut.slice(i) = arma::join_cols(aMat, bMat); + } + x = 2; + } + if (axis == 1) + { + calculatedOut.set_size(outputWidth, 2 * outputHeight, outputChannel); + for (size_t i = 0; i < A.n_slices; ++i) + { + arma::mat aMat = A.slice(i); + arma::mat bMat = B.slice(i); + calculatedOut.slice(i) = arma::join_rows(aMat, bMat); + } + y = 2; + } + if (axis == 2) + { + calculatedOut = arma::join_slices(A, B); + z = 2; + } + + // Compute output of Concat<> layer. + Concat module(axis); + module.Add(moduleA); + module.Add(moduleB); + module.InputDimensions() = std::vector({ inputWidth, inputHeight }); + module.ComputeOutputDimensions(); + output.set_size(module.OutputSize(), 1); + module.Forward(input, output); + arma::cube concatOut(output.memptr(), x * outputWidth, + y * outputHeight, z * outputChannel); + + // Verify if the output reshaped to cubes are similar. + CheckMatrices(concatOut, calculatedOut, 1e-12); + + // Ensure that the child layers don't get deleted when `module` is + // deallocated. + module.Network().clear(); + } + + delete moduleA; + delete moduleB; +} + +/** + * Test that the function that can access the axis parameter of the + * Concat layer works. + */ +TEST_CASE("ConcatLayerParametersTest", "[ANNLayerTest]") +{ + Concat layer(2); + + // Make sure we can get the parameters successfully. + REQUIRE(layer.Axis() == 2); +} + +/** + * Concat layer numerical gradient test. + */ +TEST_CASE("GradientConcatLayerTest", "[ANNLayerTest]") +{ + // Concat function gradient instantiation. + struct GradientFunction + { + GradientFunction() : + input(arma::randu(10, 1)), + target(arma::mat("0")) + { + model = new FFN(); + model->ResetData(input, target); + model->Add(10); + + concat = new Concat(); + concat->Add(5); + concat->Add(5); + model->Add(concat); + model->Add(2); + + model->Add(); + } + + ~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* model; + Concat* concat; + arma::mat input, target; + } function; + + REQUIRE(CheckGradient(function) <= 1e-4); +} diff --git a/src/mlpack/tests/ann/layer/concatenate.cpp b/src/mlpack/tests/ann/layer/concatenate.cpp new file mode 100644 index 0000000000..0cbcb3ef2b --- /dev/null +++ b/src/mlpack/tests/ann/layer/concatenate.cpp @@ -0,0 +1,95 @@ +/** + * @file tests/ann/layer/concatenate.cpp + * @author Marcus Edel + * @author Praveen Ch + * + * Tests the ann layer modules. + * + * 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 +#include + +#include "../../test_catch_tools.hpp" +#include "../../catch.hpp" +#include "../../serialization.hpp" +#include "../ann_test_tools.hpp" + +using namespace mlpack; + + +/** + * Simple concatenate module test. + */ +TEST_CASE("SimpleConcatenateLayerTest", "[ANNLayerTest]") +{ + arma::mat input = arma::ones(5, 1); + arma::mat output, delta; + + Concatenate module; + module.Concat() = arma::ones(5, 1) * 0.5; + module.InputDimensions() = std::vector({ 5 }); + module.ComputeOutputDimensions(); + + // Test the Forward function. + output.set_size(module.OutputSize(), 1); + module.Forward(input, output); + + REQUIRE(arma::accu(output) == 7.5); + + // Test the Backward function. + delta.set_size(5, 1); + module.Backward(input, output, delta); + REQUIRE(arma::accu(delta) == 5); +} + +/** + * Concatenate layer numerical gradient test. + */ +TEST_CASE("GradientConcatenateLayerTest", "[ANNLayerTest]") +{ + // Concatenate function gradient instantiation. + struct GradientFunction + { + GradientFunction() : + input(arma::randu(10, 1)), + target(arma::mat("0")) + { + model = new FFN(); + model->ResetData(input, target); + model->Add(5); + + arma::mat concat = arma::ones(5, 1); + // concatenate = new Concatenate(); + // concatenate->Concat() = concat; + // model->Add(concatenate); + model->Add(concat); + + model->Add(5); + model->Add(); + } + + ~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* model; + Concatenate* concatenate; + arma::mat input, target; + } function; + + REQUIRE(CheckGradient(function) <= 1e-4); +} diff --git a/src/mlpack/tests/ann/layer/parametric_relu.cpp b/src/mlpack/tests/ann/layer/parametric_relu.cpp new file mode 100644 index 0000000000..1a9c610200 --- /dev/null +++ b/src/mlpack/tests/ann/layer/parametric_relu.cpp @@ -0,0 +1,96 @@ +/** + * @file tests/ann/layer/parametric_relu.cpp + * @author Adarsh Santoria + * + * Tests the parametric relu layer modules. + * + * 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 +#include + +#include "../../test_catch_tools.hpp" +#include "../../catch.hpp" +#include "../../serialization.hpp" +#include "../ann_test_tools.hpp" + +using namespace mlpack; + +/** + * PReLU FORWARD Test. + */ +TEST_CASE("PReLUFORWARDTest", "[ANNLayerTest]") +{ + arma::mat input = {{0.5, 1.2, 3.1}, + {-2.2, -1.5, 0.8}, + {5.5, -4.7, 2.1}, + {0.2, 0.1, -0.5}}; + PReLU module(0.01); + module.Training() = true; + arma::mat moduleParams(module.WeightSize(), 1); + module.CustomInitialize(moduleParams, module.WeightSize()); + module.SetWeights((double*) moduleParams.memptr()); + arma::mat predOutput; + module.Forward(input, predOutput); + arma::mat actualOutput = {{0.5, 1.2, 3.1}, + {-0.022, -0.015, 0.8}, + {5.5, -0.047, 2.1}, + {0.2, 0.1, -0.005}}; + REQUIRE(arma::accu(arma::abs(actualOutput - predOutput)) == + Approx(0.0).margin(1e-4)); +} + +/** + * PReLU BACKWARD Test. + */ +TEST_CASE("PReLUBACKWARDTest", "[ANNLayerTest]") +{ + arma::mat input = {{0.5, 1.2, 3.1}, + {-2.2, -1.5, 0.8}, + {5.5, -4.7, 2.1}, + {0.2, 0.1, -0.5}}; + PReLU module(0.01); + arma::mat moduleParams(module.WeightSize(), 1); + module.CustomInitialize(moduleParams, module.WeightSize()); + module.SetWeights((double*) moduleParams.memptr()); + arma::mat gy = {{0.2, -0.5, 0.8}, + {1.5, -0.6, 0.1}, + {-0.3, 0.2, -0.5}, + {0.1, -0.1, 0.3}}; + arma::mat predG; + module.Backward(input, gy, predG); + arma::mat actualG = {{0.2, -0.5, 0.8}, + {0.015, -0.006, 0.1}, + {-0.3, 0.002, -0.5}, + {0.1, -0.1, 0.0030}}; + + REQUIRE(arma::accu(arma::abs(actualG - predG)) == + Approx(0.0).margin(1e-4)); +} + +/** + * PReLU GRADIENT Test. + */ +TEST_CASE("PReLUGRADIENTTest", "[ANNLayerTest]") +{ + arma::mat input = {{0.5, 1.2, 3.1}, + {-2.2, -1.5, 0.8}, + {5.5, -4.7, 2.1}, + {0.2, 0.1, -0.5}}; + PReLU module(0.01); + arma::mat moduleParams(module.WeightSize(), 1); + module.CustomInitialize(moduleParams, module.WeightSize()); + module.SetWeights((double*) moduleParams.memptr()); + arma::mat error = {{0.2, -0.5, 0.8}, + {-0.015, -0.006, 0.001}, + {-0.3, 0.002, -0.005}, + {0.1, -0.1, 0.0035}}; + arma::mat predGradient; + module.Gradient(input, error, predGradient); + + REQUIRE(0.0103 - arma::accu(predGradient) == + Approx(0.0).margin(1e-4)); +} diff --git a/src/mlpack/tests/ann/layer/softmin.cpp b/src/mlpack/tests/ann/layer/softmin.cpp new file mode 100644 index 0000000000..c11f446964 --- /dev/null +++ b/src/mlpack/tests/ann/layer/softmin.cpp @@ -0,0 +1,57 @@ +/** + * @file tests/ann/layer/softmin.cpp + * @author Aditya Raj + * + * Tests the ann layer modules. + * + * 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 +#include + +#include "../../test_catch_tools.hpp" +#include "../../catch.hpp" +#include "../../serialization.hpp" +#include "../ann_test_tools.hpp" + +using namespace mlpack; +using namespace std; + +/** + * Simple Softmin module test. + */ +TEST_CASE("SimpleSoftminLayerTest", "[ANNLayerTest]") +{ + arma::mat input, output, gy, g; + Softmin module; + + // Test the forward function. + input = {{0.0, 0.1, 0.2}, + {1.0, 1.1, 1.2}, + {2.0, 2.1, 2.2}, + {2.9, 2.8, 2.5}}; + arma::mat actualOutput = {{0.641750, 0.636772, 0.623646}, + {0.236086, 0.234255, 0.229426}, + {0.086851, 0.086177, 0.084401}, + {0.035311, 0.042794, 0.062526}}; + + module.Forward(input, output); + REQUIRE(arma::accu(arma::abs(actualOutput - output)) == + Approx(0.0).margin(1e-4)); + + // Test the backward function. + gy = arma::zeros(input.n_rows, input.n_cols); + gy(1) = 1; + module.Backward(output, gy, g); + arma::mat calculatedGradient = {{-0.1515, 0, 0}, + {0.1803, 0, 0}, + {-0.0205, 0, 0}, + {-0.0083, 0, 0}}; + + REQUIRE(arma::accu(arma::abs(calculatedGradient - g)) == + Approx(0.0).margin(1e-04)); + +} diff --git a/src/mlpack/tests/ann/layer_test.cpp b/src/mlpack/tests/ann/layer_test.cpp index e22bf8573f..73456b23e9 100644 --- a/src/mlpack/tests/ann/layer_test.cpp +++ b/src/mlpack/tests/ann/layer_test.cpp @@ -22,6 +22,8 @@ #include "layer/alpha_dropout.cpp" #include "layer/batch_norm.cpp" #include "layer/convolution.cpp" +#include "layer/concat.cpp" +#include "layer/concatenate.cpp" #include "layer/dropout.cpp" #include "layer/grouped_convolution.cpp" #include "layer/identity.cpp" @@ -31,4 +33,6 @@ #include "layer/max_pooling.cpp" #include "layer/mean_pooling.cpp" #include "layer/padding.cpp" +#include "layer/parametric_relu.cpp" #include "layer/softmax.cpp" +#include "layer/softmin.cpp" diff --git a/src/mlpack/tests/ann/loss_functions_test.cpp b/src/mlpack/tests/ann/loss_functions_test.cpp index 9c6695eef0..2afa26fe3a 100644 --- a/src/mlpack/tests/ann/loss_functions_test.cpp +++ b/src/mlpack/tests/ann/loss_functions_test.cpp @@ -1251,3 +1251,24 @@ TEST_CASE("NegativeLogLikelihoodLossTest", "[LossFunctionsTest]") REQUIRE(output.n_cols == input.n_cols); CheckMatrices(output, expectedOutput, 0.1); } + +/** + * Jacobian negative log likelihood module test. + */ +TEST_CASE("JacobianNegativeLogLikelihoodLayerTest", "[LossFunctionsTest]") +{ + for (size_t i = 0; i < 5; ++i) + { + NegativeLogLikelihood module; + const size_t inputElements = RandInt(5, 100); + arma::mat input; + RandomInitialization init(0, 1); + init.Initialize(input, inputElements, 1); + + arma::mat target(1, 1); + target(0) = RandInt(0, inputElements - 2); + + double error = JacobianPerformanceTest(module, input, target); + REQUIRE(error <= 1e-5); + } +} diff --git a/src/mlpack/tests/ann/not_adapted/activation_functions_test.cpp b/src/mlpack/tests/ann/not_adapted/activation_functions_test.cpp index 4ba0505549..8a8b27b179 100644 --- a/src/mlpack/tests/ann/not_adapted/activation_functions_test.cpp +++ b/src/mlpack/tests/ann/not_adapted/activation_functions_test.cpp @@ -87,79 +87,6 @@ void CheckHardTanHDerivativeCorrect(const arma::colvec input, } }*/ -/** - * Implementation of the PReLU activation function test. The function - * is implemented as PReLU layer in the file parametric_relu.hpp. - * - * @param input Input data used for evaluating the PReLU activation - * function. - * @param target Target data used to evaluate the PReLU activation. - * -void CheckPReLUActivationCorrect(const arma::colvec input, - const arma::colvec target) -{ - PReLU<> prelu; - - // Test the activation function using the entire vector as input. - arma::colvec activations; - prelu.Forward(input, activations); - for (size_t i = 0; i < activations.n_elem; ++i) - { - REQUIRE(activations.at(i) == Approx(target.at(i)).epsilon(1e-5)); - } -}*/ - -/** - * Implementation of the PReLU activation function derivative test. - * The function is implemented as PReLU layer in the file - * parametric_relu.hpp - * - * @param input Input data used for evaluating the PReLU activation - * function. - * @param target Target data used to evaluate the PReLU activation. - * -void CheckPReLUDerivativeCorrect(const arma::colvec input, - const arma::colvec target) -{ - PReLU<> prelu; - - // Test the calculation of the derivatives using the entire vector as input. - arma::colvec derivatives; - - // This error vector will be set to 1 to get the derivatives. - arma::colvec error = arma::ones(input.n_elem); - prelu.Backward(input, error, derivatives); - for (size_t i = 0; i < derivatives.n_elem; ++i) - { - REQUIRE(derivatives.at(i) == Approx(target.at(i)).epsilon(1e-5)); - } -}*/ - -/** - * Implementation of the PReLU activation function gradient test. - * The function is implemented as PReLU layer in the file - * parametric_relu.hpp - * - * @param input Input data used for evaluating the PReLU activation - * function. - * @param target Target data used to evaluate the PReLU gradient. - * -void CheckPReLUGradientCorrect(const arma::colvec input, - const arma::colvec target) -{ - PReLU<> prelu; - - // Test the calculation of the derivatives using the entire vector as input. - arma::colvec gradient; - - // This error vector will be set to 1 to get the gradient. - arma::colvec error = arma::ones(input.n_elem); - prelu.Gradient(input, error, gradient); - REQUIRE(gradient.n_rows == 1); - REQUIRE(gradient.n_cols == 1); - REQUIRE(gradient(0) == Approx(target(0)).epsilon(1e-5)); -}*/ - /** * Implementation of the Hard Shrink activation function test. The function is * implemented as Hard Shrink layer in the file hardshrink.hpp @@ -460,23 +387,6 @@ TEST_CASE("HardTanHFunctionTest", "[ActivationFunctionsTest]") CheckHardTanHDerivativeCorrect(activationData, desiredDerivatives); }*/ -/** - * Basic test of the PReLU function. - * -TEST_CASE("PReLUFunctionTest", "[ActivationFunctionsTest]") -{ - const arma::colvec desiredActivations("-0.06 3.2 4.5 -3.006 \ - 1 -0.03 2 0"); - - const arma::colvec desiredDerivatives("0.03 1 1 0.03 \ - 1 0.03 1 1"); - const arma::colvec desiredGradient("-103.2"); - - CheckPReLUActivationCorrect(activationData, desiredActivations); - CheckPReLUDerivativeCorrect(desiredActivations, desiredDerivatives); - CheckPReLUGradientCorrect(activationData, desiredGradient); -}*/ - /** * Basic test of the CReLU function. * diff --git a/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp b/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp index c1d4a242fd..cfd9ec0464 100644 --- a/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp +++ b/src/mlpack/tests/ann/not_adapted/ann_layer_test.cpp @@ -386,27 +386,6 @@ TEST_CASE("ConstantLayerParametersTest", "[ANNLayerTest]") // REQUIRE(CheckGradient(function) <= 1e-4); // } -// /** -// * Jacobian negative log likelihood module test. -// */ -// TEST_CASE("JacobianNegativeLogLikelihoodLayerTest", "[ANNLayerTest]") -// { -// for (size_t i = 0; i < 5; ++i) -// { -// NegativeLogLikelihood module; -// const size_t inputElements = RandInt(5, 100); -// arma::mat input; -// RandomInitialization init(0, 1); -// init.Initialize(input, inputElements, 1); - -// arma::mat target(1, 1); -// target(0) = RandInt(0, inputElements - 2); - -// double error = JacobianPerformanceTest(module, input, target); -// REQUIRE(error <= 1e-5); -// } -// } - /** * Jacobian LeakyReLU module test. * @@ -1848,285 +1827,6 @@ TEST_CASE("SimpleJoinLayerTest", "[ANNLayerTest]") // boost::apply_visitor(DeleteVisitor(), layer); // } -/** - * Simple concat module test. - */ -TEST_CASE("SimpleConcatLayerTest", "[ANNLayerTest]") -{ - arma::mat output, input, delta, error; - - Linear* moduleA = new Linear(10); - moduleA->InputDimensions() = std::vector({ 10 }); - moduleA->ComputeOutputDimensions(); - arma::mat weightsA(moduleA->WeightSize(), 1); - moduleA->SetWeights((double*) weightsA.memptr()); - moduleA->Parameters().randu(); - - Linear* moduleB = new Linear(10); - moduleB->InputDimensions() = std::vector({ 10 }); - moduleB->ComputeOutputDimensions(); - arma::mat weightsB(moduleB->WeightSize(), 1); - moduleB->SetWeights((double*) weightsB.memptr()); - moduleB->Parameters().randu(); - - Concat module; - module.Add(moduleA); - module.Add(moduleB); - module.InputDimensions() = std::vector({ 10 }); - module.ComputeOutputDimensions(); - - // Test the Forward function. - input = arma::zeros(10, 1); - output.set_size(module.OutputSize(), 1); - module.Forward(input, output); - - const double sumModuleA = arma::accu( - moduleA->Parameters().submat( - 100, 0, moduleA->Parameters().n_elem - 1, 0)); - const double sumModuleB = arma::accu( - moduleB->Parameters().submat( - 100, 0, moduleB->Parameters().n_elem - 1, 0)); - REQUIRE(sumModuleA + sumModuleB == - Approx(arma::accu(output.col(0))).epsilon(1e-5)); - - // Test the Backward function. - error = arma::zeros(20, 1); - delta.set_size(input.n_rows, input.n_cols); - module.Backward(input, error, delta); - REQUIRE(arma::accu(delta) == 0); -} - -/** - * Test to check Concat layer along different axes. - */ -TEST_CASE("ConcatAlongAxisTest", "[ANNLayerTest]") -{ - arma::mat output, input, error, outputA, outputB; - size_t inputWidth = 4, inputHeight = 4, inputChannel = 2; - size_t outputWidth, outputHeight, outputChannel = 2; - size_t kW = 3, kH = 3; - size_t batch = 1; - - // Using Convolution<> layer as inout to Concat<> layer. - // Compute the output shape of convolution layer. - outputWidth = (inputWidth - kW) + 1; - outputHeight = (inputHeight - kH) + 1; - - input = arma::ones(inputWidth * inputHeight * inputChannel, batch); - - Convolution* moduleA = new Convolution(outputChannel, kW, kH, 1, 1, 0, 0); - Convolution* moduleB = new Convolution(outputChannel, kW, kH, 1, 1, 0, 0); - - moduleA->InputDimensions() = std::vector({ inputWidth, inputHeight }); - moduleA->ComputeOutputDimensions(); - arma::mat weightsA(moduleA->WeightSize(), 1); - moduleA->SetWeights((double*) weightsA.memptr()); - moduleA->Parameters().randu(); - - moduleB->InputDimensions() = std::vector({ inputWidth, inputHeight }); - moduleB->ComputeOutputDimensions(); - arma::mat weightsB(moduleB->WeightSize(), 1); - moduleB->SetWeights((double*) weightsB.memptr()); - moduleB->Parameters().randu(); - - // Compute output of each layer. - outputA.set_size(moduleA->OutputSize(), 1); - outputB.set_size(moduleB->OutputSize(), 1); - moduleA->Forward(input, outputA); - moduleB->Forward(input, outputB); - - arma::cube A(outputA.memptr(), outputWidth, outputHeight, outputChannel); - arma::cube B(outputB.memptr(), outputWidth, outputHeight, outputChannel); - - error = arma::ones(outputWidth * outputHeight * outputChannel * 2, 1); - - for (size_t axis = 0; axis < 3; ++axis) - { - size_t x = 1, y = 1, z = 1; - arma::cube calculatedOut; - if (axis == 0) - { - calculatedOut.set_size(2 * outputWidth, outputHeight, outputChannel); - for (size_t i = 0; i < A.n_slices; ++i) - { - arma::mat aMat = A.slice(i); - arma::mat bMat = B.slice(i); - calculatedOut.slice(i) = arma::join_cols(aMat, bMat); - } - x = 2; - } - if (axis == 1) - { - calculatedOut.set_size(outputWidth, 2 * outputHeight, outputChannel); - for (size_t i = 0; i < A.n_slices; ++i) - { - arma::mat aMat = A.slice(i); - arma::mat bMat = B.slice(i); - calculatedOut.slice(i) = arma::join_rows(aMat, bMat); - } - y = 2; - } - if (axis == 2) - { - calculatedOut = arma::join_slices(A, B); - z = 2; - } - - // Compute output of Concat<> layer. - Concat module(axis); - module.Add(moduleA); - module.Add(moduleB); - module.InputDimensions() = std::vector({ inputWidth, inputHeight }); - module.ComputeOutputDimensions(); - output.set_size(module.OutputSize(), 1); - module.Forward(input, output); - arma::cube concatOut(output.memptr(), x * outputWidth, - y * outputHeight, z * outputChannel); - - // Verify if the output reshaped to cubes are similar. - CheckMatrices(concatOut, calculatedOut, 1e-12); - - // Ensure that the child layers don't get deleted when `module` is - // deallocated. - module.Network().clear(); - } - - delete moduleA; - delete moduleB; -} - -/** - * Test that the function that can access the axis parameter of the - * Concat layer works. - */ -TEST_CASE("ConcatLayerParametersTest", "[ANNLayerTest]") -{ - Concat layer(2); - - // Make sure we can get the parameters successfully. - REQUIRE(layer.Axis() == 2); -} - -/** - * Concat layer numerical gradient test. - */ -TEST_CASE("GradientConcatLayerTest", "[ANNLayerTest]") -{ - // Concat function gradient instantiation. - struct GradientFunction - { - GradientFunction() : - input(arma::randu(10, 1)), - target(arma::mat("0")) - { - model = new FFN(); - model->ResetData(input, target); - model->Add(10); - - concat = new Concat(); - concat->Add(5); - concat->Add(5); - model->Add(concat); - model->Add(2); - - model->Add(); - } - - ~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* model; - Concat* concat; - arma::mat input, target; - } function; - - REQUIRE(CheckGradient(function) <= 1e-4); -} - -/** - * Simple concatenate module test. - */ -TEST_CASE("SimpleConcatenateLayerTest", "[ANNLayerTest]") -{ - arma::mat input = arma::ones(5, 1); - arma::mat output, delta; - - Concatenate module; - module.Concat() = arma::ones(5, 1) * 0.5; - module.InputDimensions() = std::vector({ 5 }); - module.ComputeOutputDimensions(); - - // Test the Forward function. - output.set_size(module.OutputSize(), 1); - module.Forward(input, output); - - REQUIRE(arma::accu(output) == 7.5); - - // Test the Backward function. - delta.set_size(5, 1); - module.Backward(input, output, delta); - REQUIRE(arma::accu(delta) == 5); -} - -/** - * Concatenate layer numerical gradient test. - */ -TEST_CASE("GradientConcatenateLayerTest", "[ANNLayerTest]") -{ - // Concatenate function gradient instantiation. - struct GradientFunction - { - GradientFunction() : - input(arma::randu(10, 1)), - target(arma::mat("0")) - { - model = new FFN(); - model->ResetData(input, target); - model->Add(5); - - arma::mat concat = arma::ones(5, 1); - // concatenate = new Concatenate(); - // concatenate->Concat() = concat; - // model->Add(concatenate); - model->Add(concat); - - model->Add(5); - model->Add(); - } - - ~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* model; - Concatenate* concatenate; - arma::mat input, target; - } function; - - REQUIRE(CheckGradient(function) <= 1e-4); -} - /** * Simple lookup module test. * @@ -2239,69 +1939,6 @@ TEST_CASE("LookupLayerParametersTest", "[ANNLayerTest]") } */ -/** - * Simple Softmax module test. - */ -TEST_CASE("SimpleSoftmaxLayerTest", "[ANNLayerTest]") -{ - arma::mat input, output, gy, g; - Softmax module; - - // Test the forward function. - input = arma::mat("1.7; 3.6"); - module.Forward(input, output); - REQUIRE(arma::accu(arma::abs(arma::mat("0.130108; 0.869892") - output)) == - Approx(0.0).margin(1e-4)); - - // Test the backward function. - gy = arma::zeros(input.n_rows, input.n_cols); - gy(0) = 1; - module.Backward(output, gy, g); - REQUIRE(arma::accu(arma::abs(arma::mat("0.11318; -0.11318") - g)) == - Approx(0.0).margin(1e-04)); -} - -/** - * Softmax layer numerical gradient test. - */ -TEST_CASE("GradientSoftmaxTest", "[ANNLayerTest]") -{ - // Softmax function gradient instantiation. - struct GradientFunction - { - GradientFunction() : - input(arma::randu(10, 1)), - target(arma::mat("1; 0")) - { - model = new FFN; - model->ResetData(input, target); - model->Add(10); - model->Add(); - model->Add(2); - model->Add(); - } - - ~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* model; - arma::mat input, target; - } function; - - REQUIRE(CheckGradient(function) <= 1e-4); -} - /** * Simple test for the NearestInterpolation layer * @@ -4058,155 +3695,6 @@ TEST_CASE("TransposedConvolutionLayerPaddingTest", "[ANNLayerTest]") // REQUIRE(output.n_elem == 4); // } -/** - * Simple test for AddMerge layer. - */ -TEST_CASE("AddMergeTestCase", "[ANNLayerTest]") -{ - // For rectangular input to pooling layers. - arma::mat input = arma::mat(28, 1); - input.zeros(); - input(0) = input(16) = 1; - input(1) = input(17) = 2; - input(2) = input(18) = 3; - input(3) = input(19) = 4; - input(4) = input(20) = 5; - input(5) = input(23) = 6; - input(6) = input(24) = 7; - input(14) = input(25) = 8; - input(15) = input(26) = 9; - - AddMerge module1; - module1.Add(2, 2, 2, 2, false); - module1.Add(2, 2, 2, 2, false); - - AddMerge module2; - module2.Add(2, 2, 2, 2, true); - module2.Add(2, 2, 2, 2, true); - - module1.InputDimensions() = std::vector({ 7, 4 }); - module1.ComputeOutputDimensions(); - module2.InputDimensions() = std::vector({ 7, 4 }); - module2.ComputeOutputDimensions(); - - // Calculated using torch.nn.MeanPool2d(). - arma::mat result1, result2; - result1 << 1.5000 << 8.5000 << arma::endr - << 3.5000 << 8.0000 << arma::endr - << 5.5000 << 12.0000 << arma::endr - << 7.0000 << 5.0000 << arma::endr; - - result2 << 1.5000 << 8.5000 << arma::endr - << 3.5000 << 8.0000 << arma::endr - << 5.5000 << 12.0000 << arma::endr; - - arma::mat output1, output2; - output1.set_size(8, 1); - output2.set_size(6, 1); - module1.Forward(input, output1); - REQUIRE(arma::accu(output1) == 51.0); - module2.Forward(input, output2); - REQUIRE(arma::accu(output2) == 39.0); - output1.reshape(4, 2); - output2.reshape(3, 2); - CheckMatrices(output1, result1, 1e-1); - CheckMatrices(output2, result2, 1e-1); - - arma::mat prevDelta1, prevDelta2; - prevDelta1 << 3.6000 << -0.9000 << arma::endr - << 3.6000 << -0.9000 << arma::endr - << 3.6000 << -0.9000 << arma::endr - << 3.6000 << -0.9000 << arma::endr; - - prevDelta2 << 3.6000 << -0.9000 << arma::endr - << 3.6000 << -0.9000 << arma::endr - << 3.6000 << -0.9000 << arma::endr; - arma::mat delta1, delta2; - delta1.set_size(28, 1); - delta2.set_size(28, 1); - prevDelta1.reshape(8, 1); - prevDelta2.reshape(6, 1); - module1.Backward(input, prevDelta1, delta1); - REQUIRE(arma::accu(delta1) == Approx(21.6).epsilon(1e-3)); - module2.Backward(input, prevDelta2, delta2); - REQUIRE(arma::accu(delta2) == Approx(16.2).epsilon(1e-3)); -} - -/** - * Complex test for AddMerge layer. - * This test includes: - * 1. AddMerge layer inside the AddMerge layer. - * 2. Batch Size > 1. - * 3. AddMerge layer with single child layer. - */ -TEST_CASE("AddMergeAdvanceTestCase", "[ANNLayerTest]") -{ - AddMerge r; - AddMerge* r2 = new AddMerge(); - r2->Add(5); - r.Add(5); - r.Add(r2); - r.InputDimensions() = std::vector({ 5 }); - r.ComputeOutputDimensions(); - arma::mat rParams(r.WeightSize(), 1); - r.SetWeights((double*) rParams.memptr()); - r.Network()[0]->Parameters().fill(2.0); - ((AddMerge*) r.Network()[1])->Network()[0]->Parameters().fill(-1.0); - - Linear l(5); - l.InputDimensions() = std::vector({ 5 }); - l.ComputeOutputDimensions(); - arma::mat lParams(l.WeightSize(), 1); - l.SetWeights((double*) lParams.memptr()); - l.Parameters().fill(1.0); - - arma::mat input(arma::randn(5, 10)); - arma::mat output1, output2; - output1.set_size(5, 10); - output2.set_size(5, 10); - - r.Forward(input, output1); - l.Forward(input, output2); - - CheckMatrices(output1, output2, 1e-3); - - arma::mat delta1, delta2; - delta1.set_size(5, 10); - delta2.set_size(5, 10); - r.Backward(input, output1, delta1); - l.Backward(input, output2, delta2); - - CheckMatrices(output1, output2, 1e-3); -} - -/** - * Simple test for Identity layer. - */ -TEST_CASE("IdentityTestCase", "[ANNLayerTest]") -{ - // For rectangular input to pooling layers. - arma::mat input = arma::mat(12, 1, arma::fill::randn); - arma::mat output; - // Output-Size should be 4 x 3. - output.set_size(12, 1); - - Identity module1; - module1.InputDimensions() = std::vector({ 4, 3 }); - module1.ComputeOutputDimensions(); - module1.Forward(input, output); - CheckMatrices(output, input, 1e-1); - REQUIRE(output.n_elem == 12); - REQUIRE(output.n_cols == 1); - REQUIRE(input.memptr() != output.memptr()); - - arma::mat prevDelta = arma::mat(12, 1, arma::fill::randn); - arma::mat delta; - delta.set_size(12, 1); - module1.Backward(input, prevDelta, delta); - CheckMatrices(delta, prevDelta, 1e-1); - REQUIRE(delta.memptr() != prevDelta.memptr()); -} - /** * Test that the functions that can modify and access the parameters of the * Glimpse layer work. diff --git a/src/mlpack/tests/cf_test.cpp b/src/mlpack/tests/cf_test.cpp index b876b3316c..7dd88a2bb3 100644 --- a/src/mlpack/tests/cf_test.cpp +++ b/src/mlpack/tests/cf_test.cpp @@ -496,212 +496,38 @@ void Serialization() /** * Make sure that correct number of recommendations are generated when query - * set for randomized SVD. + * set for all methods. */ -TEST_CASE("CFGetRecommendationsAllUsersRandSVDTest", "[CFTest]") +TEMPLATE_TEST_CASE("CFGetRecommendationsAllUsersTest", "[CFTest]", + RandomizedSVDPolicy, RegSVDPolicy, BatchSVDPolicy, NMFPolicy, + SVDCompletePolicy, SVDIncompletePolicy, BiasSVDPolicy, SVDPlusPlusPolicy, + QUIC_SVDPolicy, BlockKrylovSVDPolicy) { - GetRecommendationsAllUsers(); + GetRecommendationsAllUsers(); } /** - * Make sure that correct number of recommendations are generated when query - * set for regularized SVD. + * Make sure that the recommendations are generated for queried users + * for all methods. */ -TEST_CASE("CFGetRecommendationsAllUsersRegSVDTest", "[CFTest]") +TEMPLATE_TEST_CASE("CFGetRecommendationsQueriedUsersTest", "[CFTest]", + RandomizedSVDPolicy, RegSVDPolicy, BatchSVDPolicy, NMFPolicy, + SVDCompletePolicy, SVDIncompletePolicy, BiasSVDPolicy, SVDPlusPlusPolicy, + QUIC_SVDPolicy, BlockKrylovSVDPolicy) { - GetRecommendationsAllUsers(); -} - -/** - * Make sure that correct number of recommendations are generated when query - * set for Batch SVD. - */ - -TEST_CASE("CFGetRecommendationsAllUsersBatchSVDTest", "[CFTest]") -{ - GetRecommendationsAllUsers(); -} - -/** - * Make sure that correct number of recommendations are generated when query - * set for NMF. - */ -TEST_CASE("CFGetRecommendationsAllUsersNMFTest", "[CFTest]") -{ - GetRecommendationsAllUsers(); -} - -/** - * Make sure that correct number of recommendations are generated when query - * set for SVD Complete Incremental method. - */ -TEST_CASE("CFGetRecommendationsAllUsersSVDCompleteTest", "[CFTest]") -{ - GetRecommendationsAllUsers(); -} - -/** - * Make sure that correct number of recommendations are generated when query - * set for SVD Incomplete Incremental method. - */ -TEST_CASE("CFGetRecommendationsAllUsersSVDIncompleteTest", "[CFTest]") -{ - GetRecommendationsAllUsers(); -} - -/** - * Make sure that correct number of recommendations are generated when query - * set for Bias SVD method. - */ -TEST_CASE("CFGetRecommendationsAllUsersBiasSVDTest", "[CFTest]") -{ - GetRecommendationsAllUsers(); -} - -/** - * Make sure that correct number of recommendations are generated when query - * set for SVDPlusPlus method. - */ -TEST_CASE("CFGetRecommendationsAllUsersSVDPPTest", "[CFTest]") -{ - GetRecommendationsAllUsers(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for randomized SVD. - */ -TEST_CASE("CFGetRecommendationsQueriedUserRandSVDTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for regularized SVD. - */ -TEST_CASE("CFGetRecommendationsQueriedUserRegSVDTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for batch SVD. - */ -TEST_CASE("CFGetRecommendationsQueriedUserBatchSVDTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for NMF. - */ -TEST_CASE("CFGetRecommendationsQueriedUserNMFTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for SVD Complete Incremental method. - */ -TEST_CASE("CFGetRecommendationsQueriedUserSVDCompleteTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for SVD Incomplete Incremental method. - */ -TEST_CASE("CFGetRecommendationsQueriedUserSVDIncompleteTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for Bias SVD method. - */ -TEST_CASE("CFGetRecommendationsQueriedUserBiasSVDTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); -} - -/** - * Make sure that the recommendations are generated for queried users only - * for SVDPlusPlus method. - */ -TEST_CASE("CFGetRecommendationsQueriedUserSVDPPTest", "[CFTest]") -{ - GetRecommendationsQueriedUser(); + GetRecommendationsQueriedUser(); } /** * Make sure recommendations that are generated are reasonably accurate - * for randomized SVD. + * for all methods except SVDPlusPlus method. */ -TEST_CASE("RecommendationAccuracyRandSVDTest", "[CFTest]") +TEMPLATE_TEST_CASE("RecommendationAccuracyTest", "[CFTest]", + RandomizedSVDPolicy, RegSVDPolicy, BatchSVDPolicy, NMFPolicy, + SVDCompletePolicy, SVDIncompletePolicy, BiasSVDPolicy, QUIC_SVDPolicy, + BlockKrylovSVDPolicy) { - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for regularized SVD. - */ -TEST_CASE("RecommendationAccuracyRegSVDTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for batch SVD. - */ -TEST_CASE("RecommendationAccuracyBatchSVDTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for NMF. - */ -TEST_CASE("RecommendationAccuracyNMFTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for SVD Complete Incremental method. - */ -TEST_CASE("RecommendationAccuracySVDCompleteTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for SVD Incomplete Incremental method. - */ -TEST_CASE("RecommendationAccuracySVDIncompleteTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for Bias SVD method. - */ -TEST_CASE("RecommendationAccuracyBiasSVDTest", "[CFTest]") -{ - // This algorithm seems to be far less effective than others. - // We therefore allow failures on 44% of the runs. - RecommendationAccuracy(22); + RecommendationAccuracy(); } /** @@ -715,326 +541,82 @@ TEST_CASE("RecommendationAccuracyBiasSVDTest", "[CFTest]") // RecommendationAccuracy(); // } -// Make sure that Predict() is returning reasonable results for randomized SVD. -TEST_CASE("CFPredictRandSVDTest", "[CFTest]") +/** + * Make sure that Predict() is returning reasonable results for all methods. + */ +TEMPLATE_TEST_CASE("CFPredictTest", "[CFTest]", + RandomizedSVDPolicy, RegSVDPolicy, BatchSVDPolicy, NMFPolicy, + SVDCompletePolicy, SVDIncompletePolicy, BiasSVDPolicy, SVDPlusPlusPolicy, + QUIC_SVDPolicy, BlockKrylovSVDPolicy) { - CFPredict(); -} - -// Make sure that Predict() is returning reasonable results for regularized SVD. -TEST_CASE("CFPredictRegSVDTest", "[CFTest]") -{ - CFPredict(); -} - -// Make sure that Predict() is returning reasonable results for batch SVD. -TEST_CASE("CFPredictBatchSVDTest", "[CFTest]") -{ - CFPredict(); -} - -// Make sure that Predict() is returning reasonable results for NMF. -TEST_CASE("CFPredictNMFTest", "[CFTest]") -{ - CFPredict(); + CFPredict(); } /** - * Make sure that Predict() is returning reasonable results for SVD Complete - * Incremental method. + * Compare batch Predict() and individual Predict() for all methods. */ -TEST_CASE("CFPredictSVDCompleteTest", "[CFTest]") +TEMPLATE_TEST_CASE("CFBatchPredictTest", "[CFTest]", + RandomizedSVDPolicy, RegSVDPolicy, BatchSVDPolicy, NMFPolicy, + SVDCompletePolicy, SVDIncompletePolicy, BiasSVDPolicy, SVDPlusPlusPolicy, + QUIC_SVDPolicy, BlockKrylovSVDPolicy) { - CFPredict(); + BatchPredict(); } /** - * Make sure that Predict() is returning reasonable results for SVD Incomplete - * Incremental method. + * Make sure we can train an already-trained model and it works okay for + * some methods */ -TEST_CASE("CFPredictSVDIncompleteTest", "[CFTest]") +TEMPLATE_TEST_CASE("TrainTest_1", "[CFTest]", + RandomizedSVDPolicy, BatchSVDPolicy, NMFPolicy, SVDCompletePolicy, + SVDIncompletePolicy, QUIC_SVDPolicy, BlockKrylovSVDPolicy) { - CFPredict(); -} - -/** - * Make sure that Predict() is returning reasonable results for Bias SVD - * method. - */ -TEST_CASE("CFPredictBiasSVDTest", "[CFTest]") -{ - CFPredict(); -} - -/** - * Make sure that Predict() is returning reasonable results for SVDPlusPlus - * method. - */ -TEST_CASE("CFPredictSVDPPTest", "[CFTest]") -{ - CFPredict(); -} - -// Compare batch Predict() and individual Predict() for randomized SVD. -TEST_CASE("CFBatchPredictRandSVDTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for regularized SVD. -TEST_CASE("CFBatchPredictRegSVDTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for batch SVD. -TEST_CASE("CFBatchPredictBatchSVDTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for NMF. -TEST_CASE("CFBatchPredictNMFTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for -// SVD Complete Incremental method. -TEST_CASE("CFBatchPredictSVDCompleteTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for -// SVD Incomplete Incremental method. -TEST_CASE("CFBatchPredictSVDIncompleteTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for -// Bias SVD method. -TEST_CASE("CFBatchPredictBiasSVDTest", "[CFTest]") -{ - BatchPredict(); -} - -// Compare batch Predict() and individual Predict() for -// SVDPlusPlus method. -TEST_CASE("CFBatchPredictSVDPPTest", "[CFTest]") -{ - BatchPredict(); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * randomized SVD. - */ -TEST_CASE("TrainRandSVDTest", "[CFTest]") -{ - RandomizedSVDPolicy decomposition; + TestType decomposition; Train(decomposition); } /** - * Make sure we can train an already-trained model and it works okay for - * regularized SVD. + * Make sure we can train an already-trained model and it works okay for + * some methods */ -TEST_CASE("TrainRegSVDTest", "[CFTest]") +TEMPLATE_TEST_CASE("TrainTest_2", "[CFTest]", + RegSVDPolicy, BiasSVDPolicy, SVDPlusPlusPolicy) { - RegSVDPolicy decomposition; - TrainWithCoordinateList(decomposition); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * batch SVD. - */ -TEST_CASE("TrainBatchSVDTest", "[CFTest]") -{ - BatchSVDPolicy decomposition; - Train(decomposition); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * NMF. - */ -TEST_CASE("TrainNMFTest", "[CFTest]") -{ - NMFPolicy decomposition; - Train(decomposition); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * SVD Complete Incremental method. - */ -TEST_CASE("TrainSVDCompleteTest", "[CFTest]") -{ - SVDCompletePolicy decomposition; - Train(decomposition); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * SVD Incomplete Incremental method. - */ -TEST_CASE("TrainSVDIncompleteTest", "[CFTest]") -{ - SVDIncompletePolicy decomposition; - Train(decomposition); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * BiasSVD method. - */ -TEST_CASE("TrainBiasSVDTest", "[CFTest]") -{ - BiasSVDPolicy decomposition; - TrainWithCoordinateList(decomposition); -} - -/** - * Make sure we can train an already-trained model and it works okay for - * SVDPlusPlus method. - */ -TEST_CASE("TrainSVDPPTest", "[CFTest]") -{ - SVDPlusPlusPolicy decomposition; + TestType decomposition; TrainWithCoordinateList(decomposition); } /** * Make sure we can train a model after using the empty constructor when - * using randomized SVD. + * using any of the method. */ -TEST_CASE("EmptyConstructorTrainRandSVDTest", "[CFTest]") +TEMPLATE_TEST_CASE("EmptyConstructorTrainTest", "[CFTest]", + RandomizedSVDPolicy, RegSVDPolicy, BatchSVDPolicy, NMFPolicy, + SVDCompletePolicy, SVDIncompletePolicy, BiasSVDPolicy, QUIC_SVDPolicy, + BlockKrylovSVDPolicy) { - EmptyConstructorTrain(); + EmptyConstructorTrain(); } /** - * Make sure we can train a model after using the empty constructor when - * using regularized SVD. + * Ensure we can load and save the CF model using any of the method. */ -TEST_CASE("EmptyConstructorTrainRegSVDTest", "[CFTest]") +TEMPLATE_TEST_CASE("SerializationTest", "[CFTest]", + RandomizedSVDPolicy, BatchSVDPolicy, NMFPolicy, SVDCompletePolicy, + SVDIncompletePolicy, QUIC_SVDPolicy, BlockKrylovSVDPolicy) { - EmptyConstructorTrain(); -} - -/** - * Make sure we can train a model after using the empty constructor when - * using batch SVD. - */ -TEST_CASE("EmptyConstructorTrainBatchSVDTest", "[CFTest]") -{ - EmptyConstructorTrain(); -} - -/** - * Make sure we can train a model after using the empty constructor when - * using NMF. - */ -TEST_CASE("EmptyConstructorTrainNMFTest", "[CFTest]") -{ - EmptyConstructorTrain(); -} - -/** - * Make sure we can train a model after using the empty constructor when - * using SVD Complete Incremental method. - */ -TEST_CASE("EmptyConstructorTrainSVDCompleteTest", "[CFTest]") -{ - EmptyConstructorTrain(); -} - -/** - * Make sure we can train a model after using the empty constructor when - * using SVD Incomplete Incremental method. - */ -TEST_CASE("EmptyConstructorTrainSVDIncompleteTest", "[CFTest]") -{ - EmptyConstructorTrain(); -} - -/** - * Ensure we can load and save the CF model using randomized SVD policy. - */ -TEST_CASE("SerializationRandSVDTest", "[CFTest]") -{ - Serialization(); -} - -/** - * Ensure we can load and save the CF model using batch SVD policy. - */ -TEST_CASE("SerializationBatchSVDTest", "[CFTest]") -{ - Serialization(); -} - -/** - * Ensure we can load and save the CF model using NMF policy. - */ -TEST_CASE("SerializationNMFTest", "[CFTest]") -{ - Serialization(); -} - -/** - * Ensure we can load and save the CF model using SVD Complete Incremental. - */ -TEST_CASE("SerializationSVDCompleteTest", "[CFTest]") -{ - Serialization(); -} - -/** - * Ensure we can load and save the CF model using SVD Incomplete Incremental. - */ -TEST_CASE("SerializationSVDIncompleteTest", "[CFTest]") -{ - Serialization(); + Serialization(); } /** * Make sure that Predict() is returning reasonable results for NMF and - * OverallMeanNormalization. + * all types of Normalization except default. */ -TEST_CASE("CFPredictOverallMeanNormalization", "[CFTest]") +TEMPLATE_TEST_CASE("CFPredictNormalization", "[CFTest]", + OverallMeanNormalization, UserMeanNormalization, ItemMeanNormalization, + ZScoreNormalization) { - CFPredict(2.0); -} - -/** - * Make sure that Predict() is returning reasonable results for NMF and - * UserMeanNormalization. - */ -TEST_CASE("CFPredictUserMeanNormalization", "[CFTest]") -{ - CFPredict(2.0); -} - -/** - * Make sure that Predict() is returning reasonable results for NMF and - * ItemMeanNormalization. - */ -TEST_CASE("CFPredictItemMeanNormalization", "[CFTest]") -{ - CFPredict(2.0); -} - -/** - * Make sure that Predict() is returning reasonable results for NMF and - * ZScoreNormalization. - */ -TEST_CASE("CFPredictZScoreNormalization", "[CFTest]") -{ - CFPredict(2.0); + CFPredict(2.0); } /** @@ -1061,38 +643,13 @@ TEST_CASE("CFPredictNoNormalization", "[CFTest]") /** * Make sure recommendations that are generated are reasonably accurate - * for OverallMeanNormalization. + * for all types of Normalization except default. */ -TEST_CASE("RecommendationAccuracyOverallMeanNormalizationTest", "[CFTest]") +TEMPLATE_TEST_CASE("RecommendationAccuracyNormalizationTest", "[CFTest]", + OverallMeanNormalization, UserMeanNormalization, ItemMeanNormalization, + ZScoreNormalization) { - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for UserMeanNormalization. - */ -TEST_CASE("RecommendationAccuracyUserMeanNormalizationTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for ItemMeanNormalization. - */ -TEST_CASE("RecommendationAccuracyItemMeanNormalizationTest", "[CFTest]") -{ - RecommendationAccuracy(); -} - -/** - * Make sure recommendations that are generated are reasonably accurate - * for ZScoreNormalization. - */ -TEST_CASE("RecommendationAccuracyZScoreNormalizationTest", "[CFTest]") -{ - RecommendationAccuracy(); + RecommendationAccuracy(); } /** @@ -1109,35 +666,14 @@ TEST_CASE("RecommendationAccuracyCombinedNormalizationTest", "[CFTest]") } /** - * Ensure we can load and save the CF model using OverallMeanNormalization. + * Ensure we can load and save the CF model using any type of Normalization + * except default. */ -TEST_CASE("SerializationOverallMeanNormalizationTest", "[CFTest]") +TEMPLATE_TEST_CASE("SerializationNormalizationTest", "[CFTest]", + OverallMeanNormalization, UserMeanNormalization, ItemMeanNormalization, + ZScoreNormalization) { - Serialization(); -} - -/** - * Ensure we can load and save the CF model using UserMeanNormalization. - */ -TEST_CASE("SerializationUserMeanNormalizationTest", "[CFTest]") -{ - Serialization(); -} - -/** - * Ensure we can load and save the CF model using ItemMeanNormalization. - */ -TEST_CASE("SerializationItemMeanNormalizationTest", "[CFTest]") -{ - Serialization(); -} - -/** - * Ensure we can load and save the CF model using ZScoreMeanNormalization. - */ -TEST_CASE("SerializationZScoreNormalizationTest", "[CFTest]") -{ - Serialization(); + Serialization(); } /** @@ -1153,54 +689,24 @@ TEST_CASE("SerializationCombinedNormalizationTest", "[CFTest]") } /** - * Make sure that Predict() is returning reasonable results for - * EuclideanSearch. + * Make sure that Predict() is returning reasonable results for all search + * except default. */ -TEST_CASE("CFPredictEuclideanSearch", "[CFTest]") +TEMPLATE_TEST_CASE("CFPredictSearch", "[CFTest]", + EuclideanSearch, CosineSearch, PearsonSearch) { - CFPredict(2.0); + CFPredict(2.0); } /** * Make sure that Predict() is returning reasonable results for - * CosineSearch. + * some Interpolations. */ -TEST_CASE("CFPredictCosineSearch", "[CFTest]") +TEMPLATE_TEST_CASE("CFPredictAverageInterpolation", "[CFTest]", + AverageInterpolation, SimilarityInterpolation) { - CFPredict(2.0); -} - -/** - * Make sure that Predict() is returning reasonable results for - * PearsonSearch. - */ -TEST_CASE("CFPredictPearsonSearch", "[CFTest]") -{ - CFPredict(2.0); -} - -/** - * Make sure that Predict() is returning reasonable results for - * AverageInterpolation. - */ -TEST_CASE("CFPredictAverageInterpolation", "[CFTest]") -{ - CFPredict(2.0); -} - -/** - * Make sure that Predict() is returning reasonable results for - * SimilarityInterpolation. - */ -TEST_CASE("CFPredictSimilarityInterpolation", "[CFTest]") -{ - CFPredict(2.0); + CFPredict(2.0); } /** diff --git a/src/mlpack/tests/load_save_test.cpp b/src/mlpack/tests/load_save_test.cpp index a4dcfd03ce..6329481d43 100644 --- a/src/mlpack/tests/load_save_test.cpp +++ b/src/mlpack/tests/load_save_test.cpp @@ -58,7 +58,7 @@ TEST_CASE("WrongExtensionWrongLoad", "[LoadSaveTest]") "4 8;"; arma::mat testTrans = trans(test); - REQUIRE(testTrans.quiet_save("test_file.csv", arma::arma_binary) == true); + REQUIRE(testTrans.save("test_file.csv", arma::arma_binary) == true); // Now reload through our interface. REQUIRE(data::Load("test_file.csv", test) == false); @@ -79,7 +79,7 @@ TEST_CASE("WrongExtensionCorrectLoad", "[LoadSaveTest]") "4 8;"; arma::mat testTrans = trans(test); - REQUIRE(testTrans.quiet_save("test_file.csv", arma::arma_binary) == true); + REQUIRE(testTrans.save("test_file.csv", arma::arma_binary) == true); // Now reload through our interface. REQUIRE( @@ -924,7 +924,7 @@ TEST_CASE("LoadArmaBinaryTest", "[LoadSaveTest]") "4 8;"; arma::mat testTrans = trans(test); - REQUIRE(testTrans.quiet_save("test_file.bin", arma::arma_binary) + REQUIRE(testTrans.save("test_file.bin", arma::arma_binary) == true); // Now reload through our interface. @@ -1001,7 +1001,7 @@ TEST_CASE("LoadRawBinaryTest", "[LoadSaveTest]") "7 8;"; arma::mat testTrans = trans(test); - REQUIRE(testTrans.quiet_save("test_file.bin", arma::raw_binary) + REQUIRE(testTrans.save("test_file.bin", arma::raw_binary) == true); // Now reload through our interface. @@ -1028,7 +1028,7 @@ TEST_CASE("LoadPGMBinaryTest", "[LoadSaveTest]") "4 8;"; arma::mat testTrans = trans(test); - REQUIRE(testTrans.quiet_save("test_file.pgm", arma::pgm_binary) + REQUIRE(testTrans.save("test_file.pgm", arma::pgm_binary) == true); // Now reload through our interface. @@ -1080,13 +1080,13 @@ TEST_CASE("LoadHDF5Test", "[LoadSaveTest]") "3 7;" "4 8;"; arma::mat testTrans = trans(test); - REQUIRE(testTrans.quiet_save("test_file.h5", arma::hdf5_binary) + REQUIRE(testTrans.save("test_file.h5", arma::hdf5_binary) == true); - REQUIRE(testTrans.quiet_save("test_file.hdf5", arma::hdf5_binary) + REQUIRE(testTrans.save("test_file.hdf5", arma::hdf5_binary) == true); - REQUIRE(testTrans.quiet_save("test_file.hdf", arma::hdf5_binary) + REQUIRE(testTrans.save("test_file.hdf", arma::hdf5_binary) == true); - REQUIRE(testTrans.quiet_save("test_file.he5", arma::hdf5_binary) + REQUIRE(testTrans.save("test_file.he5", arma::hdf5_binary) == true); // Now reload through our interface.