From 6edd1104b5ccf8ea31476a2feff2f754178358d3 Mon Sep 17 00:00:00 2001 From: Ryan Curtin Date: Sat, 13 Jan 2024 12:39:36 -0500 Subject: [PATCH] Minor changes and fixes. --- doc/index.md | 129 ++++++-------- doc/user/core.md | 186 +++++++++++++++++---- doc/user/load_save.md | 381 +++++++++++++++++++++++++++--------------- doc/user/matrices.md | 77 +++++---- 4 files changed, 492 insertions(+), 281 deletions(-) diff --git a/doc/index.md b/doc/index.md index 921e25183e..6ab4667bf1 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,4 +1,5 @@ # Documentation for mlpack + ## A fast, flexible machine learning library mlpack is an intuitive, fast, and flexible header-only C++ machine learning @@ -18,22 +19,21 @@ _If you use mlpack, please [cite the software](citation.md)._ Installing mlpack can be done using the [instructions in the README](README.md#3-installing-and-using-mlpack-in-c); -or the [Windows build guide](user/build_windows.md). Then, the following -simple guides are good places to get started: +or the [Windows build guide](user/build_windows.md). +The following basic guides are *highly recommended* before using mlpack. - * [mlpack C++ quickstart](quickstart/cpp.md): create a couple simple C++ - programs that use mlpack - * [Sample Windows mlpack C++ application](user/sample_ml_app.md): create a - working mlpack Windows program using Visual Studio + * ***First steps***: + - [mlpack C++ quickstart](quickstart/cpp.md): create a couple simple C++ + programs that use mlpack + - [Sample Windows mlpack C++ application](user/sample_ml_app.md): create a + working mlpack Windows program using Visual Studio -After that, it's a good idea to familiarize yourself with the basics of the -library. The documentation for mlpack's algorithms depends on the concepts in -the pages below. + * ***Basics of matrices and data in mlpack***: + - [Matrices and data in mlpack](user/matrices.md) + - [Loading and saving mlpack objects](user/load_save.md) - * [Matrices and data in mlpack](user/matrices.md) - * [Loading and saving mlpack objects](user/load_save.md) - * [Core mlpack documentation](user/core.md): reference documentation for all - core classes and functions that are used in mlpack. + * ***Reference for mlpack core classes***: + - [Core mlpack documentation](user/core.md) ## mlpack algorithm documentation @@ -53,68 +53,6 @@ detailed in the sections below. * [Modeling utilities](#modeling-utilities): cross-validation, hyperparameter tuning, etc. -## Bindings to other languages - -mlpack's bindings to other languages have less complete functionality than -mlpack in C++, but almost all of the same algorithms are available. - -***Python***: - - * [Python quickstart](quickstart/python.md) - * [Python reference documentation](https://www.mlpack.org/doc/python_documentation.html) - -***Julia***: - - * [Julia quickstart](quickstart/julia.md) - * [Julia reference documentation](https://www.mlpack.org/doc/julia_documentation.html) - -***R***: - - * [R quickstart](quickstart/r.md) - * [R reference documentation](https://www.mlpack.org/doc/r_documentation.html) - -***Command-line programs***: - - * [Command-line quickstart](quickstart/cli.md) - * [Command-line reference documentation](https://www.mlpack.org/doc/cli_documentation.html) - -***Go***: - - * [Go quickstart](quickstart/go.md) - * [Go reference documentation](https://www.mlpack.org/doc/go_documentation.html) - -## Examples and further documentation - - * [mlpack examples repository](https://github.com/mlpack/examples/): numerous - fully-working example applications of mlpack, in C++ and other languages. - * [mlpack models repository](https://github.com/mlpack/models/): complex models - in C++ built with mlpack - -For additional documentation beyond what is covered in all the resources above, -the source code should be consulted. Each method is fully documented. - -## Developer documentation - -Throughout the codebase, mlpack uses some common template parameter policies. -These are documented below. - - * [The `ElemType` policy](developer/elemtype.md): element types for data - * [The `MetricType` policy](developer/metrics.md): distance metrics - * [The `KernelType` policy](developer/kernels.md): kernel functions - * [The `TreeType` policy](developer/trees.md): space trees (ball trees, - KD-trees, etc.) - -In addition, the following documentation may be useful when developing bindings -for other languages: - - * [Timers](developer/timer.md): timing parts of bindings - * [Writing an mlpack binding](developer/iodoc.md): simple examples of mlpack - bindings - * [Automatic bindings](developer/bindings.md): details on mlpack's automatic - binding generator system. - -## Algorithm documentation - ### Classification algorithms Classify points as discrete labels (`0`, `1`, `2`, ...). @@ -164,3 +102,44 @@ Transform data from one space to another. Cross-validation, hyperparameter tuning, etc. + +## Bindings to other languages + +mlpack's bindings to other languages have less complete functionality than +mlpack in C++, but almost all the same algorithms are available. + +| ***Python*** | -- | [quickstart](quickstart/python.md) | -- | [reference](https://www.mlpack.org/doc/python_documentation.html) | +| ***Julia*** | -- | [quickstart](quickstart/julia.md) | -- | [reference](https://www.mlpack.org/doc/julia_documentation.html) | +| ***R*** | -- | [quickstart](quickstart/r.md) | -- | [reference](https://www.mlpack.org/doc/r_documentation.html) +| ***Command-line programs*** | -- | [quickstart](quickstart/cli.md) | -- | [reference](https://www.mlpack.org/doc/cli_documentation.html) | +| ***Go*** | -- | [quickstart](quickstart/go.md) | -- | [reference](https://www.mlpack.org/doc/go_documentation.html) | + +## Examples and further documentation + + * [mlpack examples repository](https://github.com/mlpack/examples/): numerous + fully-working example applications of mlpack, in C++ and other languages. + * [mlpack models repository](https://github.com/mlpack/models/): complex models + in C++ built with mlpack + +For additional documentation beyond what is covered in all the resources above, +the source code should be consulted. Each method is fully documented. + +## Developer documentation + +Throughout the codebase, mlpack uses some common template parameter policies. +These are documented below. + + * [The `ElemType` policy](developer/elemtype.md): element types for data + * [The `MetricType` policy](developer/metrics.md): distance metrics + * [The `KernelType` policy](developer/kernels.md): kernel functions + * [The `TreeType` policy](developer/trees.md): space trees (ball trees, + KD-trees, etc.) + +In addition, the following documentation may be useful when developing bindings +for other languages: + + * [Timers](developer/timer.md): timing parts of bindings + * [Writing an mlpack binding](developer/iodoc.md): simple examples of mlpack + bindings + * [Automatic bindings](developer/bindings.md): details on mlpack's automatic + binding generator system. diff --git a/doc/user/core.md b/doc/user/core.md index 1e2a5e0acc..4b930c7d9a 100644 --- a/doc/user/core.md +++ b/doc/user/core.md @@ -4,24 +4,42 @@ Underlying the implementations of [mlpack's machine learning algorithms](index.md#mlpack-algorithm-documentation) are mlpack core support classes, each of which are documented on this page. + * [Core math utilities](#core-math-utilities): utility classes for mathematical + purposes + * [Distributions](#distributions): probability distributions + * [Metrics](#metrics): distance metrics for geometric algorithms + * [Kernels](#kernels): Mercer kernels for kernel-based algorithms + ## Core math utilities Utilities in the `mlpack::math::` namespace are meant to provide additional mathematical support on top of Armadillo. + * [`math::Range`](#mathrange): simple mathematical range (i.e. `[0, 3]`) + --- ### `math::Range` The `math::Range` class represents a simple mathematical range (i.e. `[0, 3]`), -with each value represented as a `double`. +with the bounds represented as `double`s. + +--- + +#### Constructors * `r = math::Range()` + - Construct an empty range. + * `r = math::Range(p)` + - Construct the range `[p, p]`. + * `r = math::Range(lo, hi)` - - Construct a range. If no value is specified, the range is empty; if `p` is - specified, the range is `[p, p]`; if `lo` and `hi` are specified, the range - is `[lo, hi]`. + - Construct the range `[lo, hi]`. + +--- + +#### Accessing and modifying range properties * `r.Lo()` and `r.Hi()` return the lower and upper bounds of the range as `double`s. @@ -33,6 +51,10 @@ with each value represented as a `double`. * `r.Mid()` returns the midpoint of the range as a `double`. +--- + +#### Working with ranges + * Given two ranges `r1` and `r2`, - `r1 | r2` returns the union of the ranges, - `r1 |= r2` expands `r1` to include the range `r2`, @@ -50,24 +72,34 @@ with each value represented as a `double`. - `r *= d` scales `r.Lo()` and `r.Hi()` by `d`, and - `r.Contains(d)` returns `true` if `d` is contained in the range. +--- + * To use ranges with different element types (e.g. `float`), use the type `math::RangeType` or similar. +--- + Example: ```c++ -math::Range r1(5.0, 6.0); // [5, 6] -math::Range r2(7.0, 8.0); // [7, 8] +mlpack::math::Range r1(5.0, 6.0); // [5, 6] +mlpack::math::Range r2(7.0, 8.0); // [7, 8] -math::Range r3 = r1 | r2; // [5, 8] -math::Range r4 = r1 & r2; // empty range +mlpack::math::Range r3 = r1 | r2; // [5, 8] +mlpack::math::Range r4 = r1 & r2; // empty range bool b1 = r1.Contains(r2); // false bool b2 = r1.Contains(5.5); // true bool b3 = r1.Contains(r3); // true bool b4 = r3.Contains(r4); // false + +// Create a range of `float`s and a range of `int`s. +mlpack::math::RangeType r5(1.0f, 1.5f); // [1.0, 1.5] +mlpack::math::RangeType r6(3, 4); // [3, 4] ``` +--- + `math::Range` is used by: * [`RangeSearch`](range_search.md) @@ -80,11 +112,16 @@ bool b4 = r3.Contains(r4); // false mlpack has support for a number of different distributions, each supporting the same API. These can be used with, for instance, the [`HMM`](hmm.md) class. + * [`DiscreteDistribution`](#discretedistribution): multidimensional categorical + distribution (generalized Bernoulli distribution) + * [`GaussianDistribution`](#gaussiandistribution): multidimensional Gaussian + distribution + ### `DiscreteDistribution` `DiscreteDistribution` represents a multidimensional categorical distribution -(or generalized Bernoulli distribution) where integer-valued vectors (e.g. `[0, -3, 4]`) are associated with specific probabilities in each dimension. +(or generalized Bernoulli distribution) where integer-valued vectors (e.g. +`[0, 3, 4]`) are associated with specific probabilities in each dimension. *Example:* a 3-dimensional `DiscreteDistribution` will have a specific probability value associated with each integer value in each dimension. So, for @@ -92,6 +129,10 @@ the vector `[0, 3, 4]`, `P(0)` in dimension 0 could be, e.g., `0.3`, `P(3)` in dimension 1 could be, e.g., `0.4`, and `P(4)` in dimension 2 could be, e.g., `0.6`. Then, `P([0, 3, 4])` would be `0.3 * 0.4 * 0.6 = 0.072`. +--- + +#### Constructors + * `d = DiscreteDistribution(numObservations)` - Create a one-dimensional discrete distribution with `numObservations` different observations in the one and only dimension. `numObservations` is @@ -112,10 +153,14 @@ dimension 1 could be, e.g., `0.4`, and `P(4)` in dimension 2 could be, e.g., - `probabilities[i]` is a vector such that `probabilities[i][j]` contains the probability of `j` in dimension `i`. +--- + +#### Access and modify properties of distribution + * `d.Dimensionality()` returns a `size_t` indicating the number of dimensions in the multidimensional discrete distribution. - * `d.Probabilities(i)` returns an `arma::vec` containing the probabilities of + * `d.Probabilities(i)` returns an `arma::vec&` containing the probabilities of each observation in dimension `i`. - `d.Probabilities(i)[j]` is the probability of `j` in dimension `i`. - This can be used to modify probabilities: `d.Probabilities(0)[1] = 0.7` @@ -123,6 +168,10 @@ dimension 1 could be, e.g., `0.4`, and `P(4)` in dimension 2 could be, e.g., - *Note:* when setting probabilities manually, be sure that the sum of probabilities in a dimension is 1! +--- + +#### Compute probabilities of points + * `d.Probability(observation)` returns the probability of the given observation as a `double`. - `observation` should be an `arma::vec` of size `d.Dimensionality()`. @@ -142,9 +191,17 @@ dimension 1 could be, e.g., `0.4`, and `P(4)` in dimension 2 could be, e.g., * `d.LogProbability(observations, probabilities)` computes the log-probabilities of many observations. +--- + +#### Sample from the distribution + * `d.Random()` returns an `arma::vec` with a random sample from the multidimensional discrete distribution. +--- + +#### Fit the distribution to observations + * `d.Train(observations)` - Fit the distribution to the given observations. - `observations` should be an `arma::mat` with number of rows equal to @@ -160,11 +217,13 @@ dimension 1 could be, e.g., `0.4`, and `P(4)` in dimension 2 could be, e.g., - `observationProbabilities[i]` should be equal to the probability that `observations.col(i)` is from `d`. +--- + *Example usage:* ```c++ // Create a single-dimension Bernoulli distribution: P([0]) = 0.3, P([1]) = 0.7. -DiscreteDistribution bernoulli(2); +mlpack::DiscreteDistribution bernoulli(2); bernoulli.Probabilities(0)[0] = 0.3; bernoulli.Probabilities(0)[1] = 0.7; @@ -177,7 +236,7 @@ arma::vec probDim0 = arma::vec("0.1 0.3 0.5 0.1"); // 4 possible values. arma::vec probDim1 = arma::vec("0.7 0.3"); // 2 possible values. arma::vec probDim2 = arma::vec("0.4 0.4 0.2"); // 3 possible values. std::vector probs { probDim0, probDim1, probDim2 }; -DiscreteDistribution d(probs); +mlpack::DiscreteDistribution d(probs); arma::vec obs("2 0 1"); const double p3 = d.Probability(obs); // p3 = 0.5 * 0.7 * 0.4 = 0.14. @@ -185,11 +244,12 @@ const double p3 = d.Probability(obs); // p3 = 0.5 * 0.7 * 0.4 = 0.14. // Estimate a 10-dimensional discrete distribution. // Each dimension takes values between 0 and 9. arma::mat observations = arma::randi(10, 1000, - arma::distr_param(0, 10)); + arma::distr_param(0, 9)); // Create a distribution with 10 observations in each of the 10 dimensions. -DiscreteDistribution d2(arma::Col("10 10 10 10 10 10 10 10 10 10")); -d2.Estimate(observations); +mlpack::DiscreteDistribution d2( + arma::Col("10 10 10 10 10 10 10 10 10 10")); +d2.Train(observations); // Compute the probabilities of each point. arma::vec probabilities; @@ -205,6 +265,10 @@ std::cout << "Average probability: " << arma::mean(probabilities) << "." `GaussianDistribution` is a standard multivariate Gaussian distribution with parameterized mean and covariance. +--- + +#### Constructors + * `g = GaussianDistribution(dimensionality)` - Create the distribution with the given dimensionality. - The distribution will have a zero mean and unit diagonal covariance matrix. @@ -216,6 +280,10 @@ parameterized mean and covariance. - `covariance` is of type `arma::mat`, and should be symmetric and square, with rows and columns equal to the dimensionality of the distribution. +--- + +#### Access and modify properties of distribution + * `g.Dimensionality()` returns the dimensionality of the distribution as a `size_t`. @@ -232,6 +300,10 @@ parameterized mean and covariance. * `g.LogDetCov()` returns a `double` holding the log-determinant of the covariance. +--- + +#### Compute probabilities of points + * `g.Probability(observation)` returns the probability of the given observation as a `double`. - `observation` should be an `arma::vec` of size `d.Dimensionality()`. @@ -249,9 +321,17 @@ parameterized mean and covariance. * `g.LogProbability(observations, probabilities)` computes the log-probabilities of many observations. +--- + +#### Sample from the distribution + * `g.Random()` returns an `arma::vec` with a random sample from the multidimensional discrete distribution. +--- + +#### Fit the distribution to observations + * `g.Train(observations)` - Fit the distribution to the given observations. - `observations` should be an `arma::mat` with number of rows equal to @@ -272,7 +352,7 @@ parameterized mean and covariance. ```c++ // Create a Gaussian distribution in 3 dimensions with zero mean and unit // covariance. -GaussianDistribution g(3); +mlpack::GaussianDistribution g(3); // Compute the probability of the point [0, 0.5, 0.25]. const double p = g.Probability(arma::vec("0 0.5 0.25")); @@ -292,7 +372,7 @@ const double p2 = g.Probability(arma::vec("0 0.5 0.25")); // dimensions. arma::mat samples(50, 10000, arma::fill::randn); // Normally distributed. -GaussianDistribution g2(50); +mlpack::GaussianDistribution g2(50); g2.Train(samples); // Compute the probability of all of the samples. @@ -320,6 +400,12 @@ including: * [`RANN`](rann.md) * [`KMeans`](kmeans.md) +Supported metrics: + + * [`LMetric`](#lmetric): generalized L-metric/Lp-metric, including + Manhattan/Euclidean/Chebyshev distances + * [Implement a custom metric](../developer/metrics.md) + ### `LMetric` The `LMetric` template class implements a [generalized @@ -341,6 +427,8 @@ LMetric - If set to `false`, the metric will no longer satisfy the triangle inequality. +--- + Several convenient typedefs are available: * `ManhattanDistance` (defined as `LMetric<1>`) @@ -348,6 +436,8 @@ Several convenient typedefs are available: * `SquaredEuclideanDistance` (defined as `LMetric<2, false>`) * `ChebyshevDistance` (defined as `LMetric`) +--- + The static `Evaluate()` method can be used to compute the distance between two vectors. @@ -363,23 +453,36 @@ implements the Armadillo API (e.g. `arma::fvec`, `arma::sp_fvec`, etc.). arma::vec a("0.0 1.0 5.0"); arma::vec b("1.0 3.0 5.0"); -const double d1 = ManhattanDistance::Evaluate(a, b); // d1 = 3.0 -const double d2 = EuclideanDistance::Evaluate(a, b); // d2 = 2.236 -const double d3 = SquaredEuclideanDistance::Evaluate(a, b); // d3 = 5.0 -const double d4 = ChebyshevDistance::Evaluate(a, b); // d4 = 2.0 -const double d5 = LMetric<4>::Evaluate(a, b); // d5 = 2.0305 -const double d6 = LMetric<3, false>::Evaluate(a, b); // d6 = 9.0 +const double d1 = mlpack::ManhattanDistance::Evaluate(a, b); // d1 = 3.0 +const double d2 = mlpack::EuclideanDistance::Evaluate(a, b); // d2 = 2.24 +const double d3 = mlpack::SquaredEuclideanDistance::Evaluate(a, b); // d3 = 5.0 +const double d4 = mlpack::ChebyshevDistance::Evaluate(a, b); // d4 = 2.0 +const double d5 = mlpack::LMetric<4>::Evaluate(a, b); // d5 = 2.03 +const double d6 = mlpack::LMetric<3, false>::Evaluate(a, b); // d6 = 9.0 + +std::cout << "Manhattan distance: " << d1 << "." << std::endl; +std::cout << "Euclidean distance: " << d2 << "." << std::endl; +std::cout << "Squared Euclidean distance: " << d3 << "." << std::endl; +std::cout << "Chebyshev distance: " << d4 << "." << std::endl; +std::cout << "L4-distance: " << d5 << "." << std::endl; +std::cout << "Cubed L3-distance: " << d6 << "." << std::endl; // Compute the distance between two random 10-dimensional vectors in a matrix. arma::mat m(10, 100, arma::fill::randu); -const double d7 = EuclideanDistance::Evaluate(m.col(0), m.col(7)); +const double d7 = mlpack::EuclideanDistance::Evaluate(m.col(0), m.col(7)); + +std::cout << std::endl; +std::cout << "Distance between two random vectors: " << d7 << "." << std::endl; +std::cout << std::endl; // Compute the distance between two 32-bit precision `float` vectors. arma::fvec fa("0.0 1.0 5.0"); arma::fvec fb("1.0 3.0 5.0"); -const double d8 = EuclideanDistance::Evaluate(fa, fb); // d8 = 2.236 +const double d8 = mlpack::EuclideanDistance::Evaluate(fa, fb); // d8 = 2.236 + +std::cout << "Euclidean distance (fvec): " << d8 << "." << std::endl; ``` ## Kernels @@ -397,6 +500,12 @@ including: * [`FastMKS`](fastmks.md) * [`NystroemMethod`](nystroem_method.md) +Supported kernels: + + * [`GaussianKernel`](#gaussiankernel): standard Gaussian/radial basis + function/RBF kernel + * [Implement a custom kernel](../developer/kernels.md) + ### `GaussianKernel` The `GaussianKernel` class implements the standard [Gaussian @@ -407,12 +516,20 @@ The Gaussian kernel is defined as: `k(x1, x2) = exp(-|| x1 - x2 ||^2 / (2 * bw^2))` where `bw` is the bandwidth parameter of the kernel. +--- + +#### Constructors and properties + * `g = GaussianKernel(bw=1.0)` - Create a `GaussianKernel` with the given bandwidth `bw`. * `g.Bandwidth()` returns the bandwidth of the kernel as a `double`. - To set the bandwidth, use `g.Bandwidth(newBandwidth)`. +--- + +#### Kernel evaluation + * `g.Evaluate(x1, x2)` - Compute the kernel value between two vectors `x1` and `x2`. - `x1` and `x2` should be vector types that implement the Armadillo API @@ -423,6 +540,10 @@ where `bw` is the bandwidth parameter of the kernel. between those two vectors (`distance`) is already known. - `distance` should have type `double`. +--- + +#### Other utilities + * `g.Gradient(distance)` - Compute the (one-dimensional) gradient of the kernel function with respect to the distance between two points, evaluated at `distance`. @@ -438,16 +559,18 @@ where `bw` is the bandwidth parameter of the kernel. ```c++ // Create a Gaussian kernel with default bandwidth. -GaussianKernel g; +mlpack::GaussianKernel g; // Create a Gaussian kernel with bandwidth 5.0. -GaussianKernel g2(5.0); +mlpack::GaussianKernel g2(5.0); // Evaluate the kernel value between two 3-dimensional points. arma::vec x1("0.5 1.0 1.5"); arma::vec x2("1.5 1.0 0.5"); const double k1 = g.Evaluate(x1, x2); const double k2 = g2.Evaluate(x1, x2); +std::cout << "Kernel values: " << k1 << " (bw=1.0), " << k2 << " (bw=5.0)." + << std::endl; // Evaluate the kernel value when the distance between two points is already // computed. @@ -457,16 +580,19 @@ const double k3 = g.Evaluate(distance); // Change the bandwidth of the kernel to 2.5. g.Bandwidth(2.5); const double k4 = g.Evaluate(x1, x2); +std::cout << "Kernel value with bw=2.5: " << k4 << "." << std::endl; // Evaluate the kernel value between x1 and all points in a random matrix. arma::mat r(3, 100, arma::fill::randu); arma::vec kernelValues(100); for (size_t i = 0; i < r.n_cols; ++i) kernelValues[i] = g.Evaluate(x1, r.col(i)); +std::cout << "Average kernel value for random points: " + << arma::mean(kernelValues) << "." << std::endl; // Compute the kernel value between two 32-bit floating-point vectors. arma::fvec fx1("0.5 1.0 1.5"); arma::fvec fx2("1.5 1.0 0.5"); -const double k4 = g.Evaluate(fx1, fx2); -const double k5 = g2.Evaluate(fx1, fx2); +const double k5 = g.Evaluate(fx1, fx2); +const double k6 = g2.Evaluate(fx1, fx2); ``` diff --git a/doc/user/load_save.md b/doc/user/load_save.md index ed109a68e3..c74083f982 100644 --- a/doc/user/load_save.md +++ b/doc/user/load_save.md @@ -6,26 +6,41 @@ any mlpack object via the [cereal](https://uscilab.github.io/cereal/) serialization toolkit. A number of other utilities related to loading and saving data and objects are also available. + * [Numeric data](#numeric-data) + * [Mixed categorical data](#mixed-categorical-data) + - [`data::DatasetInfo`](#datadatasetinfo) + - [Loading categorical data](#loading-categorical-data) + * [Image data](#image-data) + - [`data::ImageInfo`](#dataimageinfo) + - [Loading images](#loading-images) + * [mlpack objects](#mlpack-objects): load or save any mlpack object + * [Normalizing labels](#normalizing-labels): convert labels to ranges required + by mlpack classifiers + * [Formats](#formats): supported formats for each load/save variant + ## Numeric data Numeric data or general numeric matrices can be loaded or saved with the following functions. - - `data::Load(filename, matrix, fatal=false, transpose=true, - format=FileType::AutoDetect)` - - `data::Save(filename, matrix, fatal=false, transpose=true, - format=FileType::AutoDetect)` + - `data::Load(filename, matrix, fatal=false, transpose=true, format=FileType::AutoDetect)` + - `data::Save(filename, matrix, fatal=false, transpose=true, format=FileType::AutoDetect)` * `filename` is a `std::string` with a path to the file to be loaded. + * By default the format is auto-detected based on the file extension, but can be explicitly specified with `format`; see [Formats](#formats). + * `matrix` is an `arma::mat&`, `arma::Mat&`, or similar (e.g., a reference to an Armadillo object that data will be loaded into or saved from). + * If `fatal` is `true`, a `std::runtime_error` will be thrown on failure. + * If `transpose` is `true`, then for plaintext formats (CSV/TSV/ASCII), the matrix will be transposed on save. (Keep this `true` if you want a column-major matrix to be saved with points as rows and dimensions as columns; that is generally what is desired.) + * A `bool` is returned indicating whether the operation was successful. --- @@ -48,8 +63,8 @@ std::cout << " - " << dataset.n_rows << " dimensions." << std::endl; std::cout << "The labels in 'satellite.train.labels.csv' have: " << std::endl; std::cout << " - " << labels.n_elem << " labels." << std::endl; -std::cout << " - A maximum label of " << labels.max() << std::endl; -std::cout << " - A minimum label of " << labels.min() << std::endl; +std::cout << " - A maximum label of " << labels.max() << "." << std::endl; +std::cout << " - A minimum label of " << labels.min() << "." << std::endl; // Modify and save the data. Add 2 to the data and drop the last column. dataset += 2; @@ -70,7 +85,7 @@ mlpack, string data and other non-numerical data must be mapped to categorical values and represented as part of an `arma::mat`. Category information is stored in an auxiliary `data::DatasetInfo` object. -### `DatasetInfo` +### `data::DatasetInfo` @@ -79,15 +94,23 @@ mlpack represents categorical data via the use of the auxiliary numeric or categorical and allows conversion from the original category values to the numeric values used to represent those categories. +--- + +#### Constructors + - `info = data::DatasetInfo()` - * Create an empty `DatasetInfo` object. - * Use this constructor if you intend to populate the `DatasetInfo` via a - `data::Load()` call. + * Create an empty `data::DatasetInfo` object. + * Use this constructor if you intend to populate the `data::DatasetInfo` via + a `data::Load()` call. - `info = data::DatasetInfo(dimensionality)` - * Create a `DatasetInfo` object with the given dimensionality + * Create a `data::DatasetInfo` object with the given dimensionality * All dimensions are assumed to be numeric (not categorical). +--- + +#### Accessing and setting properties + - `info.Type(d)` * Get the type (categorical or numeric) of dimension `d`. * Returns a `data::Datatype`, either `data::Datatype::numeric` or @@ -103,9 +126,13 @@ to the numeric values used to represent those categories. - `info.Dimensionality()` * Return the dimensionality of the object as a `size_t`. - - `info.MapString(value, d)` - * Given `value` (a `std::string`), return the `size_t` representing the - categorical mapping of `value` in dimension `d`. +--- + +#### Map to and from numeric values + + - `info.MapString(value, d)` + * Given `value` (a `std::string`), return the `double` representing the + categorical mapping (an integer value) of `value` in dimension `d`. * If a mapping for `value` does not exist in dimension `d`, a new mapping is created, and `info.NumMappings(d)` is increased by one. * If dimension `d` is numeric and `value` cannot be parsed as a numeric @@ -119,10 +146,13 @@ to the numeric values used to represent those categories. --- -With a `DatasetInfo` object, categorical data can be loaded: +### Loading categorical data + +With a `data::DatasetInfo` object, categorical data can be loaded: - `data::Load(filename, matrix, info, fatal=false, transpose=true)` * `filename` is a `std::string` with a path to the file to be loaded. + * The format is auto-detected based on the extension of the filename and the contents of the file: - `.csv`, `.tsv`, or `.txt` for CSV/TSV (tab-separated)/ASCII @@ -132,14 +162,18 @@ With a `DatasetInfo` object, categorical data can be loaded: * `matrix` is an `arma::mat&`, `arma::Mat&`, or similar (e.g., a reference to an Armadillo object that data will be loaded into or saved from). + * `info` is a `data::DatasetInfo&` object. This will be populated with the category information of the file when loading, and used to unmap values when saving. + * If `fatal` is `true`, a `std::runtime_error` will be thrown on failure. + * If `transpose` is `true`, then for plaintext formats (CSV/TSV/ASCII), the matrix will be transposed on save. (Keep this `true` if you want a column-major matrix to be saved with points as rows and dimensions as columns; that is generally what is desired.) + * A `bool` is returned indicating whether the operation was successful. Saving should be performed with the [numeric](#numeric-data) `data::Load()` @@ -187,7 +221,7 @@ for (size_t d = 0; d < info.Dimensionality(); ++d) { // This will create a new mapping if the string "hooray!" does not already // exist as a category for dimension d.. - dataset(d, 4) = info.MapString("hooray!", d); + dataset(d, 4) = info.MapString("hooray!", d); } else { @@ -198,7 +232,7 @@ for (size_t d = 0; d < info.Dimensionality(); ++d) --- -Example usage to manually create a `DatasetInfo` object. +Example usage to manually create a `data::DatasetInfo` object. ```c++ // This will manually create the following data matrix (shown as it would appear @@ -232,20 +266,20 @@ dataset(0, 4) = 5; dataset(0, 5) = 6; // The second dimension is categorical. -dataset(1, 0) = info.MapString("TRUE", 1); -dataset(1, 1) = info.MapString("FALSE", 1); -dataset(1, 2) = info.MapString("FALSE", 1); -dataset(1, 3) = info.MapString("TRUE", 1); -dataset(1, 4) = info.MapString("TRUE", 1); -dataset(1, 5) = info.MapString("FALSE", 1); +dataset(1, 0) = info.MapString("TRUE", 1); +dataset(1, 1) = info.MapString("FALSE", 1); +dataset(1, 2) = info.MapString("FALSE", 1); +dataset(1, 3) = info.MapString("TRUE", 1); +dataset(1, 4) = info.MapString("TRUE", 1); +dataset(1, 5) = info.MapString("FALSE", 1); // The third dimension is categorical. -dataset(2, 0) = info.MapString("good", 2); -dataset(2, 1) = info.MapString("good", 2); -dataset(2, 2) = info.MapString("bad", 2); -dataset(2, 3) = info.MapString("bad", 2); -dataset(2, 4) = info.MapString("unknown", 2); -dataset(2, 5) = info.MapString("unknown", 2); +dataset(2, 0) = info.MapString("good", 2); +dataset(2, 1) = info.MapString("good", 2); +dataset(2, 2) = info.MapString("bad", 2); +dataset(2, 3) = info.MapString("bad", 2); +dataset(2, 4) = info.MapString("unknown", 2); +dataset(2, 5) = info.MapString("unknown", 2); // The fourth dimension is numeric. dataset(3, 0) = 7.0; @@ -259,12 +293,12 @@ dataset(3, 5) = 5.1; // category values in the order they are seen, even if the category can be // parsed as a number. So, here, the value '4' will be assigned category '0', // since it is seen first. -dataset(4, 0) = info.MapString("4", 4); -dataset(4, 1) = info.MapString("3", 4); -dataset(4, 2) = info.MapString("4", 4); -dataset(4, 3) = info.MapString("1", 4); -dataset(4, 4) = info.MapString("0", 4); -dataset(4, 5) = info.MapString("2", 4); +dataset(4, 0) = info.MapString("4", 4); +dataset(4, 1) = info.MapString("3", 4); +dataset(4, 2) = info.MapString("4", 4); +dataset(4, 3) = info.MapString("1", 4); +dataset(4, 4) = info.MapString("0", 4); +dataset(4, 5) = info.MapString("2", 4); // Print the dataset with mapped categories. dataset.print("Dataset with mapped categories"); @@ -277,10 +311,8 @@ for (size_t i = 0; i < info.NumMappings(2); ++i) << std::endl; } - -// Save as a CSV. Note that this will look the same as the comment at the start -// of this example! -mlpack::data::Save("manual_categorical_data.csv", dataset, info); +// Now `dataset` is ready for use with an mlpack algorithm that supports +// categorical data. ``` --- @@ -298,31 +330,39 @@ Supported formats for saving are `jpg`, `png`, `tga`, `bmp`, and `hdr`. When loading images, each image is represented as a flattened single column vector in a data matrix; each row of the resulting vector will correspond to a -single pixel value in a single channel. An auxiliary `ImageInfo` class is used -to store information about the images. +single pixel value in a single channel. An auxiliary `data::ImageInfo` class is +used to store information about the images. -### `ImageInfo` +### `data::ImageInfo` -The `ImageInfo` class contains the metadata of the images. +The `data::ImageInfo` class contains the metadata of the images. - - `info = ImageInfo()` - * Create an `ImageInfo` object with no data. - * Use this constructor if you intend to populate the `DatasetInfo` via a +--- + +#### Constructors + + - `info = data::ImageInfo()` + * Create a `data::ImageInfo` object with no data. + * Use this constructor if you intend to populate the `data::ImageInfo` via a `data::Load()` call. - - `info = ImageInfo(width, height, channels)` - * Create an `ImageInfo` object with the given image specifications. + - `info = data::ImageInfo(width, height, channels)` + * Create a `data::ImageInfo` object with the given image specifications. * `width` and `height` are specified as pixels. - - `info.Quality(q)` will set the compression quality (e.g. for saving JPEGs) to - `q`. +--- + +#### Accessing and modifying image metadata + + - `info.Quality() = q` will set the compression quality (e.g. for saving JPEGs) + to `q`. * `q` should take values between `0` and `100`. * The quality value is ignored unless calling `data::Save()` with `info`. - - Calling `info.Channels(1)` before loading will cause images to be loaded in - grayscale. + - Calling `info.Channels() = 1` before loading will cause images to be loaded + in grayscale. - - Metadata stored in the `ImageInfo()` can be accessed with the following + - Metadata stored in the `data::ImageInfo` can be accessed with the following members: * `info.Width()` returns the image width in pixels. * `info.Height()` returns the image height in pixels. @@ -332,53 +372,76 @@ The `ImageInfo` class contains the metadata of the images. --- -With an `ImageInfo` object, image data can be loaded or saved, handling either -one or multiple images at a time: +### Loading images + +With a `data::ImageInfo` object, image data can be loaded or saved, handling +either one or multiple images at a time: - `data::Load(filename, matrix, info, fatal=false)` - * Load a *single image* from `filename` into `matrix`. - * Format is chosen by extension (e.g. `image.png` will load as PNG). + * Load a ***single image*** from `filename` into `matrix`. + - Format is chosen by extension (e.g. `image.png` will load as PNG). + * `matrix` will have one column representing the image as a flattened vector. + * `info` will be populated with information from the image in `filename`. + * If `fatal` is `true`, a `std::runtime_error` will be thrown upon load failure. + * Returns a `bool` indicating the success of the operation. +--- + - `data::Load(files, matrix, info, fatal=false)` - * Load *multiple images* from `files` into `matrix`. - * `files` is of type `std::vector` and should contain the list - of images to be loaded. - * `matrix` will have `files.size()` columns, each representing the - corresponding image as a flattened vector. + * Load ***multiple images*** from `files` into `matrix`. + - `files` is of type `std::vector` and should contain the list + of images to be loaded. + - `matrix` will have `files.size()` columns, each representing the + corresponding image as a flattened vector. + * `info` will be populated with information from the images in `files`. + * If `fatal` is `true`, a `std::runtime_error` will be thrown if any files fail to load. + * Returns a `bool` indicating the success of the operation. +--- + - `data::Save(filename, matrix, info, fatal=false)` - * Save a *single image* from `matrix` into the file `filename`. - * Format is chosen by extension (e.g. `image.png` will save as PNG). + * Save a ***single image*** from `matrix` into the file `filename`. + - Format is chosen by extension (e.g. `image.png` will save as PNG). + * `matrix` is expected to have only one column representing the image as a flattened vector. + * If `fatal` is `true`, a `std::runtime_error` will be thrown in the event of save failure. + * Returns a `bool` indicating the success of the operation. +--- + - `data::Save(files, matrix, info, fatal=false)` - * Save *multiple images* from `matrix` into `files`. - * `files` is of type `std::vector` and should contain the list - of files to save to. - * The format of each file is chosen by extension (e.g. `image.png` will save - as PNG); it is allowed for filenames in `files` to have different - extensions. + * Save ***multiple images*** from `matrix` into `files`. + - `files` is of type `std::vector` and should contain the list + of files to save to. + - The format of each file is chosen by extension (e.g. `image.png` will + save as PNG); it is allowed for filenames in `files` to have different + extensions. + * `matrix` is expected to have `files.size()` columns representing images as flattened vectors. + * If `fatal` is `true`, a `std::runtime_error` will be thrown if any images fail to save. + * Returns a `bool` indicating the success of the operation. +--- + Images are flattened along rows, with channel values interleaved, starting from the top left. Thus, the value of the pixel at position `(x, y)` in channel `c` will be contained in element/row `y * (width * channels) + x * (channels) + c` @@ -412,7 +475,7 @@ std::cout << matrix[index] << "." << std::endl; // Increment each pixel value, but make sure they are still within the bounds. matrix += 1; -matrix.clamp(0, 255); +matrix = arma::clamp(matrix, 0, 255); mlpack::data::Save("numfocus-logo-mod.png", matrix, info); ``` @@ -434,7 +497,7 @@ images.push_back("ensmallen-favicon.png"); images.push_back("armadillo-favicon.png"); images.push_back("bandicoot-favicon.png"); -ImageInfo info; +mlpack::data::ImageInfo info; info.Channels(1); // Force loading in grayscale. arma::mat matrix; @@ -449,7 +512,7 @@ std::cout << "Loaded " << matrix.n_cols << " images. Images are of size " matrix = (255.0 - matrix); // Save as compressed JPEGs with low quality. -info.Quality(75); +info.Quality() = 75; std::vector outImages; outImages.push_back("mlpack-favicon-inv.jpeg"); outImages.push_back("ensmallen-favicon-inv.jpeg"); @@ -469,21 +532,25 @@ Each object must be given a logical name. - `data::Load(filename, name, object, fatal=false, format=data::format::autodetect)` - `data::Save(filename, name, object, fatal=false, format=data::format::autodetect)` * Load/save `object` to/from `filename` with the logical name `name`. + * If `fatal` is `true`, a `std::runtime_error` will be thrown in the event of load or save failure. + * The format is autodetected based on extension (`.bin`, `.json`, or `.xml`), but can be manually specified: - `data::format::binary`: binary blob (smallest and fastest). No checks; assumes all data is correct. - `data::format::json`: JSON. - `data::format::xml`: XML (largest and slowest). + * For JSON and XML types, when loading, `name` must match the name used to save the object. + * Returns a `bool` indicating the success of the operation. ***Note:*** when loading an object that was saved as a binary blob, the C++ type -of the object must be *exactly the same* (including template parameters) as the -type used to save the object. If not, undefined behavior will occur---most +of the object must be ***exactly the same*** (including template parameters) as +the type used to save the object. If not, undefined behavior will occur---most likely a crash. --- @@ -526,19 +593,26 @@ mlpack classifiers and other algorithms require labels to be in the range `0` to `numClasses - 1`. A vector of labels with arbitrary (`size_t`) values can be normalized to the required range with the `NormalizeLabels()` function. - * `NormalizeLabels(labelsIn, labelsOut, mappings)` +--- + + * `data::NormalizeLabels(labelsIn, labelsOut, mappings)` - Map vector `labelsIn` into the range `0` to `numClasses - 1`, storing as `labelsOut` (of type `arma::Row`). - - `numClasses` is automatically detected using the number of unique values - in `labelsIn`. + * `numClasses` is automatically detected using the number of unique values + in `labelsIn`. + - The column vector `mappings` will be filled with the reverse mappings to convert back to the old labels; this can be used by `RevertLabels()`. + - `mappings[i]` contains the original class label for the mapped label `i`. - * `RevertLabels(labelsIn, mappings, labelsOut)` +--- + + * `data::RevertLabels(labelsIn, mappings, labelsOut)` - Unmap normalized labels `labelsIn` using `mappings` into `labelsOut`. - - Performs the reverse operation of `NormalizeLabels()`. - - `mappings` should be the same vector output by `NormalizeLabels()`. + + - Performs the reverse operation of `NormalizeLabels()`; `mappings` should + be the same vector output by `NormalizeLabels()`. --- @@ -557,18 +631,18 @@ arma::Row labels = { 3, 7, 3, 3, 5 }; // We will map them to that range using NormalizeLabels(). arma::Row mappedLabels; arma::Col mappings; -NormalizeLabels(labels, mappedLabels, mapping); +mlpack::data::NormalizeLabels(labels, mappedLabels, mappings); const size_t numClasses = mappedLabels.max() + 1; // Print the mapped values: // [3, 7, 3, 3, 5] maps to [0, 1, 0, 0, 2]. // The `mappings` vector will be [3, 7, 5]. std::cout << "Original labels: " << labels; -std::cout << "Mapped labels: " << mappedLabels; +std::cout << "Mapped labels: " << mappedLabels; std::cout << "Mappings: " << mappings; // Learn a model with the mapped labels. -DecisionTree d(dataset, mappedLabels, numClasses); +mlpack::DecisionTree d(dataset, mappedLabels, numClasses, 1 /* leaf size */); // Make predictions on the training dataset. arma::Row mappedPredictions; @@ -577,13 +651,13 @@ d.Classify(dataset, mappedPredictions); // The predictions use mapped labels (0, 1, 2), which we will need to map back // to the original labels using RevertLabels(). arma::Row predictions; -RevertLabels(mappedPredictions, mappings, predictions); +mlpack::data::RevertLabels(mappedPredictions, mappings, predictions); // Print the predictions before and after unmapping. // The mapped predictions will take values 0, 1, or 2; the predictions will take // values 3, 7, or 5 (like the original data). std::cout << "Mapped predictions: " << mappedPredictions; -std::cout << "Predictions: " << predictions; +std::cout << "Predictions: " << predictions; ``` ## Formats @@ -591,34 +665,49 @@ std::cout << "Predictions: " << predictions; mlpack's `data::Load()` and `data::Save()` functions support a variety of different formats in different contexts. - * [Numeric data](#numeric-data) - - By default, load/save format is autodetected, but can be manually specified - with the `format` parameter using one of the options below: - * `FileType::AutoDetect` (default): auto-detects the format as one of the - formats below using the extension of the filename and inspecting the file - contents. - * `FileType::CSVASCII` (autodetect extensions `.csv`, `.tsv`): CSV format - with no header. - * `FileType::RawASCII` (autodetect extensions `.csv`, `.txt`): - space-separated values or tab-separated values (TSV) with no header. - * `FileType::ArmaASCII` (autodetect extension `.txt`): space-separated - values as saved by Armadillo with the - [`arma_ascii`](https://arma.sourceforge.net/docs.html#save_load_mat) - format. - * `FileType::CoordASCII` (not autodetected, must be manually specified): - coordinate list format for sparse data (see - [`coord_ascii`](https://arma.sourceforge.net/docs.html#save_load_mat)). - * `FileType::ArmaBinary` (autodetect extension `.bin`): Armadillo's - efficient binary matrix format - ([`arma_binary`](https://arma.sourceforge.net/docs.html#save_load_mat)). - * `FileType::HDF5Binary` (autodetect extensions `.h5`, `.hdf5`, `.hdf`, - `.he5`): [HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) - binary format; only available if Armadillo is configured with - [HDF5 support](https://arma.sourceforge.net/docs.html#config_hpp). - * `FileType::RawBinary` (autodetect extension `.bin`): packed binary data - with no header and no size information; data will be loaded as a single - column vector _(not recommended)_. - * `FileType::PGMBinary` (autodetect extension `.pgm`): PGM image format +--- + +#### [Numeric data](#numeric-data) + +By default, load/save format is ***autodetected***, but can be manually +specified with the `format` parameter using one of the options below: + + - `FileType::AutoDetect` (default): auto-detects the format as one of the + formats below using the extension of the filename and inspecting the file + contents. + + - `FileType::CSVASCII` (autodetect extensions `.csv`, `.tsv`): CSV format + with no header. + + - `FileType::RawASCII` (autodetect extensions `.csv`, `.txt`): + space-separated values or tab-separated values (TSV) with no header. + + - `FileType::ArmaASCII` (autodetect extension `.txt`): space-separated + values as saved by Armadillo with the + [`arma_ascii`](https://arma.sourceforge.net/docs.html#save_load_mat) + format. + + - `FileType::CoordASCII` (not autodetected, must be manually specified): + coordinate list format for sparse data (see + [`coord_ascii`](https://arma.sourceforge.net/docs.html#save_load_mat)). + + - `FileType::ArmaBinary` (autodetect extension `.bin`): Armadillo's + efficient binary matrix format + ([`arma_binary`](https://arma.sourceforge.net/docs.html#save_load_mat)). + + - `FileType::HDF5Binary` (autodetect extensions `.h5`, `.hdf5`, `.hdf`, + `.he5`): [HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) + binary format; only available if Armadillo is configured with + [HDF5 support](https://arma.sourceforge.net/docs.html#config_hpp). + + - `FileType::RawBinary` (autodetect extension `.bin`): packed binary data + with no header and no size information; data will be loaded as a single + column vector _(not recommended)_. + + - `FileType::PGMBinary` (autodetect extension `.pgm`): PGM image format + +***Notes:*** + - ASCII formats (`CSVASCII`, `RawASCII`, `ArmaASCII`) are human-readable but large; to reduce dataset size, consider a binary format such as `ArmaBinary` or `HDF5Binary`. @@ -626,30 +715,44 @@ different formats in different contexts. binary format (`ArmaBinary` or `HDF5Binary`) or as a coordinate list (`CoordASCII`). - * [Mixed categorical data](#mixed-categorical-data) - - The format of mixed categorical data is detected automatically based on the - file extension and inspecting the file contents: - * `.csv`, `.txt`, or `.tsv` indicates CSV/TSV/ASCII format - * `.arff` indicates [ARFF](https://www.cs.waikato.ac.nz/~ml/weka/arff.html) +--- - * [Image data](#image-data) - - The format of images are detected automatically based on the file - extension. - - The following formats are supported for loading: `.jpg`, `.jpeg`, `.png`, - `.tga`, `.bmp`, `.psd`, `.gif`, `.hdr`, `.pic`, `.pnm` - - The following formats are supported for saving: `.jpg`, `.png`, `.tga`, - `.bmp`, `.hdr` +#### [Mixed categorical data](#mixed-categorical-data) - * [mlpack objects](#mlpack-objects) - - By default, load/save format for mlpack objects is autodetected, but can be - manually specified with the `format` parameter using one of the options - below: - * `format::autodetect` (default): auto-detects the format as one of the - formats below using the extension of the filename - * `format::json` (autodetect extension `.json`) - * `format::xml` (autodetect extension `.xml`) - * `format::binary` (autodetect extension `.bin`) - - `format::json` (`.json`) and `format::xml` (`.xml`) produce human-readable - files, but they may be quite large. - - `format::binary` (`.bin`) is recommended for the sake of size; objects in - binary format may be an order of magnitude or more smaller than JSON! +The format of mixed categorical data is detected automatically based on the +file extension and inspecting the file contents: + + - `.csv`, `.txt`, or `.tsv` indicates CSV/TSV/ASCII format + - `.arff` indicates [ARFF](https://www.cs.waikato.ac.nz/~ml/weka/arff.html) + +--- + +#### [Image data](#image-data) + +The format of images are detected automatically based on the file extension. + + - The following formats are supported for loading: `.jpg`, `.jpeg`, `.png`, + `.tga`, `.bmp`, `.psd`, `.gif`, `.hdr`, `.pic`, `.pnm` + + - The following formats are supported for saving: `.jpg`, `.png`, `.tga`, + `.bmp`, `.hdr` + +--- + +#### [mlpack objects](#mlpack-objects) + +By default, load/save format for mlpack objects is autodetected, but can be +manually specified with the `format` parameter using one of the options below: + + - `format::autodetect` (default): auto-detects the format as one of the + formats below using the extension of the filename + - `format::json` (autodetect extension `.json`) + - `format::xml` (autodetect extension `.xml`) + - `format::binary` (autodetect extension `.bin`) + +***Notes:*** + + - `format::json` (`.json`) and `format::xml` (`.xml`) produce human-readable + files, but they may be quite large. + - `format::binary` (`.bin`) is recommended for the sake of size; objects in + binary format may be an order of magnitude or more smaller than JSON! diff --git a/doc/user/matrices.md b/doc/user/matrices.md index fa01a290b5..b9c8135d6f 100644 --- a/doc/user/matrices.md +++ b/doc/user/matrices.md @@ -1,14 +1,23 @@ # Matrices in mlpack -mlpack uses Armadillo matrices for matrix support. Armadillo is a fast C++ -matrix library which makes use of advanced template metaprogramming techniques -to provide the fastest possible linear algebra operations. +mlpack uses Armadillo matrices for linear algebra support. Armadillo is a fast +C++ matrix library which uses advanced template metaprogramming techniques to +provide the fastest possible linear algebra operations. -Documentation on Armadillo can be found on [the Armadillo +

