autodiff is an Automatic Differentiation library created purely using numpy and python which allows users to evaluate the derivatives of multivariable functions.
This is an improvement to the automatic differentiation system featured in bren, which tackles two main limitations of the library. First, it allows for higher derivatives to be calculated with the previous version being limited to only the first. Next, this new version introduces reusability of already traversed graph structures.
Install
To install the latest version of autodiff, run the following command in your terminal.
pip install python-autodiff
How it works
autodiff works by using numpy containers to automatically generate computation graphs whenever operations are performed on a Tensor object. Initially, when a tensor is instantiated, it stores a Variable node. Whenever an operation is performed on a tensor, it will create another tensor that stores an Operation node. This operation node will contain an attribute that contains its inputs, therefore creating a connection between the nodes. Thus, doing this with many operation nodes will create a computation graph.
The Graph object will be responsible for traversing these computation graphs and generating gradient functions. The gradient method will be responsible for evaluating the derivatives, taking in a tensor as a dy and a list of tensors as dx and outputting a list of evaluated derivatives with respect to each of the tensors in dx.
When the gradient function is called, it first checks if a gradient function for the pair of dy and dx tensor exists; if it does, it simply outputs the value of that function. Otherwise, it will perform a depth first search of the computation graph of dy and at each operation node it will determine the correct gradient function to use and apply it to the current set of inputs. This result will be multiplied with the result from the previous operation node. Doing this would implement the process of multiplying many derivatives, which is done in the chain rule.
This chain of operations performed, operation node by operation node will be performed on a Tensor object (until a Variable node is reached), which will create a computation graph of its own. This, in essence, would be the computation graph of the derivative of dy with respect to the dx referenced by the Variable node.
This new computation graph can now be used to generate a reusable function for this specific pair of dy and dx, making this process more reusable. If the same derivative needed to be calculated again, then dy's graph structure would not need to be traversed again. As a result, this would make the process of finding derivatives reusable and faster.
Additionally, the computation graph of the derivative could itself be traversed again giving the derivative of a derivative, thus the second derivative. This could be applied repeatedly to give many more higher order derivatives.
Your first autodiff program
import autodiff as ad
# Create a Tensor object
a = ad.Tensor([1, 2, 3, 4])
# Perform an operation on it
b = a ** 2
# Create a graph object and add a as a variable
graph = ad.Graph()
graph.add_variables(a)
# find the gradient of b with respect to a
gradients = graph.gradient(b, [a])
print(gradients) # [array([2., 4., 6., 8.])]
Example Projects
Here are two example projects that showcase this library in use.
Higher derivative graph plotter
This project displays the ability of this library to calculate higher order derivatives and showcases in a visual manner through plotting their graphs.
Step 1: Create a tensor that contains a range of values and perform an operation on it.
import autodiff as ad
import numpy as np
import matplotlib.pyplot as plt
a = ad.Tensor(np.arange(-2, 2, 0.01), dtype="float64")
d = np.sin((a ** 2) * np.tanh(a))
Step 2: Instanciate a graph object and add a as a variable.
g = ad.Graph()
g.add_variables(a)
Step 3: Plot the function and its derivatives and display the graphs.
# the _i attribute allows you to access the value of the tensor
plt.plot(d._i)
labels = ["function"]
# loop for as many derivatives as you wish to take
for i in range(3):
# plot the (i+1)th gradient of b
plt.plot(g.gradient(d, [a], i + 1)[0])
labels.append(f"derivative {i + 1}")
# display the graph
plt.legend(labels)
plt.show()
Neural network for XOR dataset
This project shows how this library can be used to create a simple neural network to be fit on the XOR data. As we can see, only the forward function has been explicitly written, the backpropagation is handled by the Graph object.
Although simple, this project acts as an example of how this library can be used to create neural networks. It also shows how easy it is to scale this model (by changing the dataset or adding more layers for example) without having to change much of the code.
Step 1: Initialise the Graph object, Dataset and the model parameters.
import autodiff as ad
import numpy as np
g = ad.Graph()
# Dataset
X = ad.Tensor([[[0], [0]], [[0], [1]], [[1], [0]], [[1], [1]]], dtype="float64")
Y = ad.Tensor([[0], [1], [1], [0]], dtype="float64")
# Model parameters
W1 = ad.Tensor(np.random.rand(160, 2), dtype="float64")
B1 = ad.Tensor(np.random.rand(160, 1), dtype="float64")
W2 = ad.Tensor(np.random.rand(1, 160), dtype="float64")
B2 = ad.Tensor(np.random.rand(1, 1), dtype="float64")
Step 2: Define the forward propagation function.
def forward(x):
Z1 = np.matmul(W1, x) + B1
A1 = np.tanh(Z1)
Z2 = np.matmul(W2, A1) + B2
return Z2
Step 3: Train the model for 1000 epochs. This will involve performing forward propagation, calculating the error and performing backward propagation. Backward propation will be performed using the Graph object. Here, updating the values of x and y at each iteration is very important as it allows different values to be inputting into the gradient functions.
for i in range(1000):
for x, y in zip(X, Y):
parameters = [W1, B1, W2, B2]
# update the values of x and y
g.add_variables(*parameters, x, y)
# perform forward propagation
Z2 = forward(x)
# calculate and display the error
E = (y - Z2) ** 2
print(E)
# Find the gradients of E with respect to all the parameters
for param, gradient in zip(parameters, g.gradient(E, parameters)):
# update the parameter values
param.assign(param._i - (0.01 * gradient))
Step 4: As the model is now trained, lets run the forward function on X to view the model's predictions.
print("predictions")
# for a larger dataset, the test set would be used here
for x in X:
print(forward(x))
Metadata
Release files for python-autodiff 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| python_autodiff-0.1.1.tar.gz | 11.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| python_autodiff-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.9 kB
Release files / python_autodiff-0.1.1.tar.gz
| Download URL | python_autodiff-0.1.1.tar.gz |
|---|---|
| Size | 11.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
36d8d224bf4856028e0378a2c69f5a4ac40c6eceff981ae11aa09fb144e112f8
|
|
BLAKE2b-256 checksum How to use checksums |
a6a88401e193c557dc46ccc2fd70f2402e6cacebf51bc143c35981fdec23a062
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.4
|
Release files / python_autodiff-0.1.1-py3-none-any.whl
| Download URL | python_autodiff-0.1.1-py3-none-any.whl |
|---|---|
| Size | 11.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
10a5c338fa01da86e2704ee2f7ad6b657ad1c44a8f61cb7e08f96f77cd997a41
|
|
BLAKE2b-256 checksum How to use checksums |
5aec2031053375d6543560710361bc76cf776ae2f02801fdf39dffa8b3e000c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.4
|
Security for python-autodiff 0.1.1
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.