Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/_static/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,18 @@ html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(
.wy-alert.wy-alert-warning .rst-content .admonition-title,
.wy-alert.wy-alert-warning .wy-alert-title {
background: #e94f3b;
}


html.writer-html5 .rst-content table.docutils th {
border: none;
}

html,
body {
background: #ffffff;
}

.wy-nav-content-wrap {
background: none;
}
99 changes: 99 additions & 0 deletions docs/classification.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
HDC Learning
============

After learning about representing and manipulating information in hyperspace, we can implement our first HDC classification model! We will use as an example the famous MNIST dataset that contains images of handwritten digits.


We start by importing Torchhd and any other libraries we need:

.. code-block:: python

import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision.datasets import MNIST
import torchmetrics

from torchhd import functional
from torchhd import embeddings

Datasets
--------

Next, we load the training and testing datasets:

.. code-block:: python

transform = torchvision.transforms.ToTensor()

train_ds = MNIST("../data", train=True, transform=transform, download=True)
train_ld = torch.utils.data.DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True)

test_ds = MNIST("../data", train=False, transform=transform, download=True)
test_ld = torch.utils.data.DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False)


In addition to the various datasets available in the Torch ecosystem, such as MNIST, the :ref:`datasets` module provides interface to several commonly used datasets in HDC. Such interfaces inherit from PyTorch's dataset class, ensuring interoperability with other datasets.

Training
--------

To perform the training, we start by defining a model. In addition to specifying the basis-hypervectors sets, the core part of the model is the encoding function. In the example below, we use random-hypervectors and level-hypervectors to encode the position and value of each pixel, respectively:

.. code-block:: python

class Model(nn.Module):
def __init__(self, num_classes, size):
super(Model, self).__init__()

self.flatten = torch.nn.Flatten()

self.position = embeddings.Random(size * size, DIMENSIONS)
self.value = embeddings.Level(NUM_LEVELS, DIMENSIONS)

self.classify = nn.Linear(DIMENSIONS, num_classes, bias=False)
self.classify.weight.data.fill_(0.0)

def encode(self, x):
x = self.flatten(x)
sample_hv = functional.bind(self.position.weight, self.value(x))
sample_hv = functional.multiset(sample_hv)
return functional.hard_quantize(sample_hv)

def forward(self, x):
enc = self.encode(x)
logit = self.classify(enc)
return logit


model = Model(len(train_ds.classes), IMG_SIZE)
model = model.to(device)


Having defined the model, we iterate over the training samples to create the class-vectors:

.. code-block:: python

for samples, labels in train_ld:
samples = samples.to(device)
labels = labels.to(device)

samples_hv = model.encode(samples)
model.classify.weight[labels] += samples_hv

model.classify.weight[:] = F.normalize(model.classify.weight)

Testing
-------

With the model trained, we can classify the testing samples by encoding them and comparing them to the class-vectors:

.. code-block:: python

for samples, labels in test_ld:
samples = samples.to(device)

outputs = model(samples)
predictions = torch.argmax(outputs, dim=-1)
accuracy.update(predictions.cpu(), labels)
2 changes: 2 additions & 0 deletions docs/datasets.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _datasets:

torchhd.datasets
================

Expand Down
2 changes: 2 additions & 0 deletions docs/embeddings.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _embeddings:

torchhd.embeddings
==================

Expand Down
2 changes: 2 additions & 0 deletions docs/functional.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _functional:

torchhd.functional
=========================

Expand Down
109 changes: 109 additions & 0 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
Getting started
===============

In this tutorial, we demonstrate some basic features of Torchhd. We show how the library makes it easy to represent and manipulate information in hyperspace through the fictitious example in the following table:

.. list-table::
:widths: 10 10 10 10
:align: center
:header-rows: 1

* - Record
- Fruit
- Weight
- Season
* - :math:`r_1`
- apple
- 149.0
- fall
* - :math:`r_2`
- lemon
- 70.5
- winter
* - :math:`r_3`
- mango
- 173.2
- summer


Basis-hypervectors
------------------

The first step to encode these records is to define the basis-hypervectors for each information set. Since the nature of these information sets is different, so are the basis-hypervector sets used to encode them. We start by defining the number of dimensions of the hyperspace and then using the methods from the :ref:`functional` module to create the basis-hypervectors with the apropriate correlation profile:

.. code-block:: python

from torchhd import functional

d = 10000 # dimensions
fruits = functional.random_hv(3, d)
weights = functional.level_hv(10, d)
seasons = functional.circular_hv(4, d)
var = functional.random_hv(3, d)

which creates hypervectors for the 3 fruit types, 10 weight levels, 4 seasons and the 3 variables.

Similar behavior can be achieved using the classes in the :ref:`embeddings` module. The classes add convenience methods for mapping values to hypervectors. For example, to map the interval :math:`[0, 200]` to the ten weight hypervectors the :ref:`functional<functional>` version above requires an explicit mapping to an index:

.. code-block:: python

import torch

weight = torch.tensor([149.0])
# explicit mapping of the fruit weight to an index
w_i = functional.value_to_index(weight, 0, 200, 10)
weights[w_i] # select representation of 149

whereas the :ref:`embeddings<embeddings>` have this common behavior built-in:

.. code-block:: python

from torchhd import embeddings

W_emb = embeddings.Level(10, d, low=0, high=200)
# select representation of 149
W_emb(weight) # same result as weights[w_i]

Operations
----------

Once the basis-hypervectors are defined, we can use the MAP operations from :ref:`functional` to represent more complex objects. The hypervector for record :math:`r_1` can then be created as follows:

.. code-block:: python

f = functional.bind(var[0], fruits[0]) # fruit = apple
w = functional.bind(var[1], weights[w_i]) # weight = 149
s = functional.bind(var[2], seasons[3]) # season = fall
r1 = functional.bundle(functional.bundle(f, w), s)

which is equivalent to using the following shortened syntax:

.. code-block:: python

r1 = var[0] * fruits[0] + var[1] * weights[w_i] + var[2] * seasons[3]

Data Structures
---------------

Alternatively, we can use one of the commonly used encodings provided in the :ref:`functional` module. Using these, record :math:`r_1` can be encoded as follows:

.. code-block:: python

# combine values in one tensor of shape (3, d)
values = torch.stack([fruits[0], weights[w_i], seasons[3]])
r1 = functional.hash_table(var, values)

The :ref:`structures` module contains the same encoding patterns in addition to binary trees and finite state automata, but provides them as data structures. This module provides class-based implementations of HDC data structures. Using the hash table class, record :math:`r_1` can be implemented as follows:

.. code-block:: python

from torchhd import structures

r1 = structures.HashTable(d) # r1 = 0
r1.add(var[0], fruits[0]) # r1 + var[0] * fruits[0]
r1.add(var[1], weights[w_i]) # r1 + var[1] * weights[w_i]
r1.add(var[2], seasons[3]) # r1 + var[2] * seasons[3]
# query the hash table by key:
fruit = r1.get(var[0]) # r1 * var[0]
weight = r1.get(var[1]) # r1 * var[1]
season = r1.get(var[2]) # r1 * var[2]
8 changes: 8 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ Welcome to the Torchhd documentation!

*Torchhd* is a Python library dedicated to Hyperdimensional Computing and the operations related to it.

.. toctree::
:glob:
:maxdepth: 1
:caption: Tutorials

getting_started
classification

.. toctree::
:maxdepth: 2
:caption: Package Reference:
Expand Down
2 changes: 2 additions & 0 deletions docs/structures.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _structures:

torchhd.structures
==================

Expand Down