From 0bd33d1f92147c93849d10a97c29e9b66ac64d48 Mon Sep 17 00:00:00 2001
From: bclarkson-code <57139598+bclarkson-code@users.noreply.github.com>
Date: Sat, 10 Feb 2024 12:47:01 +0000
Subject: [PATCH 1/3] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index b5aa76a..cbdef6a 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Here are some things you can do with Tricycle:
Here are some things you can't do with Tricycle (yet):
- Do anything at the speed of pytorch
-- Use any built in layers, optimisers, regularisation techniques etc
+- Perform more advanced operations like Attention
- Use a GPU
If you want to do these things, you should check out [pytorch](https://pytorch.org/)
From 1d808bd3036b9bd31b34e8c4a7888273ca2ec21d Mon Sep 17 00:00:00 2001
From: bclarkson-code <57139598+bclarkson-code@users.noreply.github.com>
Date: Sat, 10 Feb 2024 13:00:31 +0000
Subject: [PATCH 2/3] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index cbdef6a..9ec0dad 100644
--- a/README.md
+++ b/README.md
@@ -40,3 +40,5 @@ Tricycle is tested using [pytest](https://docs.pytest.org/en/latest/)
poetry run pytest
```
+## Contact
+To get in touch, you can send an email to: [bclarkson-code@proton.me](mailto:bclarkson-code@proton.me)
From fc82237e3a5fd488aa3853be889f67d47c159209 Mon Sep 17 00:00:00 2001
From: bclarkson-code <57139598+bclarkson-code@users.noreply.github.com>
Date: Sat, 10 Feb 2024 14:29:48 +0000
Subject: [PATCH 3/3] Blog post 1 (#10)
* Started workm on blog post
* Finished first 2 chapters
* Added more to blog
* Completed graphical derivatives
* Finished first draft of content
* Updated blog post
* Added nicer pictures
* Updated images in explanation
* Updted post
* Finished all but the intro
* Added tech tree
* Added intorduction
---
blog_post_1.ipynb | 1597 ++++++++++++++++++++++++++++
images/square_error.png | Bin 0 -> 122012 bytes
images/tech_tree_post_1.png | Bin 0 -> 339174 bytes
images/variable_and_box.png | Bin 0 -> 38069 bytes
images/y_eq_mx_plus_p_deriv.png | Bin 0 -> 154834 bytes
images/y_eq_mx_plus_p_labelled.png | Bin 0 -> 53802 bytes
images/z_eq_mx.png | Bin 0 -> 57343 bytes
images/z_eq_xx_plus_xc.png | Bin 0 -> 85621 bytes
8 files changed, 1597 insertions(+)
create mode 100644 blog_post_1.ipynb
create mode 100644 images/square_error.png
create mode 100644 images/tech_tree_post_1.png
create mode 100644 images/variable_and_box.png
create mode 100644 images/y_eq_mx_plus_p_deriv.png
create mode 100644 images/y_eq_mx_plus_p_labelled.png
create mode 100644 images/z_eq_mx.png
create mode 100644 images/z_eq_xx_plus_xc.png
diff --git a/blog_post_1.ipynb b/blog_post_1.ipynb
new file mode 100644
index 0000000..20f5049
--- /dev/null
+++ b/blog_post_1.ipynb
@@ -0,0 +1,1597 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "b9938fb2-6080-4c89-bcac-bf815d78f1bc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import Any, Optional\n",
+ "\n",
+ "import networkx as nx"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f0009d17-27f8-48f8-a099-fbec7c5157e5",
+ "metadata": {},
+ "source": [
+ "# Building an LLM from scratch\n",
+ "## Automatic Differentiation"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "50d2a732-5cc2-42c8-9426-c0bf135e35c4",
+ "metadata": {},
+ "source": [
+ "Approach 1: I have some holes that need filling\n",
+ "When trying to follow the latest research, I sometimes get the impression that I'm missing something, knowing the name of a technique and vaguely how it works but not *actually* understanding it (Feynmann, 1995). It feels like there is an insurmountable pile of research, with each development depending on all of the previous developments. Of course, it doesn't help that information is shared through a combination of dense scientific papers and deluge of posts on Twitter/X which makes finding the signal amongst the noise very challenging.\n",
+ "\n",
+ "When I've been confused about something technical in the past, one of the things that has helped me is getting my hands dirty and doing it myself. With maths at university, this was trying to teach my classmates. Here, I can't think of a better method that building a modern language model myself.\n",
+ "\n",
+ "Nowadays, it is \"relatively\" simple to copy-paste some code from huggingface, feed in some data and get a language model training. The whole process can be done in less than an hour and is a fantastic example of giving the public access to the latest research. Unfortunately, this ease of use is only possible by hiding a lot of important details which leaves people (me) with the feeling that there are gaps in their knowledge.\n",
+ "\n",
+ "Approach 2: Deep learning is done top down. I want to do bottom up\n",
+ "One of the best things about the AI community is how open and accessible (with some notable exceptions, I'm looking at you \"Open\"AI) modern developments are. With a bit of python knowledge, you can go to huggingface, copy-paste some code, feed in some data and start training a state of the art language model. The whole process can be done in less than an hour and is a fantastic example of giving the public access to the latest research. Unfortunately, this ease of use is only possible by hiding a lot of important details which leaves people (me) with the feeling that there might be gaps in their knowledge.\n",
+ "\n",
+ "I like to imagine deep learning research as a tree, with each new technique sprouting from the branches of earlier developments. To understand something new, you can read through the paper and every time you come across something you don't understand, you can follow the tree back a few branches to get the necessary background and then continue with the paper.\n",
+ "\n",
+ "I think that a lot of people who get into the field learn the basic theory (e.g gradient descent) and then jump to the cutting edge, filling in any gaps by following the branches backwards as they go. This is a great way to do useful things as fast as possible but also requires a lot of work to fill in the gaps every time new research comes out. Given the pace of development, it is easy to feel overwhelmed with everything that is going on.\n",
+ "\n",
+ "To fix this issue, I want to start from the bottom of the tree and work up, building a modern language model with all the bells and whistles completely from scratch. Borrowing (shamelessly stealing) from computer games, I've built a tech tree of everything that I think I'll need to implement to get a fully functional language model. If you think anything is missing, please let me [know](mailto:bclarkson-code@proton.me): \n",
+ "\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "Before we can move onto building modern features like Rotary Positional Encodings, we first need to figure out how to differentiate with a computer. The backpropagation algorithm that underpins the entire field of Deep Learning requires the ability to differentiate the outputs of neural networks with respect to (wrt) their inputs. In this post, we'll go from nothing to a (admittedly very limited) automatic differentiation library that can differentiate arbitrary functions of scalar values.\n",
+ "\n",
+ "This one algorithm will form the core of out deep learning library that, eventually, will include everything we need to train a language model. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "477924ef-6dfc-43c2-881a-2f69167e1920",
+ "metadata": {},
+ "source": [
+ "## Creating a tensor \n",
+ "We can't do any differentiation if we don't have any numbers to differentiate. We'll want to add some extra functionality that is in standard `float` types so we'll need to create out own. Lets call it a `Tensor`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "fdfa0e00-4a53-4e52-9d02-99f93e9e303e",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Tensor(5)"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "class Tensor:\n",
+ " \"\"\"\n",
+ " Just a number (for now)\n",
+ " \"\"\"\n",
+ "\n",
+ " value: float\n",
+ "\n",
+ " def __init__(self, value: float):\n",
+ " self.value = value\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " \"\"\"\n",
+ " Create a printable string representation of this\n",
+ " object\n",
+ "\n",
+ " This function gets called when you pass a Tensor to print\n",
+ "\n",
+ " Without this function:\n",
+ " >>> print(Tensor(5))\n",
+ " <__main__.Tensor at 0x104fd1950>\n",
+ "\n",
+ " With this function:\n",
+ " >>> print(Tensor(5))\n",
+ " Tensor(5)\n",
+ " \"\"\"\n",
+ " return f\"Tensor({self.value})\"\n",
+ "\n",
+ "\n",
+ "# try it out\n",
+ "Tensor(5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7aa4ebb0-e1a0-4e1f-a5ed-5fa0b0ce62ba",
+ "metadata": {},
+ "source": [
+ "Next we'll need some simple operations we want to perform: addition, subtraction and multiplication."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "c01dc061-c61f-4b3d-b70a-06129efd9fae",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def _add(a: Tensor, b: Tensor):\n",
+ " \"\"\"\n",
+ " Add two tensors\n",
+ " \"\"\"\n",
+ " return Tensor(a.value + b.value)\n",
+ "\n",
+ "\n",
+ "def _sub(a: Tensor, b: Tensor):\n",
+ " \"\"\"\n",
+ " Subtract tensor b from tensor a\n",
+ " \"\"\"\n",
+ " return Tensor(a.value - b.value)\n",
+ "\n",
+ "\n",
+ "def _mul(a: Tensor, b: Tensor):\n",
+ " \"\"\"\n",
+ " Multiply two tensors\n",
+ " \"\"\"\n",
+ " return Tensor(a.value * b.value)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fade0f0f-988d-4f4f-a495-8f414f2381d9",
+ "metadata": {},
+ "source": [
+ "We can use use our operations as follows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "4007641b-ec4a-4cdb-9160-427bf0ec2b6f",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "✅ - Want: 7, Got: 7\n",
+ "✅ - Want: -1, Got: -1\n",
+ "✅ - Want: 12, Got: 12\n"
+ ]
+ }
+ ],
+ "source": [
+ "def test(got: Any, want: Any):\n",
+ " \"\"\"\n",
+ " Check that two objects are equal to each other\n",
+ " \"\"\"\n",
+ " indicator = \"✅\" if want == got else \"❌\"\n",
+ " print(f\"{indicator} - Want: {want}, Got: {got}\")\n",
+ "\n",
+ "\n",
+ "a = Tensor(3)\n",
+ "b = Tensor(4)\n",
+ "\n",
+ "\n",
+ "test(_add(a, b).value, 7)\n",
+ "test(_sub(a, b).value, -1)\n",
+ "test(_mul(a, b).value, 12)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "605f5529-fc6f-43a2-b299-eb25cbc0523f",
+ "metadata": {},
+ "source": [
+ "## Scalar derivatives\n",
+ "Diving straight into differentiating matrices sounds too hard so lets start with something simpler: differentiating scalars. The simplest scalar derivative I can think of is the derivative of a tensor with respect to (wrt) itself:\n",
+ "$$\\frac{dx}{dx} = 1$$"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "db267512-5932-411f-8204-b87d688c79d1",
+ "metadata": {},
+ "source": [
+ "A more interesting case is the derivative of two tensors added together (note we are using partial derivatives because our function has multiple inputs):\n",
+ "$$f(x, y) = x + y$$\n",
+ "$$\\frac{\\partial f}{\\partial x} = 1$$\n",
+ "$$\\frac{\\partial f}{\\partial y} = 1$$\n",
+ "\n",
+ "We can do a similar thing for multiplication and subtraction\n",
+ "\n",
+ "|$f(x, y)$|$\\frac{\\partial f}{\\partial x}$|$\\frac{\\partial f}{\\partial y}$|\n",
+ "|-|-|-|\n",
+ "|$x + y$|$1$|$1$|\n",
+ "|$x - y$|$1$|$-1$|\n",
+ "|$x \\times y$|$y$|$x$|"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b3ed0850-456b-4127-b557-d7cf39524e47",
+ "metadata": {},
+ "source": [
+ "Now that we've worked out these derivatives mathematically, the next step is to convert them into code. In the table above, when we make a tensor by combining two tensors with an operation, the derivative only ever depends on the inputs and the operation. There is no \"hidden state\".\n",
+ "\n",
+ "This means that the only information we need to store is the inputs to an operation and a function to calculate the derivative wrt each inputs. With this, we should be able to differentiate any binary function wrt its inputs. A good place to store this information is in the tensor that is produced by the operation.\n",
+ "\n",
+ "We'll add some new attributes to our `Tensor`: `args` `local_derivatives`. If the tensor is the output of an operation, then `args` will store the arguments to the operation and `local_derivatives` will store the derivatives wrt each input. We're calling it `local_derivatives` to avoid confusion when we start nesting functions.\n",
+ "\n",
+ "Once we've calculated the derivative (from our `args` and `local_derivatives`) we'll need to store it. It turns out that the neatest place to put this is in tensor that the output is being differentiated wrt. We'll call this `derivative`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "36f5546b-33a1-4808-be0d-77a898938066",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Tensor:\n",
+ " \"\"\"\n",
+ " A number that can be differentiated\n",
+ " \"\"\"\n",
+ "\n",
+ " # If the tensor was made by an operation, the operation arguments\n",
+ " # are stored in args\n",
+ " args: tuple[\"Tensor\"] = ()\n",
+ " # If the tensor was made by an operation, the derivatives wrt\n",
+ " # operation inputs are stored in derivatives\n",
+ " local_derivatives: tuple[\"Tensor\"] = ()\n",
+ " # The derivative we have calculated\n",
+ " derivative: Optional[\"Tensor\"] = None\n",
+ "\n",
+ " def __init__(self, value: float):\n",
+ " self.value = value\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " \"\"\"\n",
+ " Create a printable string representation of this\n",
+ " object\n",
+ "\n",
+ " This function gets called when you pass a Tensor to print\n",
+ "\n",
+ " Without this function:\n",
+ " >>> print(Tensor(5))\n",
+ " <__main__.Tensor at 0x104fd1950>\n",
+ "\n",
+ " With this function:\n",
+ " >>> print(Tensor(5))\n",
+ " Tensor(5)\n",
+ " \"\"\"\n",
+ " return f\"Tensor({self.value})\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "befb4571-7c6f-4e07-8731-d511dff0667e",
+ "metadata": {},
+ "source": [
+ "For example, if we have "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "ede7d7e3-b1c1-4f41-9657-316cbe0eee51",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "a = Tensor(3)\n",
+ "b = Tensor(4)\n",
+ "\n",
+ "output = _mul(a, b)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1df49bf3-7c4f-414b-8582-d65c68ca6563",
+ "metadata": {},
+ "source": [
+ "Then `output.args` and `output.local_derivatives` should be equal to should be set to:\n",
+ "\n",
+ "```python\n",
+ "output.args == (Tensor(3), Tensor(4))\n",
+ "output.derivatives == (\n",
+ " b, # derivative of output wrt a is b\n",
+ " a, # derivative of output wrt b is a\n",
+ ")\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fd49ca9b-e27e-4400-9542-fa1912fd63fa",
+ "metadata": {},
+ "source": [
+ "Once we have actually computed the derivatives, then the derivative of `output` wrt `a` will be stored in `a.derivative` and should be equal to `b` (which is 4 in this case). \n",
+ "\n",
+ "We know that we've done everything right once these tests pass:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "8d86f1c3-9a65-4468-9089-20a6bf0691c7",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "❌ - Want: (Tensor(3), Tensor(4)), Got: ()\n",
+ "❌ - Want: (Tensor(4), Tensor(3)), Got: ()\n",
+ "❌ - Want: Tensor(4), Got: None\n",
+ "❌ - Want: Tensor(3), Got: None\n"
+ ]
+ }
+ ],
+ "source": [
+ "a = Tensor(3)\n",
+ "b = Tensor(4)\n",
+ "\n",
+ "output = _mul(a, b)\n",
+ "\n",
+ "# TODO: differentiate here\n",
+ "\n",
+ "test(got=output.args, want=(a, b))\n",
+ "test(got=output.local_derivatives, want=(b, a))\n",
+ "test(a.derivative, b)\n",
+ "test(b.derivative, a)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2ed01e92-17b5-4aba-8d32-0be2983466e1",
+ "metadata": {},
+ "source": [
+ "First, lets add a function to our `Tensor` that will actually calculate the derivatives for each of the function arguments. Pytorch calls this function `backward` so we'll do the same."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "3f0c4e1e-66e2-43e1-ae62-f029347369c8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Tensor:\n",
+ " \"\"\"\n",
+ " A number that can be differentiated\n",
+ " \"\"\"\n",
+ "\n",
+ " # If the tensor was made by an operation, the operation arguments\n",
+ " # are stored in args\n",
+ " args: tuple[\"Tensor\"] = ()\n",
+ " # If the tensor was made by an operation, the derivatives wrt\n",
+ " # operation inputs are stored in\n",
+ " local_derivatives: tuple[\"Tensor\"] = ()\n",
+ " # The derivative we have calculated\n",
+ " derivative: Optional[\"Tensor\"] = None\n",
+ "\n",
+ " def __init__(self, value: float):\n",
+ " self.value = value\n",
+ "\n",
+ " def backward(self):\n",
+ " if self.args is None or self.local_derivatives is None:\n",
+ " raise ValueError(\n",
+ " \"Cannot differentiate a Tensor that is not a function of other Tensors\"\n",
+ " )\n",
+ "\n",
+ " for arg, derivative in zip(self.args, self.local_derivatives):\n",
+ " arg.derivative = derivative\n",
+ "\n",
+ " def __repr__(self) -> str:\n",
+ " \"\"\"\n",
+ " Create a printable string representation of this\n",
+ " object\n",
+ "\n",
+ " This function gets called when you pass a Tensor to print\n",
+ "\n",
+ " Without this function:\n",
+ " >>> print(Tensor(5))\n",
+ " <__main__.Tensor at 0x104fd1950>\n",
+ "\n",
+ " With this function:\n",
+ " >>> print(Tensor(5))\n",
+ " Tensor(5)\n",
+ " \"\"\"\n",
+ " return f\"Tensor({self.value})\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "92d07a1a-eae0-40b4-84d6-4e57b00f16f1",
+ "metadata": {},
+ "source": [
+ "This only works if we also store the arguments and derivatives in the output tensors of operations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "99f6cd69-55a0-485d-a169-bf7f32e16e21",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def _add(a: Tensor, b: Tensor):\n",
+ " \"\"\"\n",
+ " Add two tensors\n",
+ " \"\"\"\n",
+ " result = Tensor(a.value + b.value)\n",
+ " result.local_derivatives = (Tensor(1), Tensor(1))\n",
+ " result.args = (a, b)\n",
+ " return result\n",
+ "\n",
+ "\n",
+ "def _sub(a: Tensor, b: Tensor):\n",
+ " \"\"\"\n",
+ " Subtract tensor b from a\n",
+ " \"\"\"\n",
+ " result = Tensor(a.value - b.value)\n",
+ " result.local_derivatives = (Tensor(1), Tensor(-1))\n",
+ " result.args = (a, b)\n",
+ " return result\n",
+ "\n",
+ "\n",
+ "def _mul(a: Tensor, b: Tensor):\n",
+ " \"\"\"\n",
+ " Multiply two tensors\n",
+ " \"\"\"\n",
+ " result = Tensor(a.value * b.value)\n",
+ " result.local_derivatives = (b, a)\n",
+ " result.args = (a, b)\n",
+ " return result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bc72894d-3d9a-4dfa-aca3-b5b97e57c729",
+ "metadata": {},
+ "source": [
+ "Lets re-run our tests and see if it works"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "77657e72-49ea-49a7-a8df-7edc6ad0278a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "✅ - Want: (Tensor(3), Tensor(4)), Got: (Tensor(3), Tensor(4))\n",
+ "✅ - Want: (Tensor(4), Tensor(3)), Got: (Tensor(4), Tensor(3))\n",
+ "✅ - Want: Tensor(4), Got: Tensor(4)\n",
+ "✅ - Want: Tensor(3), Got: Tensor(3)\n"
+ ]
+ }
+ ],
+ "source": [
+ "a = Tensor(3)\n",
+ "b = Tensor(4)\n",
+ "\n",
+ "output = _mul(a, b)\n",
+ "\n",
+ "output.backward()\n",
+ "\n",
+ "test(got=output.args, want=(a, b))\n",
+ "test(got=output.local_derivatives, want=(b, a))\n",
+ "test(a.derivative, b)\n",
+ "test(b.derivative, a)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fddf7959-2f69-497b-9a6c-0c31cb375e5c",
+ "metadata": {},
+ "source": [
+ "So far so good, lets try nesting operations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "0ea3288f-24db-4fa8-8a11-d9ea6dec2ffa",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "❌ - Want: Tensor(3), Got: None\n"
+ ]
+ }
+ ],
+ "source": [
+ "a = Tensor(3)\n",
+ "b = Tensor(4)\n",
+ "\n",
+ "output_1 = _mul(a, b)\n",
+ "# z = a + (a * b)\n",
+ "output_2 = _add(a, output_1)\n",
+ "\n",
+ "output_2.backward()\n",
+ "\n",
+ "# should get\n",
+ "# dz/db = 0 + a = a\n",
+ "test(b.derivative, a)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c09a6438-a069-4616-99b4-e58b7b5ac183",
+ "metadata": {},
+ "source": [
+ "Something has gone wrong. \n",
+ "\n",
+ "We should have got `a` as the derivative for `b` but we got `0` instead. Looking through the `.backward()` function, the issue is pretty clear:\n",
+ "we haven't thought about nested functions. To get this example working, we'll need to figure out how to calculate derivatives through multiple functions instead of just one."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "04b75711-af3e-446e-b2be-05fd55c00927",
+ "metadata": {},
+ "source": [
+ "## Chaining Functions Together"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7377a549-58dd-4779-847e-461ff1300e90",
+ "metadata": {},
+ "source": [
+ "To calculate derivates of nested functions, we can use a rule from calculus: The Chain Rule.\n",
+ "\n",
+ "For a variable $z$ generated by nested functions $f$ and $g$ such that\n",
+ "$$z = f(g(x))$$\n",
+ "\n",
+ "Then the derivative of $z$ wrt $x$ is:\n",
+ "$$\\frac{\\partial z}{\\partial x} = \\frac{\\partial f(u)}{\\partial u} \\frac{\\partial g(x)}{\\partial x}$$\n",
+ "\n",
+ "Here, $u$ is a dummy variable. $\\frac{\\partial f(u)}{\\partial u}$ means the derivative of $f$ wrt its input.\n",
+ "\n",
+ "For example, if \n",
+ "\n",
+ "$$f(x) = g(x)^2$$\n",
+ "Then we can define $u=g(x)$ and rewrite $f$ in terms of u \n",
+ "$$f(u) = u^2 \\implies \\frac{\\partial f(u)}{\\partial u} = 2u = 2 g(x)$$\n",
+ "\n",
+ "### Multiple Variables\n",
+ "The chain rule works as you might expect for functions of multiple variables. When differentiating wrt a variable, we can treat the other variables as constant and differentiate as normal\n",
+ "$$z = f(g(x), h(y))$$\n",
+ "\n",
+ "$$\\frac{\\partial z}{\\partial x} = \\frac{\\partial f(u)}{\\partial u} \\frac{\\partial g(x)}{\\partial x}$$\n",
+ "$$\\frac{\\partial z}{\\partial y} = \\frac{\\partial f(u)}{\\partial u} \\frac{\\partial h(y)}{\\partial y}$$\n",
+ "\n",
+ "If we have different functions that take the same input, we differentiate each of them individually and then add them together\n",
+ "\n",
+ "$$z = f(g(x), h(x))$$\n",
+ "\n",
+ "We get\n",
+ "$$\\frac{\\partial z}{\\partial x} = \\frac{\\partial f(u)}{\\partial u}\\frac{\\partial g(x)}{\\partial x} + \\frac{\\partial f(u)}{\\partial u}\\frac{\\partial h(x)}{\\partial x}$$\n",
+ "\n",
+ "### More than 2 functions\n",
+ "If we chain 3 functions together, we still just multiply the derivatives for each function together:\n",
+ "\n",
+ "$$\\frac{\\partial z}{\\partial x} = \\frac{\\partial f(u)}{\\partial u} \\frac{\\partial g(x)}{\\partial x} = \\frac{\\partial f(u)}{\\partial u} \\frac{\\partial g(u)}{\\partial u}\\frac{\\partial h(x)}{\\partial x}$$\n",
+ "\n",
+ "And this generalises to any amount of nesting\n",
+ "\n",
+ "$$z = f_1(f_2(....f_{n-1}(f_n(x))...)) \\implies \\frac{\\partial z}{\\partial x} = \\frac{\\partial f_1(u)}{\\partial u}\\frac{\\partial f_2(u)}{\\partial u}...\\frac{\\partial f_{n-1}(u)}{\\partial u}\\frac{\\partial f_{n}(x)}{\\partial x}$$"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "65f9b9d4-0d9a-47bb-94d3-64c2dfe648eb",
+ "metadata": {},
+ "source": [
+ "### A picture is worth a thousand equations\n",
+ "As you probably noticed, the maths is starting to get quite dense. When we start working with neural networks, we can easily get 100s or 1000s of functions deep so to get a handle on things, we'll need a different strategy. Helpfully, there is one: turning it into a graph.\n",
+ "\n",
+ "We can start with some rules:\n",
+ "\n",
+ "> Variables are represented with circles and operations are represented with boxes\n",
+ "\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "\n",
+ "> Inputs to an operation are represented with arrows that point to the operation box. Outputs point away.\n",
+ "\n",
+ "For example, here is the diagram for $z = mx$\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "And thats it! All of the equations we'll be working with can be represented graphically using these simple rules. To try it out, let's draw the diagram for a more complex formula:\n",
+ "\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "This is an example of a structure called a graph (also called a network). A lot of problem in computer science get much easier if you can represent them with a graph and this is no exception."
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "03129971-5cdb-454e-829b-a3d5e017207b",
+ "metadata": {},
+ "source": [
+ "The real power of these diagrams is that they can also help us with our derivatives. Take \n",
+ "$$y = mx + p = \\texttt{add}(p, \\texttt{mul}(m ,x)).$$\n",
+ "\n",
+ "From before, we can find its derivatives by differentiating each operation wrt its inputs and multiplying the results together. In this case, we get:\n",
+ "$$\\frac{\\partial y}{\\partial p} = \\frac{\\partial \\texttt{add}(u_1, u_2)}{\\partial u_1} = 1$$\n",
+ "$$\\frac{\\partial y}{\\partial m} = \\frac{\\partial \\texttt{add}(u_1, u_2)}{\\partial u_2}\\frac{\\partial \\texttt{mul}(u_1, u_2)}{\\partial u_2} = 1 \\times x = x$$\n",
+ "$$\\frac{\\partial y}{\\partial x} = \\frac{\\partial \\texttt{add}(u_1, u_2)}{\\partial u_2}\\frac{\\partial \\texttt{mul}(u_1, u_2)}{\\partial u_1} = 1 \\times m = m$$\n",
+ "\n",
+ "We can also graph it like this:\n",
+ "\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "If you imagine walking from $y$ to each of the inputs, you might notice a similarity between the edges you pass through and the equations above. If you walk from $y$ to $x$, you'll pass through `a->c->d`. Similarly, if you walk from $y$ to $m$, you'll pass through `a->d->e`. Notice that both paths go through `c`, the edge coming out of `add` that corresponds to the input $u_2$. Also, both equations include the term $\\frac{\\partial \\texttt{add}(u_1, u_2)}{\\partial u_2}$. \n",
+ "\n",
+ "If I rename the edges as follows:\n",
+ "\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "We can see that going from $y$ to $x$, we pass through $1$, $\\frac{\\partial \\texttt{add}(u_1, u_2)}{\\partial u_2}$ and $\\frac{\\partial \\texttt{mul}(u_1, u_2)}{\\partial u_1}$. If we multiply these together, we get exactly $\\frac{\\partial \\texttt{add}(u_1, u_2)}{\\partial u_2}\\frac{\\partial \\texttt{mul}(u_1, u_2)}{\\partial u_1} = \\frac{\\partial y}{\\partial x}$!\n",
+ "\n",
+ "It turns out that this rule works in general:\n",
+ "\n",
+ "> If we have some operation $\\texttt{op}(u_1, u_2, ..., u_n)$, we should label the edge corresponding to input $u_i$ with $\\frac{\\partial \\texttt{op}(u_1, u_2, ..., u_n)}{\\partial u_i}$\n",
+ "\n",
+ "Then, if we want to find the derivative of the output node wrt any of the inputs,\n",
+ "\n",
+ "> The derivative of an output variable wrt one of the input variables can be found by traversing the graph from the output to the input and multiplying together the derivatives for every edge on the path\n",
+ "\n",
+ "To cover every edge case, there are some extra details\n",
+ "\n",
+ "> If a graph contains multiple paths from the output to an input, then the derivative is the sum of the products for each path\n",
+ "\n",
+ "This comes from the case we saw earlier where when we have different functions that have the same input we have to add their derivative chains together.\n",
+ "\n",
+ "> If an edge is not the input to any function, its derivative is 1\n",
+ "\n",
+ "This covers the edge that leads from the final operation to the output. You can think of the edge having the derivative $\\frac{\\partial y}{\\partial y}=1$\n",
+ "\n",
+ "And thats it! Lets try it out with $z = (x + c)x$:\n",
+ "\n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "\n",
+ "Here, instead of writing the formulae for each derivative, I have gone ahead and calculated their actual values. Instead of just figuring out the formulae for a derivative, we want to calculate its value when we plug in our input parameters. \n",
+ "\n",
+ "All that remains is to multiply the local derivatives together along each path. We'll call the product of derivatives along a single path a chain (after the chain rule)\n",
+ "\n",
+ "We can get from $z$ to $x$ via the green path and the red path. Following these paths, we get:\n",
+ "$$\\text{red path} = 1 \\times (x + c) = x + c$$\n",
+ "Along the green path we get:\n",
+ "$$\\text{green path} = 1 \\times x \\times 1 = x$$\n",
+ "\n",
+ "Adding these together, we get $(x+c) + x = 2x + c$\n",
+ "\n",
+ "If we work out the derivative algebraically:\n",
+ "\n",
+ "$$\\frac{\\partial z}{\\partial x} = \\frac{\\partial}{\\partial x}((x+c)x) = \\frac{\\partial}{\\partial x}(x^2 + cx) = \\frac{\\partial x^2}{\\partial x} + c\\frac{\\partial x}{\\partial x} = 2x + c$$\n",
+ "\n",
+ "We can see that it seems to work! (Calculating $\\frac{\\partial z}{\\partial c}$ is left as an exercise for the reader) \n",
+ "\n",
+ "To summarise, we have invented the following algorithm for calculating of a variable wrt its inputs:\n",
+ "\n",
+ "1. Turn the equation into a graph\n",
+ "2. Label each edge with the appropriate derivative\n",
+ "3. Find every path from the output to the input variable you care about\n",
+ "4. Follow each path and multiply the derivatives you pass through\n",
+ "5. Add together the results for each path\n",
+ "\n",
+ "We have an algorithm in pictures and words, lets turn it into code."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "47989351-92e5-491c-a58a-b109c1d84f92",
+ "metadata": {},
+ "source": [
+ "### The Algorithm™\n",
+ "\n",
+ "Suprisingly, we have actually already converted our functions into graphs. If you recall, when we generate a tensor from an operation, we record the inputs to the operation in the output tensor (in `.args`). We also stored the functions to calculate derivatives for each of the inputs in `.local_derivatives` which means that we know both the destination and derivative for every edge that points to a given node. This means that we've already completed steps 1 and 2.\n",
+ "\n",
+ "The next challenge is to find all paths from the tensor we want to differentiate to the input tensors that created it. Because none of our operations are self referential (outputs are never fed back in as inputs), and all of our edges have a direction, our graph of operations is a directed acyclic graph or DAG. The property of the graph having no cycles means that we can find all paths to every parameter pretty easily with a Breadth First Search (or Depth First Search but BFS makes some optimisations easier as we'll see in part 2).\n",
+ "\n",
+ "To try it out, lets recreate that giant graph we made earlier. We can do this by first calculating $L$ from the inputs"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "05bfab9c-fea6-40cb-bcd2-0c60a7f1950c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "y = Tensor(1)\n",
+ "m = Tensor(2)\n",
+ "x = Tensor(3)\n",
+ "c = Tensor(4)\n",
+ "\n",
+ "# L = (y - (mx + c))^2\n",
+ "left = _sub(y, _add(_mul(m, x), c))\n",
+ "right = _sub(y, _add(_mul(m, x), c))\n",
+ "\n",
+ "L = _mul(left, right)\n",
+ "\n",
+ "# Attaching names to tensors will make our\n",
+ "# diagram look nicer\n",
+ "y.name = \"y\"\n",
+ "m.name = \"m\"\n",
+ "x.name = \"x\"\n",
+ "c.name = \"c\"\n",
+ "L.name = \"L\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "795a7000-73ef-45dc-b2e0-b76da641ae4d",
+ "metadata": {},
+ "source": [
+ "And then using Breadth First Search to do 3 things:\n",
+ " - Find all nodes\n",
+ " - Find all edges\n",
+ " - Find all paths from $L$ to our parameters\n",
+ "\n",
+ "We haven't implemented a simple way to check whether two tensors are identical so we'll need compare hashes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "6e20bfa5-4ebe-4ff3-a98c-94f5e099c589",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "edges = []\n",
+ "\n",
+ "stack = [(L, [L])]\n",
+ "\n",
+ "nodes = []\n",
+ "edges = []\n",
+ "while stack:\n",
+ " node, current_path = stack.pop()\n",
+ " # Record nodes we haven't seen before\n",
+ " if hash(node) not in [hash(n) for n in nodes]:\n",
+ " nodes.append(node)\n",
+ "\n",
+ " # If we have reached a parameter (it has no arguments\n",
+ " # because it wasn't created by an operation) then\n",
+ " # record the path taken to get here\n",
+ " if not node.args:\n",
+ " if not hasattr(node, \"paths\"):\n",
+ " node.paths = []\n",
+ " node.paths.append(current_path)\n",
+ " continue\n",
+ "\n",
+ " for arg in node.args:\n",
+ " stack.append((arg, current_path + [arg]))\n",
+ " # Record every new edge\n",
+ " edges.append((hash(node), hash(arg)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "13c5a96e-9485-4fa7-b19a-54f4bf76f4c3",
+ "metadata": {},
+ "source": [
+ "Now we've got all of the edges and nodes, we have complete knowledge of our computational graph. Lets use networkx to plot it"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "5f73c63a-2e9f-425f-b4a5-52604bcbb3e5",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAApQAAAHzCAYAAACe1o1DAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB/1UlEQVR4nO3dZ1RU994F4D0DglQL9oIdC6L0ZkHsiSWWIBp7NxpjTDT2mKgxdo0ajR07orFEo4kaBRGRTkTssWAvoNLLzJz3gxdeJhRBBs6U/azlutcpZzb3yp/N+Z0iEQRBABERERHRB5KKHYCIiIiINBsLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlQgLJRERERGVCAslEREREZUICyURERERlYi+2AGIiNRNSoYM9+NTkClTwEBfivoWJjAx5HJJRFQQrpBERABuP0/C3pA4nL/5AnEJqRByPScBYFnZGJ5Nq2GwiyWaVDcTKyYRkVqSCIIgvP9lRETa6WFCKmYfiUHgnVfQk0ogVxS8JGY/365xFSzua4O6lY3LMCkRkfpioSQineUbFof5v8dCphAKLZL/pSeVQF8qwQ+9rTHQybIUExIRaQYWSiLSSevP38aK07dKvJ1pXa3whWcTFSQiItJcPMubiHSOb1icSsokAKw4fQsHwuJUsi0iIk3FQklEOuVhQirm/x5b4PPJV87iwZKeyHh6u8jb/O73WDxMSFVFPCIijcRCSUQ6ZfaRGMiKcbxkUcgUAmYfiVHpNomINAkLJRHpjNvPkxB451WxTsApCrlCQOCdV7jzIkml2yUi0hQslESkM/aGxEFPKimVbetJJdhzmcdSEpFuYqEkIp1x/uYLle+dzCZXCDh/60WpbJuISN2x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5v8eC5lCKNZxQnpSCfSlEizobc0Fkoi0HtdKKg4WStJJDxNSMftIDALvvIKeVFLoYpn9fLvGVbC4rw1HN0SkM7hWUlGxUJJOu/08CXtD4nD+1gvExaci9zeDBO8uxOtpVQ1DXC3RuJqZWDGJiETFtZLeh4WS6H9SMmS4H5+CTJkCBvpS1Lcw4V0diIj+g2sl5YeFkoiIiIhKhGd5ExEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJsFASERERUYmwUBIRERFRibBQEhEREVGJ/B/cr9KGPpK4rAAAAABJRU5ErkJggg==",
+ "text/plain": [
+ "