mirror of
https://github.com/rasbt/LLMs-from-scratch.git
synced 2025-10-28 00:09:11 +00:00
851 lines
94 KiB
Plaintext
851 lines
94 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "6f678e62-7bcb-4405-86ae-dce94f494303",
|
|
"metadata": {
|
|
"id": "6f678e62-7bcb-4405-86ae-dce94f494303"
|
|
},
|
|
"source": [
|
|
"# Efficient Multi-Head Attention Implementations"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b742938a-4bfc-4527-a1f1-d5963508967d",
|
|
"metadata": {
|
|
"id": "b742938a-4bfc-4527-a1f1-d5963508967d"
|
|
},
|
|
"source": [
|
|
"This code notebook compares different ways to implement causal multi-head attention used in decoder-style LLMs like GPT, Llama, etc."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "7898551e-f582-48ac-9f66-3632abe2a93f",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "7898551e-f582-48ac-9f66-3632abe2a93f",
|
|
"outputId": "7d088260-3fa1-44f2-bd65-2a46e289f9d4"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"PyTorch version: 2.1.0\n",
|
|
"Running on cpu\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import torch\n",
|
|
"\n",
|
|
"torch.manual_seed(123)\n",
|
|
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
|
|
"print(f\"PyTorch version: {torch.__version__}\")\n",
|
|
"print(f\"Running on {device}\")\n",
|
|
"\n",
|
|
"batch_size = 8\n",
|
|
"context_len = 1024\n",
|
|
"embed_dim = 768\n",
|
|
"embeddings = torch.randn((batch_size, context_len, embed_dim), device=device)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2f9bb1b6-a1e5-4e0a-884d-0f31b374a8d6",
|
|
"metadata": {
|
|
"id": "2f9bb1b6-a1e5-4e0a-884d-0f31b374a8d6"
|
|
},
|
|
"source": [
|
|
"## 1) CausalAttention MHA wrapper class from chapter 3"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "297c93ed-aec0-4896-bb89-42c4b294d3d1",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "297c93ed-aec0-4896-bb89-42c4b294d3d1",
|
|
"outputId": "f8a33752-2cd6-4101-8feb-9d1699984719"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"torch.Size([8, 1024, 768])\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from ch03 import MultiHeadAttentionWrapper as Ch03_MHA_Wrapper\n",
|
|
"\n",
|
|
"mha_ch03_wrapper = Ch03_MHA_Wrapper(\n",
|
|
" d_in=embed_dim,\n",
|
|
" d_out=embed_dim//12,\n",
|
|
" block_size=context_len,\n",
|
|
" dropout=0.0,\n",
|
|
" num_heads=12,\n",
|
|
" qkv_bias=False\n",
|
|
").to(device)\n",
|
|
"\n",
|
|
"out = mha_ch03_wrapper(embeddings)\n",
|
|
"print(out.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "21930804-b327-40b1-8e63-94dcad39ce7b",
|
|
"metadata": {
|
|
"id": "21930804-b327-40b1-8e63-94dcad39ce7b"
|
|
},
|
|
"source": [
|
|
"## 2) The multi-head attention class from chapter 3"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "4ee6a61b-d25c-4a0c-8a59-f285544e3710",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "4ee6a61b-d25c-4a0c-8a59-f285544e3710",
|
|
"outputId": "b704a040-3547-422c-ecda-df9982a2da35"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"torch.Size([8, 1024, 768])\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from ch03 import MultiHeadAttention as Ch03_MHA\n",
|
|
"\n",
|
|
"mha_ch03 = Ch03_MHA(\n",
|
|
" d_in=embed_dim,\n",
|
|
" d_out=embed_dim,\n",
|
|
" block_size=context_len,\n",
|
|
" dropout=0.0,\n",
|
|
" num_heads=12,\n",
|
|
" qkv_bias=False\n",
|
|
").to(device)\n",
|
|
"\n",
|
|
"out = mha_ch03(embeddings)\n",
|
|
"print(out.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "73cd11da-ea3b-4081-b483-c4965dfefbc4",
|
|
"metadata": {
|
|
"id": "73cd11da-ea3b-4081-b483-c4965dfefbc4"
|
|
},
|
|
"source": [
|
|
"## 3) An alternative multi-head attention with combined weights"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1fa1a5ea-eaff-4d2d-aaf0-b34cdb6fd4dd",
|
|
"metadata": {
|
|
"id": "1fa1a5ea-eaff-4d2d-aaf0-b34cdb6fd4dd"
|
|
},
|
|
"source": [
|
|
"- The code for the `MultiHeadAttentionAlt` class below is based on code that was kindly shared by [Rayed Bin Wahed](https://github.com/rasbt/LLMs-from-scratch/discussions/51)\n",
|
|
"- The main difference between the `MultiHeadAttentionAlt` class and the `MultiHeadAttention` class used in chapter 3 is that `MultiHeadAttentionAlt` uses a single weight matrix, `self.qkv = nn.Linear(d_in, 3 * d_out, bias=qkv_bias)` instead of separate weight matrices:\n",
|
|
"\n",
|
|
" - `self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)`\n",
|
|
" - `self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)`\n",
|
|
" - `self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)`\n",
|
|
"\n",
|
|
"- Here, `self.qkv` combines all three weight matrices `self.W_query`, `self.W_key`, and `self.W_value` to carry out the query, key, and value computation in a single step\n",
|
|
"- Using `q, k, v = qkv.unbind(0)`, we obtain the individual query, key, and value tensors, which are then used similarly to the query, key, and value tensors in the `MultiHeadAttention` class in chapter 3"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "9a6bd0a2-f27c-4602-afa0-c96cd295c1a6",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "9a6bd0a2-f27c-4602-afa0-c96cd295c1a6",
|
|
"outputId": "5d948671-176f-4633-bede-97767e36becc"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"torch.Size([8, 1024, 768])\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import torch.nn as nn\n",
|
|
"\n",
|
|
"\n",
|
|
"class MultiHeadAttentionCombinedQKV(nn.Module):\n",
|
|
" def __init__(self, d_in, d_out, num_heads, block_size, dropout=0.0, qkv_bias=False):\n",
|
|
" super().__init__()\n",
|
|
"\n",
|
|
" assert d_out % num_heads == 0, \"embed_dim is indivisible by num_heads\"\n",
|
|
"\n",
|
|
" self.num_heads = num_heads\n",
|
|
" self.block_size = block_size\n",
|
|
" self.head_dim = d_out // num_heads\n",
|
|
"\n",
|
|
" self.qkv = nn.Linear(d_in, 3 * d_out, bias=qkv_bias)\n",
|
|
" self.proj = nn.Linear(d_in, d_out)\n",
|
|
" self.dropout = nn.Dropout(dropout)\n",
|
|
"\n",
|
|
" self.register_buffer(\n",
|
|
" \"mask\", torch.triu(torch.ones(block_size, block_size), diagonal=1)\n",
|
|
" )\n",
|
|
"\n",
|
|
" def forward(self, x):\n",
|
|
" batch_size, num_tokens, embed_dim = x.shape\n",
|
|
"\n",
|
|
" # (b, num_tokens, embed_dim) --> (b, num_tokens, 3 * embed_dim)\n",
|
|
" qkv = self.qkv(x)\n",
|
|
"\n",
|
|
" # (b, num_tokens, 3 * embed_dim) --> (b, num_tokens, 3, num_heads, head_dim)\n",
|
|
" qkv = qkv.reshape(batch_size, num_tokens, 3, self.num_heads, self.head_dim)\n",
|
|
"\n",
|
|
" # (b, num_tokens, 3, num_heads, head_dim) --> (3, b, num_heads, num_tokens, head_dim)\n",
|
|
" qkv = qkv.permute(2, 0, 3, 1, 4)\n",
|
|
"\n",
|
|
" # (3, b, num_heads, num_tokens, head_dim) -> 3 times (b, num_head, num_tokens, head_dim)\n",
|
|
" queries, keys, values = qkv.unbind(0)\n",
|
|
"\n",
|
|
" # (b, num_heads, num_tokens, head_dim) --> (b, num_heads, num_tokens, num_tokens)\n",
|
|
" attn_scores = queries @ keys.transpose(-2, -1)\n",
|
|
" attn_scores = attn_scores.masked_fill(\n",
|
|
" self.mask.bool()[:num_tokens, :num_tokens], -torch.inf\n",
|
|
" )\n",
|
|
"\n",
|
|
" attn_weights = torch.softmax(attn_scores / keys.shape[-1]**-0.5, dim=-1)\n",
|
|
" attn_weights = self.dropout(attn_weights)\n",
|
|
"\n",
|
|
" # (b, num_heads, num_tokens, num_tokens) --> (b, num_heads, num_tokens, head_dim)\n",
|
|
" context_vec = attn_weights @ values\n",
|
|
"\n",
|
|
" # (b, num_heads, num_tokens, head_dim) --> (b, num_tokens, num_heads, head_dim)\n",
|
|
" context_vec = context_vec.transpose(1, 2)\n",
|
|
"\n",
|
|
" # (b, num_tokens, num_heads, head_dim) --> (b, num_tokens, embed_dim)\n",
|
|
" context_vec = context_vec.reshape(batch_size, num_tokens, embed_dim)\n",
|
|
"\n",
|
|
" context_vec = self.proj(context_vec)\n",
|
|
"\n",
|
|
" return context_vec\n",
|
|
"\n",
|
|
"\n",
|
|
"mha_combined_qkv = MultiHeadAttentionCombinedQKV(\n",
|
|
" d_in=embed_dim,\n",
|
|
" d_out=embed_dim,\n",
|
|
" block_size=context_len,\n",
|
|
" dropout=0.0,\n",
|
|
" num_heads=12,\n",
|
|
" qkv_bias=False\n",
|
|
").to(device)\n",
|
|
"\n",
|
|
"out = mha_combined_qkv(embeddings)\n",
|
|
"print(out.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "48a042d3-ee78-4c29-bf63-d92fe6706632",
|
|
"metadata": {
|
|
"id": "48a042d3-ee78-4c29-bf63-d92fe6706632"
|
|
},
|
|
"source": [
|
|
"## 4) Multihead attention with PyTorch's scaled dot product attention"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f78e346f-3b85-44e6-9feb-f01131381148",
|
|
"metadata": {
|
|
"id": "f78e346f-3b85-44e6-9feb-f01131381148"
|
|
},
|
|
"source": [
|
|
"- The implementation below uses PyTorch's [`scaled_dot_product_attention`](https://pytorch.org/docs/stable/generated/torch.nn.functional.scaled_dot_product_attention.html) function, which implements a memory-optimized version of self-attention calld [flash attention](https://arxiv.org/abs/2205.14135)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "1b8e5a0d-1f65-4a03-bf6e-723f0cc428f5",
|
|
"metadata": {
|
|
"id": "1b8e5a0d-1f65-4a03-bf6e-723f0cc428f5"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"class MHAPyTorchScaledDotProduct(nn.Module):\n",
|
|
" def __init__(self, d_in, d_out, num_heads, block_size, dropout=0.0, qkv_bias=False):\n",
|
|
" super().__init__()\n",
|
|
"\n",
|
|
" assert d_out % num_heads == 0, \"embed_dim is indivisible by num_heads\"\n",
|
|
"\n",
|
|
" self.num_heads = num_heads\n",
|
|
" self.block_size = block_size\n",
|
|
" self.head_dim = d_out // num_heads\n",
|
|
" self.d_out = d_out\n",
|
|
"\n",
|
|
" self.qkv = nn.Linear(d_in, 3 * d_out, bias=qkv_bias)\n",
|
|
" self.proj = nn.Linear(d_in, d_out)\n",
|
|
" self.dropout = dropout\n",
|
|
"\n",
|
|
" self.register_buffer(\n",
|
|
" \"mask\", torch.triu(torch.ones(block_size, block_size), diagonal=1)\n",
|
|
" )\n",
|
|
"\n",
|
|
" def forward(self, x):\n",
|
|
" batch_size, num_tokens, embed_dim = x.shape\n",
|
|
"\n",
|
|
" # (b, num_tokens, embed_dim) --> (b, num_tokens, 3 * embed_dim)\n",
|
|
" qkv = self.qkv(x)\n",
|
|
"\n",
|
|
" # (b, num_tokens, 3 * embed_dim) --> (b, num_tokens, 3, num_heads, head_dim)\n",
|
|
" qkv = qkv.reshape(batch_size, num_tokens, 3, self.num_heads, self.head_dim)\n",
|
|
"\n",
|
|
" # (b, num_tokens, 3, num_heads, head_dim) --> (3, b, num_heads, num_tokens, head_dim)\n",
|
|
" qkv = qkv.permute(2, 0, 3, 1, 4)\n",
|
|
"\n",
|
|
" # (3, b, num_heads, num_tokens, head_dim) -> 3 times (b, num_heads, num_tokens, head_dim)\n",
|
|
" queries, keys, values = qkv.unbind(0)\n",
|
|
"\n",
|
|
" use_dropout = 0. if not self.training else self.dropout\n",
|
|
" context_vec = nn.functional.scaled_dot_product_attention(\n",
|
|
" queries, keys, values, attn_mask=None, dropout_p=use_dropout, is_causal=True)\n",
|
|
"\n",
|
|
" # Combine heads, where self.d_out = self.num_heads * self.head_dim\n",
|
|
" context_vec = context_vec.transpose(1, 2).contiguous().view(batch_size, num_tokens, self.d_out)\n",
|
|
"\n",
|
|
" return context_vec"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "fbc8ba92-3471-41cb-b1b2-4c0ef5be392b",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "fbc8ba92-3471-41cb-b1b2-4c0ef5be392b",
|
|
"outputId": "af9e4855-7f20-4d61-8532-4827df8dfb30"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"torch.Size([8, 1024, 768])\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"mha_pytorch_scaled = MHAPyTorchScaledDotProduct(\n",
|
|
" d_in=embed_dim,\n",
|
|
" d_out=embed_dim,\n",
|
|
" block_size=context_len,\n",
|
|
" dropout=0.0,\n",
|
|
" num_heads=12,\n",
|
|
" qkv_bias=False\n",
|
|
").to(device)\n",
|
|
"\n",
|
|
"out = mha_pytorch_scaled(embeddings)\n",
|
|
"print(out.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "351c318f-4835-4d74-8d58-a070222447c4",
|
|
"metadata": {
|
|
"id": "351c318f-4835-4d74-8d58-a070222447c4"
|
|
},
|
|
"source": [
|
|
"## 5) Using PyTorch's torch.nn.MultiheadAttention"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "74a6d060-6324-48fa-a35c-cb09f2a48965",
|
|
"metadata": {
|
|
"id": "74a6d060-6324-48fa-a35c-cb09f2a48965"
|
|
},
|
|
"source": [
|
|
"- Below, we use PyTorch's [torch.nn.MultiheadAttention](https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.html) implementation"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "3799c7ef-3155-42c6-a829-f95656453ae0",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "3799c7ef-3155-42c6-a829-f95656453ae0",
|
|
"outputId": "2a085df8-0445-4818-9978-6dc74469f568"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"torch.Size([8, 1024, 768])\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import torch.nn as nn\n",
|
|
"\n",
|
|
"\n",
|
|
"class MHAPyTorchClass(nn.Module):\n",
|
|
" def __init__(self, d_in, d_out, num_heads, block_size, dropout=0.0, qkv_bias=False, need_weights=True):\n",
|
|
" super().__init__()\n",
|
|
"\n",
|
|
" self.block_size = block_size\n",
|
|
" self.multihead_attn = nn.MultiheadAttention(\n",
|
|
" embed_dim=d_out,\n",
|
|
" num_heads=num_heads,\n",
|
|
" dropout=dropout,\n",
|
|
" bias=qkv_bias,\n",
|
|
" add_bias_kv=qkv_bias,\n",
|
|
" batch_first=True,\n",
|
|
" )\n",
|
|
"\n",
|
|
" self.need_weights = need_weights\n",
|
|
" self.proj = nn.Linear(d_out, d_out)\n",
|
|
" self.register_buffer(\"mask\", torch.triu(torch.ones(block_size, block_size), diagonal=1).bool())\n",
|
|
"\n",
|
|
" def forward(self, x):\n",
|
|
" batch_size, num_tokens, _ = x.shape\n",
|
|
"\n",
|
|
" # Ensure attn_mask is compatible with expected shape and `batch_first=True`\n",
|
|
" # No need to manually adjust for num_heads; ensure it's right for the sequence\n",
|
|
" if self.block_size >= num_tokens:\n",
|
|
" attn_mask = self.mask[:num_tokens, :num_tokens]\n",
|
|
" else:\n",
|
|
" attn_mask = self.mask[:self.block_size, :self.block_size]\n",
|
|
"\n",
|
|
" # attn_mask broadcasting will handle batch_size dimension implicitly\n",
|
|
" attn_output, _ = self.multihead_attn(\n",
|
|
" x, x, x, attn_mask=attn_mask, need_weights=self.need_weights\n",
|
|
" )\n",
|
|
"\n",
|
|
" output = self.proj(attn_output)\n",
|
|
"\n",
|
|
" return output\n",
|
|
"\n",
|
|
"\n",
|
|
"mha_pytorch_class_default = MHAPyTorchClass(\n",
|
|
" d_in=embed_dim,\n",
|
|
" d_out=embed_dim,\n",
|
|
" block_size=context_len,\n",
|
|
" dropout=0.0,\n",
|
|
" num_heads=12,\n",
|
|
" qkv_bias=False\n",
|
|
").to(device)\n",
|
|
"\n",
|
|
"out = mha_pytorch_class_default(embeddings)\n",
|
|
"print(out.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a3953bff-1056-4de2-bfd1-dfccf659eee4",
|
|
"metadata": {
|
|
"id": "a3953bff-1056-4de2-bfd1-dfccf659eee4"
|
|
},
|
|
"source": [
|
|
"## 6) Using PyTorch's torch.nn.MultiheadAttention with `scaled_dot_product_attention`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d2164859-31a0-4537-b4fb-27d57675ba77",
|
|
"metadata": {
|
|
"id": "d2164859-31a0-4537-b4fb-27d57675ba77"
|
|
},
|
|
"source": [
|
|
"- Set `need_weights` (default `True`) to need_weights=False so that MultiheadAttention uses `scaled_dot_product_attention` [according to the documentation](https://github.com/pytorch/pytorch/blob/71d020262793542974cf13b30f2a9099773f015c/torch/nn/modules/activation.py#L1096)\n",
|
|
"\n",
|
|
"> need_weights: If specified, returns ``attn_output_weights`` in addition to ``attn_outputs``.\n",
|
|
" Set ``need_weights=False`` to use the optimized ``scaled_dot_product_attention``\n",
|
|
" and achieve the best performance for MHA.\n",
|
|
" Default: ``True``."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "4a4c2afe-5e1f-4bd7-a118-67031176f147",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "4a4c2afe-5e1f-4bd7-a118-67031176f147",
|
|
"outputId": "234771f4-8a53-4478-8a9b-cf19f79a5e07"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"torch.Size([8, 1024, 768])\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"mha_pytorch_class_noweights = MHAPyTorchClass(\n",
|
|
" d_in=embed_dim,\n",
|
|
" d_out=embed_dim,\n",
|
|
" block_size=context_len,\n",
|
|
" dropout=0.0,\n",
|
|
" num_heads=12,\n",
|
|
" qkv_bias=False,\n",
|
|
" need_weights=False # NEW!\n",
|
|
").to(device)\n",
|
|
"\n",
|
|
"out = mha_pytorch_class_noweights(embeddings)\n",
|
|
"print(out.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8877de71-f84f-4f6d-bc87-7552013b6301",
|
|
"metadata": {
|
|
"id": "8877de71-f84f-4f6d-bc87-7552013b6301"
|
|
},
|
|
"source": [
|
|
"## Quick speed comparison (M3 Macbook Air CPU)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "a97c0b2e-6593-49d8-98bc-2267b3aa610f",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "a97c0b2e-6593-49d8-98bc-2267b3aa610f",
|
|
"outputId": "ebe635b2-5c03-4e9b-da3a-951d308acf7b"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"194 ms ± 2.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"## 1) CausalAttention MHA wrapper class from chapter 3\n",
|
|
"%timeit mha_ch03_wrapper(embeddings)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "19db9c2c-8e75-431a-8eef-0b4d8284e6e6",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "19db9c2c-8e75-431a-8eef-0b4d8284e6e6",
|
|
"outputId": "c6e7bcff-661c-45a6-da82-b1e3f89cf761"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"198 ms ± 4.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"## 2) The multi-head attention class from chapter 3\n",
|
|
"%timeit mha_ch03(embeddings)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "aa526ee0-7a88-4f34-a49a-f8f97da83779",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "aa526ee0-7a88-4f34-a49a-f8f97da83779",
|
|
"outputId": "92b634f8-43f8-468f-87a1-bb774b64c212"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"234 ms ± 4.26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"## 3) An alternative multi-head attention with combined weights\n",
|
|
"%timeit mha_combined_qkv(embeddings)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "cc2b4256-16d8-4c34-9fd0-d4b4af0e60fa",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "cc2b4256-16d8-4c34-9fd0-d4b4af0e60fa",
|
|
"outputId": "80c6e314-0771-470e-b090-628984ce2d85"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"71.7 ms ± 3.65 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"## 4) Multihead attention with PyTorch's scaled dot product attention\n",
|
|
"%timeit mha_pytorch_scaled(embeddings)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"id": "0f209e70-ebb6-4a1a-b608-1ff42e41c01d",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/"
|
|
},
|
|
"id": "0f209e70-ebb6-4a1a-b608-1ff42e41c01d",
|
|
"outputId": "3cd37b53-04d4-4dd0-9450-6fc8ebaac083"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"211 ms ± 5.31 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"## 5) Using PyTorch's torch.nn.MultiheadAttention\n",
|
|
"%timeit mha_pytorch_class_default(embeddings)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "3f4968c2-8d40-4ab9-8dba-052b4f77d756",
|
|
"metadata": {
|
|
"id": "3f4968c2-8d40-4ab9-8dba-052b4f77d756",
|
|
"outputId": "2e86bdb4-7fa0-4051-b000-4a2b591060a2",
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"207 ms ± 18.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"## 6) Using PyTorch's torch.nn.MultiheadAttention disabling `need_weights`\n",
|
|
"%timeit mha_pytorch_class_noweights(embeddings)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "dabc6575-0316-4640-a729-e616d5c17b73",
|
|
"metadata": {
|
|
"id": "dabc6575-0316-4640-a729-e616d5c17b73"
|
|
},
|
|
"source": [
|
|
"## Speed comparison (Nvidia A100 GPU) with warmup"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "29b63d3d-6d0b-43bb-9c68-d5514dc81000",
|
|
"metadata": {
|
|
"id": "29b63d3d-6d0b-43bb-9c68-d5514dc81000"
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# CUDA benchmark code shared by Andrei Aksionov\n",
|
|
"# and based on code from\n",
|
|
"# https://github.com/cuda-mode/lectures/blob/main/lecture1/pytorch_square.py\n",
|
|
"\n",
|
|
"import time\n",
|
|
"\n",
|
|
"def time_pytorch_function(func, *input, num_repeats = 100):\n",
|
|
" # CUDA IS ASYNC so can't use python time module\n",
|
|
" #start = torch.cuda.Event(enable_timing=True)\n",
|
|
" #end = torch.cuda.Event(enable_timing=True)\n",
|
|
" start = time.time()\n",
|
|
" # Warmup\n",
|
|
" #for _ in range(5):\n",
|
|
" # func(*input)\n",
|
|
" #torch.cuda.synchronize()\n",
|
|
"\n",
|
|
" #start.record()\n",
|
|
" for _ in range(num_repeats):\n",
|
|
" func(*input)\n",
|
|
" #torch.cuda.synchronize()\n",
|
|
" #end.record()\n",
|
|
" #torch.cuda.synchronize()\n",
|
|
" return (time.time()-start) / num_repeats"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "CDJAPZaszaqx",
|
|
"metadata": {
|
|
"colab": {
|
|
"base_uri": "https://localhost:8080/",
|
|
"height": 489
|
|
},
|
|
"id": "CDJAPZaszaqx",
|
|
"outputId": "f23e9b83-7fd6-4011-9434-0e6934cf762a"
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "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ehYv07C2tqZp06Y4OTkpM85z585N7dq1yZ49u5JySXzZ9evX6dWrF0+fPmXnzp38+uuvynX67NkzevXqxaFDh1i+fDkVKlTg9evXXLlyhQ0bNij1LGPeMif5C/lE0ozVlpaWdOrUiREjRuDu7s6wYcMwMjKiS5cumJubc/ToUXx8fNDR0VFLNil/DOpGjBjBggULaNasGTly5AA+1tH69ev5+eefOXr0KK6urtSrV48ZM2YoDffEiRPp0aNHehY9Q1KpVOTIkYOxY8cSHh7Ojh07cHd3p1mzZpw/f5527dqxc+dO+vXrh6mpKevXr6dGjRpyY6FBt27domzZssyePZvt27dTqlQpJede0ptAkbKEhATevn3L69evKV68OD4+PmzdupWAgAD27t2Lqamp9LJ9QdLr7Pbt23Tv3p1Tp05RunRp9PX1lX0iIyPx9PTkwIED7Ny5k6JFi6odR+o585IxcCmws7Nj3rx5RERE0KVLFyUpZ+J7AQEBODs7Exoaikqlolq1asycOZPr16/TtGnTdCx5xpN0wsKOHTv4+eefmTBhAidOnODNmzccP36cjh07EhISQrVq1ViwYAGhoaE4Ozunc8kzNl1dXU6dOoWnp2eyZL0lSpSgXr16tGvXjvfv31OqVCmioqL47bffePjwYfoUOBP41gHcvr6+5MyZk/j4ePr16ycD61ORtF6trKx48uQJpUqVYvLkybx9+5YSJUqwefNmTpw4weXLl5kzZw6+vr5s3bo1nUueOfz222+cOHECgEKFChEYGEiePHlo2LAhjx8/VvbLnj07PXr0YNy4cXKN/kdIAJcCPT09xo8fT9u2benTp48yziXRiRMn+Ouvv9TWi7OxscHf35+mTZvy4MGD713kDK1169YEBAQQFBTEs2fPsLGxIU+ePGzYsIFy5crx4sULBg4cyPv377GxsaFJkybJkp8KdXp6ehw/fhwPDw+l8f40AClatCjFixenT58+6OjoUKtWLWm4U5F0lvOX0iikFqRJ+oXkPp2wULRoUWbPns3ff/9N7dq1KVy4MHfv3uXIkSNERkZiYmLCunXrGDlyZIqriAj1Ov355585cOAAQ4YMUSZ8FSxYkBkzZpArVy4aNWqkpF9J2jbItfrf8MMHcKnddevr6xMUFEStWrXo1KkTBw8eJCEhARMTE3bs2MG0adOSBXZZsmTh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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"\n",
|
|
"\n",
|
|
"#embeddings_cuda = embeddings.to(torch.device(\"cuda\"))\n",
|
|
"\n",
|
|
"functions = {\n",
|
|
" \"1) MHA wrapper class\": mha_ch03_wrapper,\n",
|
|
" \"2) MHA Ch03\": mha_ch03,\n",
|
|
" \"3) MHA with combined QKV weights\": mha_combined_qkv,\n",
|
|
" \"4) MHA with PyTorch scaled_dot_product_attention\": mha_pytorch_scaled,\n",
|
|
" \"5) PyTorch MHA class defaults\": mha_pytorch_class_default,\n",
|
|
" \"6) PyTorch MHA with need_weights=False\": mha_pytorch_class_noweights\n",
|
|
"}\n",
|
|
"execution_times = [time_pytorch_function(fn, embeddings) for name,fn in functions.items()]\n",
|
|
"\n",
|
|
"\n",
|
|
"# Plotting\n",
|
|
"\n",
|
|
"# Customize further for dark mode aesthetics\n",
|
|
"plt.rcParams['figure.facecolor'] = '#121212' # Dark figure background\n",
|
|
"plt.rcParams['axes.facecolor'] = '#121212' # Dark axes background\n",
|
|
"plt.rcParams['axes.edgecolor'] = 'white' # White axes border\n",
|
|
"plt.rcParams['axes.labelcolor'] = 'white' # White labels\n",
|
|
"plt.rcParams['text.color'] = 'white' # White text\n",
|
|
"plt.rcParams['xtick.color'] = 'white' # White x ticks\n",
|
|
"plt.rcParams['ytick.color'] = 'white' # White y ticks\n",
|
|
"plt.rcParams['grid.color'] = '#444444' # Lighter grid lines for contrast\n",
|
|
"plt.rcParams['lines.linewidth'] = 2 # Thicker plot lines for visibility\n",
|
|
"plt.rcParams['lines.markersize'] = 8 # Larger markers for visibility\n",
|
|
"\n",
|
|
"fig, ax = plt.subplots()\n",
|
|
"bars = plt.bar(functions.keys(), execution_times)\n",
|
|
"\n",
|
|
"plt.ylabel('Execution time (ms)')\n",
|
|
"plt.xticks(rotation=45, ha=\"right\")\n",
|
|
"\n",
|
|
"# Calculate new ylim with a margin\n",
|
|
"max_execution_time = max(execution_times)\n",
|
|
"upper_ylim = max_execution_time + 0.2 * max_execution_time # Adding a 20% margin\n",
|
|
"\n",
|
|
"plt.ylim(0, upper_ylim) # Setting new ylim\n",
|
|
"\n",
|
|
"# Annotate bars with execution times\n",
|
|
"for bar in bars:\n",
|
|
" yval = bar.get_height()\n",
|
|
" plt.text(bar.get_x() + bar.get_width()/2, yval + (0.05 * upper_ylim), round(yval, 2), ha='center', va='bottom')\n",
|
|
"\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.savefig(\"2.pdf\")\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "d3e1137b-9acc-4cc5-bcbf-0e8533839f06",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"accelerator": "GPU",
|
|
"colab": {
|
|
"gpuType": "A100",
|
|
"machine_shape": "hm",
|
|
"provenance": []
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.10.6"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|