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.GMLDatasets — Module
GMLDatasetsDataset-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)GeometricMachineLearning.DataLoader — Method
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.
GMLDatasets.fashion_mnist — Function
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.
GMLDatasets.fashion_mnist_data_loader — Function
fashion_mnist_data_loader(split = :train; patch_length = 7, transform = identity, suppress_info = false)Load Fashion-MNIST and wrap it in a GeometricMachineLearning.DataLoader.
Like mnist_data_loader, but for fashion_mnist.
GMLDatasets.mnist — Function
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.
GMLDatasets.mnist_data_loader — Function
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.
GMLDatasets.onehotbatch — Method
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
0GMLDatasets.split_and_flatten — Method
split_and_flatten(input::AbstractArray)::AbstractArrayPerform 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 36Here we see that split_and_flatten:
- splits the original matrix into four $3\times3$ matrices and then
- 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
- $\mathtt{path\_length}^2$ and
number_of_patches.