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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ website/out/
# MCP server build artifacts
mcp-server/node_modules/
mcp-server/dist/

__pycache__
6 changes: 3 additions & 3 deletions llm/Sinusoidal-Positional-Embedding/problem.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ tracks = ["advanced", "llm-path"]

# GENERATED from the grader spec — edit python/torchleet/problems/*.py, then run: generate.py hints
hints = [
"Even dimensions get sin, odd dimensions get cos.",
"The frequency for dimension i is 1 / 10000**(2i/d_model).",
"Even dimensions 2i get sin and odd dimensions 2i+1 get cos. The pair shares one frequency.",
"i is 0, 1, ..., d_model/2 - 1, not the raw dimension. The frequency is 1 / 10000**(2i/d_model).",
"It is a fixed function of position — register it as a buffer, not a parameter.",
]

Expand All @@ -21,7 +21,7 @@ question = "sinusoidal-q7-Question.ipynb"
solution = "sinusoidal-q7.ipynb"

[grading]
tier = "C" # A=auto-graded B=partial C=not auto-graded (SEEDED, confirm)
tier = "A" # A=auto-graded B=partial C=not auto-graded (SEEDED, confirm)
entries = ["SinusoidalPositionalEmbedding"]
extras = []
device = "cpu"
Expand Down
77 changes: 25 additions & 52 deletions llm/Sinusoidal-Positional-Embedding/sinusoidal-q7-Question.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,54 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Implement Attention from Scratch\n",
"# Implement Sinusoidal Embeddings\n",
"### Problem Statement\n",
"Transformers are order-agnostic — they see tokens like goldfish: no sense of sequence. To inject **position awareness** into the model, we use **Sinusoidal Positional Embeddings**, where each position in the sequence gets a unique deterministic vector. These vectors are computed using sine and cosine waves at different frequencies.\n",
"\n",
"Your task is to implement the sinusoidal position encoding mechanism from scratch using PyTorch — no cheating with built-ins from `fairseq` or Hugging Face.\n",
"Your task is to implement the sinusoidal position encoding from \"Attention Is All You Need\" using PyTorch. Do not use a positional-encoding module from Fairseq or Hugging Face.\n",
"\n",
"---\n",
"\n",
"### Requirements\n",
"\n",
"1. **Define the Sinusoidal Embedding Class**\n",
" - Implement a `SinusoidalPositionalEmbedding` class inheriting from `nn.Module`.\n",
" - Initialize with `max_seq_len` and `d_model`.\n",
" - Create a tensor `pe` of shape `(max_seq_len, d_model)` filled with sine and cosine encodings:\n",
" - `sin(position * ω)` for even indices\n",
" - `cos(position * ω)` for odd indices\n",
" - Initialize with `max_seq_len` and `d_model`, in that order.\n",
" - Create a tensor `pe` of shape `(max_seq_len, d_model)`. Positions start at 0. `d_model` is even. For each frequency index `i = 0, 1, ..., d_model/2 - 1`, set `ω_i = 1 / 10000**(2i/d_model)`:\n",
" - dimension `2i` gets `sin(pos * ω_i)`\n",
" - dimension `2i+1` gets `cos(pos * ω_i)`\n",
" - That same `ω_i` is shared by the pair. `i` is not the raw dimension index.\n",
"\n",
"2. **Register as Buffer**\n",
" - Use `self.register_buffer(\"pe\", pe)` to store `pe` without treating it as a trainable parameter.\n",
"\n",
"3. **Generate Encodings**\n",
" - On calling `forward(x)`, return the slice of positional encodings matching the sequence length of `x`.\n",
" - `forward(x)` receives `x` of shape `(batch_size, seq_len, d_model)`.\n",
" - Use only `seq_len` (`x.shape[1]`). Ignore the values in `x`.\n",
" - Return the first `seq_len` rows of `pe` with a leading size-1 batch dimension, shape `(1, seq_len, d_model)`, so the result broadcasts across the batch.\n",
"\n",
"4. **Test the Embeddings**\n",
" - Initialize the embedding class with `max_seq_len = 100` and `d_model = 64`.\n",
" - Pass a sequence of length 50 to verify the returned shape is `(1, 50, 64)`.\n",
" - Construct `SinusoidalPositionalEmbedding(max_seq_len, d_model)` with `max_seq_len = 100` and `d_model = 64`.\n",
" - Pass an input of shape `(1, 50, 64)` and check that the output shape is `(1, 50, 64)`.\n",
"\n",
"---\n",
"\n",
"### Constraints\n",
"\n",
"- ✅ Do not use Hugging Face, Fairseq, or built-in PyTorch modules for position encoding.\n",
"- ✅ Do not use Hugging Face, Fairseq, or any prebuilt positional-encoding module.\n",
"- ✅ Ensure the `pe` tensor is not a trainable parameter.\n",
"- ✅ Support any sequence length up to `max_seq_len`.\n",
"- ❌ Do not inject these embeddings directly into token embeddings yet — this is just the embedding module.\n",
"- ❌ Do not add these embeddings to token embeddings yet — this module only returns the positional table.\n",
"\n",
"---\n",
"\n",
"<details>\n",
" <summary>💡 Hint</summary>\n",
"\n",
" - Use `torch.arange(0, max_seq_len).unsqueeze(1)` to create position indices.\n",
" - Compute frequencies with `torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))`.\n",
" - Alternate `sin` and `cos` values for even and odd embedding dimensions.\n",
" - When returning the embedding in `forward`, use `.unsqueeze(0)` to broadcast over the batch dimension.\n",
" - Compute the shared frequencies with `torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))`. `arange(0, d_model, 2)` is `2i`, so each value is the `ω_i` for one sin/cos pair.\n",
" - Write those sines into `pe[:, 0::2]` and the cosines into `pe[:, 1::2]`.\n",
" - In `forward`, slice `self.pe[:seq_len]` and call `.unsqueeze(0)` so the batch dimension is 1.\n",
"\n",
"</details>"
]
Expand All @@ -71,33 +74,9 @@
"source": [
"import torch\n",
"import torch.nn as nn\n",
"import torch.optim as optim\n",
"import torch.nn.functional as F\n",
"import math"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Synthetic data\n",
"torch.manual_seed(42)\n",
"batch_size = 3\n",
"seq_len = 4\n",
"d_model = 8\n",
"num_heads = 2\n",
"\n",
"q = torch.rand(batch_size, seq_len, d_model)\n",
"k = torch.rand(batch_size, seq_len, d_model)\n",
"v = torch.rand(batch_size, seq_len, d_model)\n",
"print(q.shape)\n",
"\n",
"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
"device = \"cpu\""
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -120,10 +99,11 @@
" Returns the positional embedding for a given input tensor.\n",
" \n",
" Args:\n",
" x (Tensor): Input tensor of shape (batch_size, seq_len, d_model).\n",
" x (Tensor): Input of shape (batch_size, seq_len, d_model).\n",
" Only seq_len is used; the values in x are ignored.\n",
" \n",
" Returns:\n",
" Tensor: Positional embeddings of shape (batch_size, seq_len, d_model).\n",
" Tensor: Positional embeddings of shape (1, seq_len, d_model).\n",
" \"\"\"\n",
" ..."
]
Expand All @@ -134,24 +114,17 @@
"metadata": {},
"outputs": [],
"source": [
"# from fairseq.modules.sinusoidal_positional_embedding import SinusoidalPositionalEmbedding\n",
"\n",
"max_seq_len = 100\n",
"d_model = 64\n",
"\n",
"# Fairseq's implementation requires the number of embeddings (seq length) and embedding dim\n",
"# pos_emb = SinusoidalPositionalEmbedding(d_model, max_seq_len, padding_idx=None)\n",
"\n",
"# Generate embeddings for a sequence of length 50\n",
"seq_len = 50\n",
"positions = torch.arange(seq_len).unsqueeze(0) # Shape: (1, seq_len)\n",
"# positional_encoding = pos_emb(positions) # Shape: (1, seq_len, d_model)\n",
"\n",
"custom_pos_emb = SinusoidalPositionalEmbedding(d_model, max_seq_len)\n",
"custom_pos_emb = SinusoidalPositionalEmbedding(max_seq_len, d_model)\n",
"\n",
"positional_encoding_custom = custom_pos_emb(positions)\n",
"x = torch.zeros(1, seq_len, d_model)\n",
"positional_encoding = custom_pos_emb(x)\n",
"\n",
"print(positional_encoding_custom.shape) # (1, 50, 64)\n"
"print(positional_encoding.shape) # (1, 50, 64)\n",
"assert positional_encoding.shape == (1, 50, 64)"
]
}
],
Expand Down
91 changes: 28 additions & 63 deletions llm/Sinusoidal-Positional-Embedding/sinusoidal-q7.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,98 +4,69 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Implement Attention from Scratch\n",
"# Implement Sinusoidal Embeddings\n",
"### Problem Statement\n",
"Transformers are order-agnostic — they see tokens like goldfish: no sense of sequence. To inject **position awareness** into the model, we use **Sinusoidal Positional Embeddings**, where each position in the sequence gets a unique deterministic vector. These vectors are computed using sine and cosine waves at different frequencies.\n",
"\n",
"Your task is to implement the sinusoidal position encoding mechanism from scratch using PyTorch — no cheating with built-ins from `fairseq` or Hugging Face.\n",
"Your task is to implement the sinusoidal position encoding from \"Attention Is All You Need\" using PyTorch. Do not use a positional-encoding module from Fairseq or Hugging Face.\n",
"\n",
"---\n",
"\n",
"### Requirements\n",
"\n",
"1. **Define the Sinusoidal Embedding Class**\n",
" - Implement a `SinusoidalPositionalEmbedding` class inheriting from `nn.Module`.\n",
" - Initialize with `max_seq_len` and `d_model`.\n",
" - Create a tensor `pe` of shape `(max_seq_len, d_model)` filled with sine and cosine encodings:\n",
" - `sin(position * ω)` for even indices\n",
" - `cos(position * ω)` for odd indices\n",
" - Initialize with `max_seq_len` and `d_model`, in that order.\n",
" - Create a tensor `pe` of shape `(max_seq_len, d_model)`. Positions start at 0. `d_model` is even. For each frequency index `i = 0, 1, ..., d_model/2 - 1`, set `ω_i = 1 / 10000**(2i/d_model)`:\n",
" - dimension `2i` gets `sin(pos * ω_i)`\n",
" - dimension `2i+1` gets `cos(pos * ω_i)`\n",
" - That same `ω_i` is shared by the pair. `i` is not the raw dimension index.\n",
"\n",
"2. **Register as Buffer**\n",
" - Use `self.register_buffer(\"pe\", pe)` to store `pe` without treating it as a trainable parameter.\n",
"\n",
"3. **Generate Encodings**\n",
" - On calling `forward(x)`, return the slice of positional encodings matching the sequence length of `x`.\n",
" - `forward(x)` receives `x` of shape `(batch_size, seq_len, d_model)`.\n",
" - Use only `seq_len` (`x.shape[1]`). Ignore the values in `x`.\n",
" - Return the first `seq_len` rows of `pe` with a leading size-1 batch dimension, shape `(1, seq_len, d_model)`, so the result broadcasts across the batch.\n",
"\n",
"4. **Test the Embeddings**\n",
" - Initialize the embedding class with `max_seq_len = 100` and `d_model = 64`.\n",
" - Pass a sequence of length 50 to verify the returned shape is `(1, 50, 64)`.\n",
" - Construct `SinusoidalPositionalEmbedding(max_seq_len, d_model)` with `max_seq_len = 100` and `d_model = 64`.\n",
" - Pass an input of shape `(1, 50, 64)` and check that the output shape is `(1, 50, 64)`.\n",
"\n",
"---\n",
"\n",
"### Constraints\n",
"\n",
"- ✅ Do not use Hugging Face, Fairseq, or built-in PyTorch modules for position encoding.\n",
"- ✅ Do not use Hugging Face, Fairseq, or any prebuilt positional-encoding module.\n",
"- ✅ Ensure the `pe` tensor is not a trainable parameter.\n",
"- ✅ Support any sequence length up to `max_seq_len`.\n",
"- ❌ Do not inject these embeddings directly into token embeddings yet — this is just the embedding module.\n",
"- ❌ Do not add these embeddings to token embeddings yet — this module only returns the positional table.\n",
"\n",
"---\n",
"\n",
"<details>\n",
" <summary>💡 Hint</summary>\n",
"\n",
" - Use `torch.arange(0, max_seq_len).unsqueeze(1)` to create position indices.\n",
" - Compute frequencies with `torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))`.\n",
" - Alternate `sin` and `cos` values for even and odd embedding dimensions.\n",
" - When returning the embedding in `forward`, use `.unsqueeze(0)` to broadcast over the batch dimension.\n",
" - Compute the shared frequencies with `torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model))`. `arange(0, d_model, 2)` is `2i`, so each value is the `ω_i` for one sin/cos pair.\n",
" - Write those sines into `pe[:, 0::2]` and the cosines into `pe[:, 1::2]`.\n",
" - In `forward`, slice `self.pe[:seq_len]` and call `.unsqueeze(0)` so the batch dimension is 1.\n",
"\n",
"</details>"
]
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"import torch.nn as nn\n",
"import torch.optim as optim\n",
"import torch.nn.functional as F\n",
"import math"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"torch.Size([3, 4, 8])\n"
]
}
],
"source": [
"# Synthetic data\n",
"torch.manual_seed(42)\n",
"batch_size = 3\n",
"seq_len = 4\n",
"d_model = 8\n",
"num_heads = 2\n",
"\n",
"q = torch.rand(batch_size, seq_len, d_model)\n",
"k = torch.rand(batch_size, seq_len, d_model)\n",
"v = torch.rand(batch_size, seq_len, d_model)\n",
"print(q.shape)\n",
"\n",
"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
"device = \"cpu\""
]
},
{
"cell_type": "code",
"execution_count": 5,
Expand Down Expand Up @@ -134,46 +105,40 @@
" Returns the positional embedding for a given input tensor.\n",
" \n",
" Args:\n",
" x (Tensor): Input tensor of shape (batch_size, seq_len, d_model).\n",
" x (Tensor): Input of shape (batch_size, seq_len, d_model).\n",
" Only seq_len is used; the values in x are ignored.\n",
" \n",
" Returns:\n",
" Tensor: Positional embeddings of shape (batch_size, seq_len, d_model).\n",
" Tensor: Positional embeddings of shape (1, seq_len, d_model).\n",
" \"\"\"\n",
" return self.pe[:x.shape[1], :].unsqueeze(0) # Shape: (1, seq_len, d_model)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"torch.Size([1, 50, 100])\n"
"torch.Size([1, 50, 64])\n"
]
}
],
"source": [
"# from fairseq.modules.sinusoidal_positional_embedding import SinusoidalPositionalEmbedding\n",
"\n",
"max_seq_len = 100\n",
"d_model = 64\n",
"\n",
"# Fairseq's implementation requires the number of embeddings (seq length) and embedding dim\n",
"# pos_emb = SinusoidalPositionalEmbedding(d_model, max_seq_len, padding_idx=None)\n",
"\n",
"# Generate embeddings for a sequence of length 50\n",
"seq_len = 50\n",
"positions = torch.arange(seq_len).unsqueeze(0) # Shape: (1, seq_len)\n",
"# positional_encoding = pos_emb(positions) # Shape: (1, seq_len, d_model)\n",
"\n",
"custom_pos_emb = SinusoidalPositionalEmbedding(d_model, max_seq_len)\n",
"custom_pos_emb = SinusoidalPositionalEmbedding(max_seq_len, d_model)\n",
"\n",
"positional_encoding_custom = custom_pos_emb(positions)\n",
"x = torch.zeros(1, seq_len, d_model)\n",
"positional_encoding = custom_pos_emb(x)\n",
"\n",
"print(positional_encoding_custom.shape) # (1, 50, 64)\n"
"print(positional_encoding.shape) # (1, 50, 64)\n",
"assert positional_encoding.shape == (1, 50, 64)"
]
}
],
Expand Down
8 changes: 4 additions & 4 deletions mcp-server/src/data/hints.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,13 @@ export const authoredHints: Record<string, string[]> = {
"With num_query_groups == num_heads GQA is ordinary multi-head attention."
],
"sinusoidal-positional-embedding": [
"Even dimensions get sin, odd dimensions get cos.",
"The frequency for dimension i is 1 / 10000**(2i/d_model).",
"Even dimensions 2i get sin and odd dimensions 2i+1 get cos. The pair shares one frequency.",
"i is 0, 1, ..., d_model/2 - 1, not the raw dimension. The frequency is 1 / 10000**(2i/d_model).",
"It is a fixed function of position — register it as a buffer, not a parameter."
],
"v2-10": [
"Even dimensions get sin, odd dimensions get cos.",
"The frequency for dimension i is 1 / 10000**(2i/d_model).",
"Even dimensions 2i get sin and odd dimensions 2i+1 get cos. The pair shares one frequency.",
"i is 0, 1, ..., d_model/2 - 1, not the raw dimension. The frequency is 1 / 10000**(2i/d_model).",
"It is a fixed function of position — register it as a buffer, not a parameter."
],
"rotary-positional-embedding": [
Expand Down
4 changes: 2 additions & 2 deletions problems.json
Original file line number Diff line number Diff line change
Expand Up @@ -1001,8 +1001,8 @@
"SinusoidalPositionalEmbedding"
],
"hints": [
"Even dimensions get sin, odd dimensions get cos.",
"The frequency for dimension i is 1 / 10000**(2i/d_model).",
"Even dimensions 2i get sin and odd dimensions 2i+1 get cos. The pair shares one frequency.",
"i is 0, 1, ..., d_model/2 - 1, not the raw dimension. The frequency is 1 / 10000**(2i/d_model).",
"It is a fixed function of position \u2014 register it as a buffer, not a parameter."
]
},
Expand Down
Loading