Skip to main content

gustavgrad

Tests codecov PyPI Python Version Code style: black

An autograd library built on NumPy, inspired by Joel Grus's livecoding.

Installation

With pip

pip install gustavgrad

How to use the library

Tensor operations

The Tensor class is the cornerstone of gustavgrad. It behaves much like an ordinary numpy.ndarray.

Tensors can be added together,

>>> from gustavgrad import Tensor
>>> x = Tensor([1, 2, 3])
>>> x + x
Tensor(data=[2 4 6], requires_grad=False)

... subracted from each other,

>>> x - x
Tensor(data=[0 0 0], requires_grad=False)

... their dot-product can calculated,

>>> x * x
Tensor(data=[1 4 9], requires_grad=False)

... and they can be multiplied with each other.

>>> y = Tensor([[1], [2], [3]])
>>> x @ y
Tensor(data=[14], requires_grad=False)

Tensor operations also support broadcasting:

>>> x * 3
Tensor(data=[3 6 9], requires_grad=False)
>>> z = Tensor([[1, 2, 3], [4, 5, 6]])
>>> x * z
Tensor(data=
[[ 1  4  9]
 [ 4 10 18]], requires_grad=False)

Automatic backpropagation

But a Tensor is not just an ndarray, they also keep track of their own gradient.

>>> speed = Tensor(1, requires_grad=True)
>>> time = Tensor(10, requires_grad=True)
>>> distance = speed * time
>>> distance
Tensor(data=10, requires_grad=True)

If a Tensor is created as the result of a Tensor operation involving a Tensor with requires_grad=True, the resulting Tensor will be able to backpropagate it's own gradient to it's ancestor.

>>> distance.backward()
>>> speed.grad
array(10.)
>>> time.grad
array(1.)

By calling the backward method on distance the gradient of speed and time is automatically updated. We can see that increasing speed by 1 would result in an increase in distance by 10, while an increase in time by 1 would only increase distance by 1.

The Tensor class supports backpropagation over arbitrary compositions of Tensor operations.

>>> t1 = Tensor([[1, 2, 3], [4, 5, 6]], requires_grad=True)
>>> t2 = Tensor([[1], [2], [3]])
>>> t3 = t1 @ t2 + 1
>>> t4 = t3 * 7
>>> t5 = t4.sum()
>>> t5.backward()
>>> t1.grad
array([[ 7., 14., 21.],
       [ 7., 14., 21.]])

The Module API

gustavgrad provides some tools to simplify setting up and training machine learning models. The Module API makes it easier to manage multiple related Tensors by registering them as Parameters. A Parameter is just a randomly initialized Tensor.

from gustavgrad.module import Module, Parameter
from gustavgrad.function import tanh
class MultilayerPerceptron(Module):
    def __init__(self, input_size: int, output_size: int, hidden_size: int = 100) -> None:
        self.layer1 = Parameter(input_size, hidden_size)
        self.bias1 = Parameter(hidden_size)
        self.layer2 = Parameter(hidden_size, output_size)
        self.bias2 = Parameter(output_size)
        
    def predict(self, x: Tensor) -> Tensor:
        x = x @ self.layer1 + self.bias1
        x = tanh(x)
        x = x @ self.layer2 + self.bias2
        return x

By subclassing Module our MultilayerPerceptron class automatically gets some helper methods for managing its Parameters. Let's create a MultilayerPerceptron that tries to learn the XOR function.

xor_input = Tensor([[0, 0], [0, 1], [1, 0], [1, 1]])
xor_targets = Tensor([[0], [1], [1], [0]])
xor_mlp = MultilayerPerceptron(input_size=2, output_size=1, hidden_size=4)

We can use the model to make predictions on the xor_input Tensor.

>>> predictions = xor_mlp.predict(xor_input)
>>> predictions
Tensor(data=
[[-1.79888385]
 [-1.07965756]
 [ 0.34373135]
 [ 1.63366069]], requires_grad=True)

The predictions of the randomly initialized model aren't right, but we can improve the model by calculating the gradient of it's Parameters in respect to a loss function.

from gustavgrad.loss import SquaredErrorLoss
se_loss = SquaredErrorLoss()
loss = se_loss.loss(xor_targets, predictions)
loss.backward()

loss is a Tensor, so we can call its backward method to do backpropagation through our xor_mlp. We can then adjust the weights of all Parameters in xor_mlp using gradient descent:

from gustavgrad.optim import SGD
optim = SGD(lr=0.01)
optim.step(xor_mlp)

After updating the weights we can reset the gradients of all parameters and make new predictions:

>>> xor_mlp.zero_grad()
>>> predictions = xor_mlp.predict(xor_input)
>>> predictions
Tensor(data=
[[-1.51682686]
 [-0.78583272]
 [ 0.55994602]
 [ 1.67962174]], requires_grad=True)

See examples/xor.py for a full example of how gustavgrad can be used to learn the XOR function. The examples directory also contains some other basic examples of how the library can be used.

Metadata

Release files for gustavgrad 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gustavgrad 0.2.0
File Size Uploaded Provenance
gustavgrad-0.2.0.tar.gz 10.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for gustavgrad 0.2.0
File Interpreter ABI Platform
gustavgrad-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 19.3 kB

Release files / gustavgrad-0.2.0.tar.gz

Download URL gustavgrad-0.2.0.tar.gz
Size 10.1 kB
Tags Source
SHA-256 checksum
How to use checksums
69b135a8e0f26c1bb093b58333933d8c517e33d32dd414d64fdf68a911cfd672
BLAKE2b-256 checksum
How to use checksums
85d20f55731f0453dc8e052f7d5bfa302bcb33f0af02069da38ebdbc95e22517
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.0.9 CPython/3.8.3 Linux/5.3.0-1032-azure

Release files / gustavgrad-0.2.0-py3-none-any.whl

Download URL gustavgrad-0.2.0-py3-none-any.whl
Size 9.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bac46c306601c0cb5de5f09c8a10e3437fce41f500fd65746f2937b31b63706e
BLAKE2b-256 checksum
How to use checksums
37179c1780c37ae6c5607662b44542d4e598e4c5cb42f1eb9e8c5f1de99c4c21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.0.9 CPython/3.8.3 Linux/5.3.0-1032-azure

Security for gustavgrad 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page