GMLDatasets

Dataset-backed demonstrations for GeometricMachineLearning and GeometricOptimizers.

Both of those packages are libraries for scientific machine learning and neither should depend on an image-dataset package to document itself. This one does: it holds the MLDatasets glue, the MNIST and Fashion-MNIST demonstrations, and the numerical experiment of [1] that shows manifold optimization making a vision transformer trainable at all.

The data loaders

mnist_data_loader and fashion_mnist_data_loader hand back a GeometricMachineLearning.DataLoader in the time series format a transformer consumes — each $28\times28$ image cut into 16 patches of $7\times7$, each patch flattened into a column, the labels one-hot encoded:

using GMLDatasets

dl = mnist_data_loader(:train; patch_length = 7, suppress_info = true)
(dl.input_dim, dl.input_time_steps, dl.n_params, dl.output_dim)
(49, 16, 60000, 10)

The pieces are available on their own too: mnist and fashion_mnist return the raw images and labels, split_and_flatten cuts an image into flattened patches and onehotbatch encodes the labels.

Where to go next

  • MNIST Tutorial trains a vision transformer with weights on the Stiefel manifold, using GeometricMachineLearning, and compares four optimizers on it.
  • The Numerical Experiment on Homogeneous Spaces carries the figures for a 500-epoch run of that experiment against GeometricOptimizers — training loss, test accuracy and drift off the manifold.
  • Running the Experiments is the operational side: what each script in scripts/ does, what the four configurations are for, and the device-memory handling the GPU runs need.

Library

GMLDatasets.GMLDatasetsModule
GMLDatasets

Dataset-backed demonstrations for GeometricMachineLearning and GeometricOptimizers.

Both of those packages are libraries for scientific machine learning and neither should depend on an image-dataset package to document itself. Everything that does lives here: the MLDatasets glue, the MNIST and Fashion-MNIST demonstrations and the numerical experiment from [1].

The entry points are mnist_data_loader and fashion_mnist_data_loader, which hand back a GeometricMachineLearning.DataLoader in the time series format a transformer wants:

using GMLDatasets

dl      = mnist_data_loader(:train; patch_length = 7)
dl_test = mnist_data_loader(:test;  patch_length = 7)
source
GeometricMachineLearning.DataLoaderMethod
DataLoader(images::AbstractArray{T, 3}, labels::AbstractVector)

Make an instance of GeometricMachineLearning.DataLoader for an image classification problem.

images is a tensor whose first two axes are the image axes and whose third axis indexes the images; labels is a vector of integer labels, one per image. Each image is cut into square patches by split_and_flatten and each patch is flattened into a column, which casts the data into the time series format a transformer consumes. The labels are encoded by onehotbatch.

Arguments

There are two keyword arguments:

  • patch_length = 7. This is the length of the patch in the $x$ and the $y$ direction;
  • suppress_info = false.

MNIST images are of size $28\times28$. For patch_length = 7 such an image is therefore split into 16 patches of $7\times7$, i.e. it becomes a matrix in $\mathbb{R}^{49\times16}$ [1].

Implementation

This method lives in GMLDatasets rather than in GeometricMachineLearning because it is the piece that is specific to labelled image data. It is also the only way to build a DataLoader whose input is Float32 and whose output is Int: the generic tensor–tensor constructor in GeometricMachineLearning requires a single shared element type, whereas a one-hot target is integer-valued whatever the images are.

source
GMLDatasets.fashion_mnistFunction
fashion_mnist(split = :train)

Return the Fashion-MNIST images and labels for split, which is either :train or :test.

Fashion-MNIST is drop-in compatible with MNIST — $28\times28$ grayscale images in ten classes — so everything that works on one works on the other. See mnist.

source
GMLDatasets.mnistFunction
mnist(split = :train)

Return the MNIST images and labels for split, which is either :train or :test.

This is MLDatasets.MNIST(split = split)[:] and nothing more — the images come back as a $28\times28\times{}n$ tensor of Float32 and the labels as a vector of Int in $0, \ldots, 9$. Use mnist_data_loader to get them in the form a transformer consumes.

The data set is downloaded on first use. Set ENV["DATADEPS_ALWAYS_ACCEPT"] = true to skip the download prompt in a non-interactive session.

source
GMLDatasets.mnist_data_loaderFunction
mnist_data_loader(split = :train; patch_length = 7, transform = identity, suppress_info = false)

Load MNIST and wrap it in a GeometricMachineLearning.DataLoader.

This composes mnist with DataLoader, i.e. it cuts each image into patches of patch_length $\times$ patch_length, flattens each patch into a column and one-hot encodes the labels.

transform is applied to the images and to the labels before the DataLoader is built, which is where the data are moved onto an accelerator:

using CUDA

dl = mnist_data_loader(:train; transform = cu)

The transform is applied before rather than after the DataLoader is constructed so that split_and_flatten and onehotbatch run their kernels on the device.

source
GMLDatasets.onehotbatchMethod
onehotbatch(target)

Performs a one-hot-batch encoding of a vector of integers: $input\in\{0,1,\ldots,9\}^\ell$.

The output is a tensor of shape $10\times1\times\ell$.

If the input is $0$, this function produces:

\[0 \mapsto \begin{bmatrix} 1 & 0 & \ldots & 0 \end{bmatrix}^T.\]

In more abstract terms: $i \mapsto e_i$.

Examples

using GMLDatasets

target = [0]
onehotbatch(target)

# output

10×1×1 Array{Int64, 3}:
[:, :, 1] =
 1
 0
 0
 0
 0
 0
 0
 0
 0
 0
source
GMLDatasets.split_and_flattenMethod
split_and_flatten(input::AbstractArray)::AbstractArray

Perform a preprocessing of an image into flattened patches.

This rearranges the input data so that it can easily be processed with a transformer.

Examples

Consider a matrix of size $6\times6$ which we want to divide into patches of size $3\times3$.

using GMLDatasets

input = [ 1  2  3  4  5  6;
          7  8  9 10 11 12; 
         13 14 15 16 17 18;
         19 20 21 22 23 24; 
         25 26 27 28 29 30; 
         31 32 33 34 35 36]

split_and_flatten(input; patch_length = 3, number_of_patches = 4)

# output

9×4 Matrix{Int64}:
  1  19   4  22
  7  25  10  28
 13  31  16  34
  2  20   5  23
  8  26  11  29
 14  32  17  35
  3  21   6  24
  9  27  12  30
 15  33  18  36

Here we see that split_and_flatten:

  1. splits the original matrix into four $3\times3$ matrices and then
  2. flattens each matrix into a column vector of size $9.$

After this all the vectors are put together again to yield a $9\times4$ matrix.

Arguments

The optional keyword arguments are:

  • patch_length: by default this is 7.
  • number_of_patches: by default this is 16.

The sizes of the first and second axis of the output of split_and_flatten are

  1. $\mathtt{path\_length}^2$ and
  2. number_of_patches.
source