Armadillo logo

+ +Detailed documentation on Armadillo can be found on [the Armadillo website](http://arma.sourceforge.net/docs.html). Nonetheless, there are a few further caveats for mlpack Armadillo usage. + * [An Armadillo primer](#an-armadillo-primer) + * [Representing data in mlpack](#representing-data-in-mlpack) + * [Loading data](#loading-data) + * [Loading and using categorical data](#loading-and-using-categorical-data) + * [Alternate matrix types](#alternate-matrix-types) + * [Adapting from other toolkits (Eigen, etc.)](#adapting-from-other-toolkits-eigen-etc) + ## An Armadillo primer The Armadillo syntax is straightforward and is aimed at ease-of-use and @@ -20,15 +29,15 @@ matrix operations. // Create a 10x15 matrix with random elements. arma::mat m(10, 15, arma::fill::randu); -std::cout << "Size of m: " << x.n_rows << " x " << x.n_cols << "." << std::endl; +std::cout << "Size of m: " << m.n_rows << " x " << m.n_cols << "." << std::endl; // Sum all elements in the matrix. const double sumVal = arma::accu(m); std::cout << "Sum of all elements: " << sumVal << "." << std::endl; // Sum the elements in each column. -arma::vec sums = arma::sum(m, 0); -std::cout << "Sums in each column: " << sums.t(); +arma::rowvec sums = arma::sum(m, 0); +std::cout << "Sums in each column: " << sums; // Add 1 to all elements. m += 1; @@ -50,16 +59,16 @@ For more information on Armadillo, see the following resources: ## Representing data in mlpack -Armadillo matrices, unlike numpy and some other toolkits, stores data in a -*column-major* format. This means that each column is located in contiguous +Armadillo matrices, unlike numpy and some other toolkits, store data in a +***column-major*** format. This means that each column is located in contiguous memory; i.e., `x(0, 0)` is adjacent to `x(1, 0)` in memory. This means that, for the vast majority of machine learning methods, it is faster -to store _observations as columns_ and _dimensions as rows_. This is counter to -most standard machine learning texts! It also has some implications for linear -algebra operations; for instance, computing the Gram matrix of a matrix `X` is -typically expressed as `X^T X`, but when using column-major matrices, the -expression must be `X X^T`. +to store ***observations as columns*** and ***dimensions as rows***. This is +counter to most standard machine learning texts! It also has some implications +for linear algebra operations; for instance, computing the Gram matrix of a +matrix `X` is typically expressed as `X^T X`, but when using column-major +matrices, the expression must be `X X^T`. In general, the following Armadillo types are commonly used inside mlpack: @@ -77,11 +86,8 @@ In general, the following Armadillo types are commonly used inside mlpack: mlpack provides two simple functions for loading and saving data matrices in a column-major form: - * `data::Load(filename, matrix, fatal=false, transpose=true, - type=FileType::AutoDetect)` ([full documentation](load_save.md#numeric_data)) - - * `data::Save(filename, matrix, fatal=false, transpose=true, - type=FileType::AutoDetect)` ([full documentation](load_save.md#numeric_data)) + * `data::Load(filename, matrix, fatal=false, transpose=true, type=FileType::AutoDetect)` ([full documentation](load_save.md#numeric_data)) + * `data::Save(filename, matrix, fatal=false, transpose=true, type=FileType::AutoDetect)` ([full documentation](load_save.md#numeric_data)) As an example, consider the following CSV file: @@ -117,11 +123,11 @@ mlpack::data::Load("data.csv", m, true); // - each row corresponds to a dimension! // std::cout << "The matrix in 'data.csv' has: " << std::endl; -std::cout << " - " << data.n_cols << " points." << std::endl; -std::cout << " - " << data.n_rows << " dimensions." << std::endl; +std::cout << " - " << m.n_cols << " points." << std::endl; +std::cout << " - " << m.n_rows << " dimensions." << std::endl; std::cout << "The second point in the dataset: " << std::endl; -std::cout << data.col(1).t(); +std::cout << m.col(1).t(); // Now modify the matrix and save to a different format (space-separated // values). @@ -131,25 +137,21 @@ mlpack::data::Save("data-mod.txt", m); Although Armadillo does provide a `.load()` and `.save()` member function for matrices, the `data::Load()` and `data::Save()` functions offer additional -flexibility, and ensure that data is loaded in a column-major format. +flexibility, and ensure that data is saved and loaded in a column-major format. ## Loading and using categorical data Some mlpack techniques support mixed categorical data, e.g., data where some dimensions take only categorical values (e.g. `0`, `1`, `2`, etc.). String data and other non-numerical data can be represented as categorical values, and -mlpack has support to load and save mixed categorical data: +mlpack has support to load mixed categorical data: * The `data::DatasetInfo` auxiliary class stores information about whether each dimension is numeric or categorical. ([full documentation](load_save.md#dataset_info)) - * `data::Load(filename, matrix, info, fatal=false, transpose=true)` ([full documentation](load_save.md#load_categorical)) - * `data::Save(filename, matrix, info, fatal=false, transpose=true)` ([full - documentation](load_save.md#save_categorical)) - For example, consider the following CSV file that contains strings: ```sh @@ -170,7 +172,8 @@ $ cat mixed_string_data.csv ``` The following program will load the data file, print information about -categorical dimensions, then save categorical data back to disk. +categorical dimensions, and prepare the data for use with an mlpack algorithm +that supports mixed categorical data. ```c++ // Load data from `mixed_string_data.csv` into `m`. Throw an exception on @@ -181,7 +184,7 @@ mlpack::data::Load("mixed_string_data.csv", m, info, true); // Print information about the data. std::cout << "The matrix in 'mixed_string_data.csv' has: " << std::endl; -std::cout << " - " << data.n_cols << " points." << std::endl; +std::cout << " - " << m.n_cols << " points." << std::endl; std::cout << " - " << info.Dimensionality() << " dimensions." << std::endl; // Print which dimensions are categorical. @@ -198,14 +201,12 @@ for (size_t d = 0; d < info.Dimensionality(); ++d) // Note that we manually map the string values; MapString() returns the category // for a given value. m(0, 2) = 4; -m(1, 2) = info.MapString("wonderful", 1); // Creates a new third category. +m(1, 2) = info.MapString("wonderful", 1); // Create new third category. m(2, 2) = 1; -m(3, 2) = info.MapString("c", 1); +m(3, 2) = info.MapString("c", 1); m(4, 2) = 0; -// Save the modified matrix. Note that "wonderful" will be automatically -// unmapped by `data::Save()`, as well as all other categorical values. -mlpack::data::Save("mixed_string_data_mod.csv", m, info); +// `m` can now be used with any mlpack algorithm that supports categorical data. ``` Not every mlpack method supports categorical data. Below are the list of @@ -255,8 +256,10 @@ arma::Row labels = // Train in the constructor, using floating-point data. // The weak learner type is now a floating-point Perceptron. -typedef Perceptron - PerceptronType; +typedef mlpack::Perceptron< + mlpack::SimpleWeightUpdate, + mlpack::ZeroInitialization, + arma::fmat> PerceptronType; mlpack::AdaBoost ab(dataset, labels, 5); // Create test data (500 points).