{ "cells": [ { "cell_type": "markdown", "id": "f8728e4d-0905-400b-a07f-c73f476311a3", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "
\n", "\n", "Supplementary code for the Build a Reasoning Model (From Scratch) book by Sebastian Raschka
\n", "
Code repository: https://github.com/rasbt/reasoning-from-scratch\n", "
\n", "
\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "d890f241-dde5-4496-9390-45083943bbe9", "metadata": {}, "source": [ "# Chapter 5: Inference-Time Scaling Via Self-Refinement" ] }, { "cell_type": "markdown", "id": "cf0d48c4-da2f-47af-8dce-8aef1b36e00f", "metadata": {}, "source": [ "Packages that are being used in this notebook:" ] }, { "cell_type": "code", "execution_count": 1, "id": "0855ec4c-ad3c-4b0d-a326-2104383a8c66", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "matplotlib version: 3.10.8\n", "reasoning_from_scratch version: 0.1.13\n", "torch version: 2.9.1\n", "tokenizers version: 0.22.2\n" ] } ], "source": [ "from importlib.metadata import version\n", "\n", "used_libraries = [\n", " \"matplotlib\",\n", " \"reasoning_from_scratch\",\n", " \"torch\",\n", " \"tokenizers\" # Used by reasoning_from_scratch\n", "]\n", "\n", "for lib in used_libraries:\n", " print(f\"{lib} version: {version(lib)}\")" ] }, { "cell_type": "markdown", "id": "4068f3ae-1fb1-4ace-aa32-3ebdf4d1ce71", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "cdddbc07-21e3-42ad-96c7-214b972caecf", "metadata": {}, "source": [ " \n", "## 5.1 Scoring and iteratively improving model responses" ] }, { "cell_type": "markdown", "id": "fd1acd24-8d13-4725-97fc-9e70d0f083b5", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "ece8e212-7bbf-409f-b300-b469d7bac17c", "metadata": {}, "source": [ "- No code in this section" ] }, { "cell_type": "markdown", "id": "f777ae22-a201-4d3f-bb03-55019f355957", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "59faffec-21c5-4dd4-9680-dc4869febd0f", "metadata": {}, "source": [ " \n", "## 5.2 Loading a pre-trained model" ] }, { "cell_type": "markdown", "id": "0895a7f1-fd61-4741-ae98-517bcfaa753d", "metadata": {}, "source": [ "- Model loading code similar to chapter 2-4" ] }, { "cell_type": "markdown", "id": "38484a79-913b-4f03-aa28-e701af3ec0ae", "metadata": {}, "source": [ "- Note that we hardcode the `\"cpu\"` device below to make the results as similar as possible across different machines and operating systems\n", "- I recommend running the chapter code here as is before deleting `device = torch.device(\"cpu\")` below\n", "- Even though we hardcode the `\"cpu\"` device, since the chapter involves the multiplication of many very small numbers, the results may differ on your computer (especially in section 5.4 and onward)" ] }, { "cell_type": "code", "execution_count": 2, "id": "07a828f1-1592-46c7-a89e-201682017449", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using Apple Silicon GPU (MPS)\n", "✓ qwen3/qwen3-0.6B-base.pth already up-to-date\n" ] } ], "source": [ "import torch\n", "\n", "from reasoning_from_scratch.ch02 import get_device\n", "from reasoning_from_scratch.ch03 import (\n", " load_model_and_tokenizer\n", ")\n", "\n", "device = get_device()\n", "device = torch.device(\"cpu\") # optionally delete to allow running on GPU\n", "\n", "model, tokenizer = load_model_and_tokenizer(\n", " which_model=\"base\",\n", " device=device,\n", " use_compile=False\n", ")" ] }, { "cell_type": "markdown", "id": "ef68702c-b387-46e7-9126-3e8fe758933f", "metadata": {}, "source": [ "- Let's try the model on a prompt from the MATH-500 dataset, which we worked with in the previous chapter:" ] }, { "cell_type": "code", "execution_count": 3, "id": "90809902-3568-48de-b283-bf5a12da4699", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ### Step 1: Understand the problem\n", "\n", "The problem states that half the value of \\(3x - 9\\) is equal to \\(x + 37\\). We need to find the value of \\(x\\).\n", "\n", "### Step 2: Translate the problem into an equation\n", "\n", "Let's translate the given statement into an equation. Half of \\(3x - 9\\) is equal to \\(x + 37\\).\n", "\n", "\\[\n", "\\frac{1}{2}(3x - 9) = x + 37\n", "\\]\n", "\n", "### Step 3: Solve the equation\n", "\n", "To solve for \\(x\\), we'll first eliminate the fraction by multiplying both sides of the equation by 2:\n", "\n", "\\[\n", "2 \\times \\frac{1}{2}(3x - 9) = 2 \\times (x + 37)\n", "\\]\n", "\n", "This simplifies to:\n", "\n", "\\[\n", "3x - 9 = 2x + 74\n", "\\]\n", "\n", "### Step 4: Isolate the variable \\(x\\)\n", "\n", "Now, we'll isolate \\(x\\) by subtracting \\(2x\\) from both sides of the equation:\n", "\n", "\\[\n", "3x - 2x - 9 = 74\n", "\\]\n", "\n", "Simplifying this, we get:\n", "\n", "\\[\n", "x - 9 = 74\n", "\\]\n", "\n", "### Step 5: Solve for \\(x\\)\n", "\n", "Next, we'll add 9 to both sides to solve for \\(x\\):\n", "\n", "\\[\n", "x - 9 + 9 = 74 + 9\n", "\\]\n", "\n", "This simplifies to:\n", "\n", "\\[\n", "x = 83\n", "\\]\n", "\n", "### Step 6: Verify the solution\n", "\n", "To ensure our solution is correct, we'll substitute \\(x = 83\\) back into the original equation and check if both sides are equal.\n", "\n", "Original equation:\n", "\n", "\\[\n", "\\frac{1}{2}(3x - 9) = x + 37\n", "\\]\n", "\n", "Substitute \\(x = 83\\):\n", "\n", "\\[\n", "\\frac{1}{2}(3 \\times 83 - 9) = 83 + 37\n", "\\]\n", "\n", "Calculate the left side:\n", "\n", "\\[\n", "\\frac{1}{2}(249 - 9) = \\frac{1}{2} \\times 240 = 120\n", "\\]\n", "\n", "Calculate the right side:\n", "\n", "\\[\n", "83 + 37 = 120\n", "\\]\n", "\n", "Since both sides equal 120, our solution is correct.\n", "\n", "### Final Answer:\n", "\n", "\\[\n", "\\boxed{83}\n", "\\]" ] } ], "source": [ "from reasoning_from_scratch.ch03 import render_prompt\n", "from reasoning_from_scratch.ch04 import (\n", " generate_text_stream_concat_flex,\n", " generate_text_top_p_stream_cache\n", ")\n", "\n", "raw_prompt = (\n", " \"Half the value of $3x-9$ is $x+37$. \"\n", " \"What is the value of $x$?\"\n", ")\n", "prompt = render_prompt(raw_prompt)\n", "prompt_cot = prompt + \"\\n\\nExplain step by step.\"\n", "\n", "torch.manual_seed(0)\n", "response_1 = generate_text_stream_concat_flex(\n", " model, tokenizer, prompt_cot, device,\n", " max_new_tokens=2048, verbose=True,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=0.9,\n", " top_p=0.9\n", ")" ] }, { "cell_type": "code", "execution_count": 4, "id": "e9cddfbf-dc30-4f63-8aff-729fb73a85ab", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " We start with the equation:\n", "\n", "\\[\n", "\\frac{1}{2} \\times (3x - 9) = x + 37\n", "\\]\n", "\n", "Step 1: Multiply both sides of the equation by 2 to eliminate the fraction:\n", "\n", "\\[\n", "2 \\times \\frac{1}{2} \\times (3x - 9) = 2 \\times (x + 37)\n", "\\]\n", "\n", "Simplifying both sides:\n", "\n", "\\[\n", "3x - 9 = 2x + 74\n", "\\]\n", "\n", "Step 2: Subtract \\(2x\\) from both sides to get all the \\(x\\) terms on one side:\n", "\n", "\\[\n", "3x - 2x - 9 = 74\n", "\\]\n", "\n", "Simplifying:\n", "\n", "\\[\n", "x - 9 = 74\n", "\\]\n", "\n", "Step 3: Add 9 to both sides to isolate \\(x\\):\n", "\n", "\\[\n", "x - 9 + 9 = 74 + 9\n", "\\]\n", "\n", "Simplifying:\n", "\n", "\\[\n", "x = 83\n", "\\]\n", "\n", "Final Answer:\n", "\n", "\\[\n", "\\boxed{83}\n", "\\]" ] } ], "source": [ "torch.manual_seed(3)\n", "response_2 = generate_text_stream_concat_flex(\n", " model, tokenizer, prompt_cot, device,\n", " max_new_tokens=2048, verbose=True,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=0.9,\n", " top_p=0.9, \n", ")" ] }, { "cell_type": "code", "execution_count": 5, "id": "6088115c-3ee9-41af-974c-b6acfdf7e7af", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Response 1 characters: 1419\n", "Response 1 tokens: 534\n", "\n", "Response 2 characters: 533\n", "Response 2 tokens: 231\n" ] } ], "source": [ "print(\"Response 1 characters:\", len(response_1))\n", "print(\"Response 1 tokens:\", len(tokenizer.encode(response_1)))\n", "print(\"\\nResponse 2 characters:\", len(response_2))\n", "print(\"Response 2 tokens:\", len(tokenizer.encode(response_2)))" ] }, { "cell_type": "markdown", "id": "32ff5525-918f-4b16-92cd-9f09a6b9dcd0", "metadata": {}, "source": [ "- By the way, the correct answer is 83" ] }, { "cell_type": "markdown", "id": "6f680722-dc25-4319-85d5-550fc73fa96a", "metadata": {}, "source": [ " \n", "## 5.3 Scoring LLM responses with a rule-based score" ] }, { "cell_type": "markdown", "id": "42931b01-e0b2-44f9-a300-58e8236446f2", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "1ccbc8c6-f078-40a4-8d81-ce9e9427bd7d", "metadata": {}, "source": [ "- Can be used as a tie breaker for self-consistency\n", "- Or can be used to score and rank all self-consistency responses (this is known as Best-of-N inference-scaling)" ] }, { "cell_type": "markdown", "id": "5a25f086-157f-4af2-969f-1a14458a6bf3", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 6, "id": "2418bf8c-8fcd-43ea-8bb6-1a84e23682ed", "metadata": {}, "outputs": [], "source": [ "from reasoning_from_scratch.ch03 import extract_final_candidate\n", "import math\n", "\n", "def heuristic_score(\n", " answer,\n", " prompt=None, # Placeholder that is ignored\n", " brevity_bonus=500.0,\n", " boxed_bonus=2.0,\n", " extract_bonus=1.0,\n", " fulltext_bonus=0.0,\n", "):\n", " score = 0.0\n", "\n", " # Reward answers that have a final boxed value\n", " cand = extract_final_candidate(answer, fallback=\"none\")\n", " if cand:\n", " score += boxed_bonus\n", "\n", " # Give weaker rewards if answer doesn't have a boxed value\n", " else:\n", " cand = extract_final_candidate(answer, fallback=\"number_only\")\n", " if cand:\n", " score += extract_bonus\n", " else:\n", " cand = extract_final_candidate(\n", " answer, fallback=\"number_then_full\"\n", " )\n", " if cand:\n", " score += fulltext_bonus\n", "\n", " # Add a brevity reward that decays with text length\n", " score += 1.5 * math.exp(-len(answer) / brevity_bonus)\n", " return score" ] }, { "cell_type": "markdown", "id": "27e3589d-a3ee-42ca-a2f0-774d8c42d5df", "metadata": {}, "source": [ "- Note that the code measures the number of characters, instead of tokens, for simplicity, so we don't have to pass a tokenizer here, but this can easily be changed" ] }, { "cell_type": "code", "execution_count": 7, "id": "41e29007-8250-4ac3-8d42-c9ba3c496708", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "def plot_brevity_curve(brevity_bonus, max_len=2048):\n", " lengths = torch.arange(1, max_len)\n", " scores = 1.5 * torch.exp(-lengths / brevity_bonus)\n", "\n", " plt.figure(figsize=(4, 3))\n", " plt.plot(lengths, scores)\n", " plt.xlabel(\"Text length (number of characters)\")\n", " plt.ylabel(\"Score contribution\")\n", " plt.tight_layout()\n", " #plt.savefig(\"brevity_curve.pdf\")\n", " plt.show()\n", "\n", "plot_brevity_curve(500)" ] }, { "cell_type": "code", "execution_count": 8, "id": "58bd6571-7866-4716-ba6f-57f3815fd18b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.088\n" ] } ], "source": [ "print(round(heuristic_score(response_1), 3))" ] }, { "cell_type": "code", "execution_count": 9, "id": "02338325-173a-49c1-9c27-eb97bf3bed50", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.517\n" ] } ], "source": [ "print(round(heuristic_score(response_2), 3))" ] }, { "cell_type": "markdown", "id": "11aeeb53-0dc9-495f-9087-a9fce8d486dc", "metadata": {}, "source": [ " \n", "## 5.4 Understanding token probability scores" ] }, { "cell_type": "markdown", "id": "fc46f489-9c2d-4272-bffb-5b245bb03cc9", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "3cca2315-04ee-48b5-ab5c-c8fc90bd8bc6", "metadata": {}, "source": [ "- Also called token likelihoods\n", "- Implement a function to score the generated texts\n", "- We will use this for the upcoming self-refinement approach, but similar to the `heuristic_score`, this can also be used as a tie-breaker in the self-consistency approach\n", "- The score could technically be calculated as part of the `generate_text_top_p_stream_cache` function in section 4.5, to improve efficiency, but here we keep it separate so it is a pluggable scoring function like `heuristic_score`" ] }, { "cell_type": "markdown", "id": "3ee36976-6982-4b81-8236-32d2898fc02c", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "8cbbdd0d-b7d1-495a-a9f9-d51076ebf270", "metadata": {}, "source": [ "- The above is a figure from the previous chapter that illustrates how the next token is generated\n", "- Here, we are interested in just the scores for different next token options, not actually generating it" ] }, { "cell_type": "markdown", "id": "5ff25557-bfd4-4dae-9e08-41b82b648f98", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "04c43ae9-ec3b-4b05-892d-db11e99f02bc", "metadata": {}, "source": [ "- For simplicity, the above showed the logits score, but it's more interpretable if we use probabilities (token likelihoods)" ] }, { "cell_type": "markdown", "id": "c35b00c0-677a-4714-b99a-d82f998ac7fe", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "c591bb5b-fb0a-46de-80b6-fc2b774faa7f", "metadata": {}, "source": [ "- The problem with the probability scores is that most of them are near zero (we will address that in the next section)\n", "- Suppose we have an LLM that generates the answer \"Germany's capital is Berlin\", which corresponds to 5 tokens when tokenized\n", "- The LLM assigns probabilities" ] }, { "cell_type": "markdown", "id": "591ac81c-7b3e-46b2-9636-35ad9ce82571", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 10, "id": "2f481338-b6c0-4b4f-9287-644606f48ee3", "metadata": {}, "outputs": [], "source": [ "@torch.inference_mode()\n", "def calc_next_token_probas(model, tokenizer, prompt, device, show=True):\n", "\n", " token_ids = torch.tensor(tokenizer.encode(prompt), device=device)\n", "\n", " # Get logits and probabilities similar to text generation functions\n", " logits = model(token_ids.unsqueeze(0)).squeeze(0)\n", " all_probas = torch.softmax(logits, dim=-1)\n", "\n", " # Positions we score (here: all)\n", " t_idx = torch.arange(0, token_ids.shape[0] - 1, device=device)\n", "\n", " # Since we have the text, we know the true next tokens\n", " next_ids = token_ids[1:]\n", "\n", " # Get probabilities for each next token\n", " next_token_probas = all_probas[t_idx, next_ids]\n", "\n", " # Likelihood of the sequence is the product of the probability scores\n", " prod_next_token_probas = torch.prod(next_token_probas)\n", "\n", " if show:\n", " print(\"Next-token probabilities:\", next_token_probas)\n", " print(\"Joint probability:\", prod_next_token_probas)\n", "\n", " else:\n", " return next_token_probas, prod_next_token_probas" ] }, { "cell_type": "markdown", "id": "61ae8989-4e0d-40f3-860e-df94867f6d5d", "metadata": {}, "source": [ "\\begin{align}\n", "P(x_1, x_2, \\dots, x_T \\mid W)\n", "&= \\prod_{t=1}^{T} P(x_t \\mid x_{1:t-1}, W) \\\\\n", "&= P(x_1 \\mid W) \\, \\cdot \\,\n", " P(x_2 \\mid x_1, W) \\, \\cdot \\,\n", " P(x_3 \\mid x_{1:2}, W) \\,\n", " \\cdots \\,\n", " P(x_T \\mid x_{1:T-1}, W)\n", "\\end{align}" ] }, { "cell_type": "markdown", "id": "45e62b13-e543-43c0-b926-709ab36228a5", "metadata": {}, "source": [ "- Where $W$ are the model parameters (the learned weights), $x$ is the sequence of tokens, and $T$ is the number of tokens\n", "- The numbers below, because they are so close to zero, will slightly differ on CPU, MPS, and CUDA devices" ] }, { "cell_type": "markdown", "id": "8a642a96-54c6-403f-af07-31bd4af12282", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 11, "id": "b3dea8f8-7a06-4ec5-b6c9-b3e7206da56e", "metadata": {}, "outputs": [], "source": [ "torch.set_printoptions(precision=4, sci_mode=True)" ] }, { "cell_type": "code", "execution_count": 12, "id": "9eea4853-e69c-4a52-b7f6-2b66754237c4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token probabilities: tensor([6.1512e-05, 4.6484e-01, 1.6724e-02, 7.3828e-01, 1.6895e-01],\n", " dtype=torch.bfloat16)\n", "Joint probability: tensor(5.9372e-08, dtype=torch.bfloat16)\n" ] } ], "source": [ "calc_next_token_probas(\n", " model, tokenizer, device=device,\n", " prompt=\"The capital of Germany is Berlin\"\n", " )" ] }, { "cell_type": "code", "execution_count": 13, "id": "342d13b0-a500-465b-850f-fc6e0814e478", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token probabilities: tensor([6.1512e-05, 4.6484e-01, 1.6724e-02, 7.3828e-01, 2.9802e-07],\n", " dtype=torch.bfloat16)\n", "Joint probability: tensor(1.0481e-13, dtype=torch.bfloat16)\n" ] } ], "source": [ "calc_next_token_probas(\n", " model, tokenizer, device=device,\n", " prompt=\"The capital of Germany is Bridge\"\n", " )" ] }, { "cell_type": "code", "execution_count": 14, "id": "045f3686-6481-42f8-ae63-18619d92f2a7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token probabilities: tensor([6.1512e-05, 4.6484e-01, 1.6724e-02, 7.3828e-01, 2.9297e-02],\n", " dtype=torch.bfloat16)\n", "Joint probability: tensor(1.0303e-08, dtype=torch.bfloat16)\n" ] } ], "source": [ "calc_next_token_probas(\n", " model, tokenizer, device=device,\n", " prompt=\"The capital of Germany is Hamburg\"\n", " )" ] }, { "cell_type": "markdown", "id": "08ab63a6-92f5-4671-ad75-d8586c8f1ff8", "metadata": {}, "source": [ " \n", "## 5.5 From token probability scores to log-probabilities" ] }, { "cell_type": "markdown", "id": "cd600967-1a96-4536-859e-a7f855ce2f25", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "7d7b6460-c632-4116-a0a3-11e785f5d806", "metadata": {}, "source": [ "- We introduce one more concept, log-probabilities (short: logprobs), they are similar to the probabilities we compute via the softmax function in the previous section, but we simply take the log" ] }, { "cell_type": "code", "execution_count": 15, "id": "7b6942d9-44f4-4fcc-a54e-fd4a843e0ad9", "metadata": {}, "outputs": [], "source": [ "torch.set_printoptions(precision=4, sci_mode=False)" ] }, { "cell_type": "code", "execution_count": 16, "id": "d22f7bc4-b57f-45e4-86ad-172c35f1efd8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([0.0090, 0.0175, 0.0341, 0.0665, 0.1295, 0.2522, 0.4912])\n" ] } ], "source": [ "logits = torch.linspace(-2, 2, steps=7)\n", "probas = torch.softmax(logits, dim=-1)\n", "print(probas)" ] }, { "cell_type": "code", "execution_count": 17, "id": "c9d7f0c6-7c01-4b1a-a35f-f4929683c6e2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([-4.7109, -4.0442, -3.3776, -2.7109, -2.0442, -1.3776, -0.7109])\n" ] } ], "source": [ "print(torch.log(probas))" ] }, { "cell_type": "code", "execution_count": 18, "id": "72228191-9b2f-471f-9291-675c01148964", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([-4.7109, -4.0442, -3.3776, -2.7109, -2.0442, -1.3776, -0.7109])\n" ] } ], "source": [ "log_probas = torch.log_softmax(logits, dim=-1)\n", "print(log_probas)" ] }, { "cell_type": "code", "execution_count": 19, "id": "2b9bdd12-9551-4dfd-ac3f-8534d1290f7d", "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3kAAAGGCAYAAADGq0gwAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZGZJREFUeJzt3QeYU9XWxvE1DDCg9N6bqIjSpAmooKBgx8JVLBQVFMECWMCrICoiVlQQRKVYENRrL6DSLIBIs1wBQdBREAZFuhSHfM+7/TI305OZZDI5+f+eJ8zk5OS0kD1n7bJ2gs/n8xkAAAAAwBOKRPsAAAAAAADhQ5AHAAAAAB5CkAcAAAAAHkKQBwAAAAAeQpAHAAAAAB5CkAcAAAAAHkKQBwAAAAAeQpAHAAAAAB5CkAcAAAAAHkKQh7jVqVMn9wBQuCxYsMASEhLs9ddft8Jg9uzZ1rx5cytRooQ7rh07dlisWbp0qRUvXtx+/vnnAi0D77nnHnfNwuWyyy6zf/3rX2HbHrwtv2XJtGnT3Pt/+ukn84q///7bbr/9dqtdu7YVKVLEunfvbrHohhtusDPOOCPtuT4jfVb6zCKpXr161qdPn7Bs648//rAjjzzSPvjgA4sEgjwUCv6CdNmyZVE7hs2bN7sbklWrVkXtGIDCYtGiRe77EIsBTTjpj7CCipIlS9qECRPsxRdfdH+UH3jgAXvrrbcsVvz73/+2nj17Wt26daN9KPm6dnfccYf95z//sa+//jrsx4XIoCwpXKZMmWIPP/ywXXLJJTZ9+nQbPHiwff/99668j5VgduPGjfbcc8/ZnXfeGe1Dsfxcu4oVK9q1115rd999d0SOjSAPceujjz5yj8Agb9SoUQR5wP/fmOn7EO9B3ldffWW7d++2++67z6655hq78sorrVixYjEV5Kni6pNPPrHrr7++wPd911132V9//ZVuWX6uXYsWLaxVq1b26KOPhukIEWmUJYXLvHnzrGbNmvb444/bVVddZR07dnSBisr7WAnynnjiCatfv76ddtppBb7vtWvX2rPPPpv2PL/XTuXyihUr3OcSbgR5iFvquqQHgMjz+XyZbvZjQUpKivtZrlw5i1VTp061OnXq2EknnVTg+y5atKjr5hpOall94403bM+ePWHdLmJDrJYlhalMi+Xy7NChQ/byyy9Hrdt2UlKSq+gLl+OOO85OOOGEiHQzJchDzFi5cqWdddZZVqZMGStVqpR17tzZlixZkmm9b775xtVMqXtVrVq17P7773c3ORn71QeOR1G//datW7vf+/bt69YN7Nu9bt06u/jii61atWruhkXb1diQnTt3Ftj5AwVFXU9uu+0297tqS/3fB31/NJ5DrVpHHXWU+2On8QnqMnPgwIF029Dyc8891+bMmeNaXvR9fOaZZ9xrah1UFyGto23o+9SrVy/7/fff023j8OHDNnr0aPe6vnf6zq9fvz7X41fL2y233JK2/SpVqrixG6otDfTaa69Zy5Yt3bFVqlTJtdJt2rQp7XWVD71793a/q3zQNdBYDP3cu3ev6+rkvzb+MRr+MWg//PCD217ZsmWtcuXKrjuObk5/+eUXu+CCC1w5pvIkY4vUwYMHbcSIEe649F51DT3llFNs/vz56dYbOXKkG08zd+7cdMv79+/vKq8CuzOq1ez0008PamycbgDVYlm1alV3zZs1a+bOM6turGoF0HnohlHXSfvMOCYm45i8nK5dsJ+blmkbH3/8ca7ng+iK9bIkO08//bQdf/zxbp81atSwgQMHZtnrQV28GzRo4I65TZs29tlnn+VpLKyGsnTt2tWVU9qWruXVV1+dbh19J4YOHerG2um4jj32WHvkkUdcuRM4Zk1lyX//+9909zk9evRw66hlzL9c90WB11/P/de/SZMmaa+rwkXPdV1VbuleLeM9mb7jug5aR+Wejl1liJ+C9kaNGrlHYAC/fft2q169urVv395SU1Pdss8//9x9vl26dAnq2s2bN8+VoSpLVVap/F29enWm9fznp2PU/0n9H8tqTHHgmLzcrl0wn5u/THv33XfTPqtwKRrWrQERogJJX1LdUGjAsGpR9AVUQblw4UJr27atW083aP4v2vDhw92XWv22VeDlVpNy7733upsr3SRpX6KCRTdd+pLqD8+NN97oCijt57333nOFum7EAC+56KKLXJDyyiuvuC49+gMlClY0fkA36BrPoRuKL7/80saMGeP+aL755puZurVoHNh1111n/fr1czcdan3R90vr64/diSee6P5gv/POO/brr7+m7UsefPBBF8jceuutrkLloYcesiuuuMLtM7fuL0q0MGjQIGvcuLG7mdCNgfap/fn/OKtCR8Gbjn/r1q2uC9AXX3zhblJ0M6BxbDrmyZMnu/JBf6D1x183F7oOumlTeSFaHujSSy915YrO4f3333eVTRUqVHDllgKusWPHutponZuO4dRTT3Xv27VrlyuzdN10zRT4PP/8864MUvIUJYDxd4PUTYECsm+//dZKly7tboLVjUg3zgrORGVVcnJy2nnnRDdXKlN186trp/NVIKwbGpV1N998c9oN83nnneeOZ8CAAe7G7O23304LiHOiMY3ZXbtgPjfRa7ph0md14YUX5rpPRE+slyVZ0Y2/uuepHND/fx3bxIkTXddu/Z/0t/Jomf4v6xgViCrIUpKT8uXLu2AzWKp4OfPMM901GzZsmCubtC0FV34KDs4//3wXwKlMUDmh8kABtsoAXXu9X98/Bbu6drrWcvTRR9tNN91kTz75pAuyVW6J/6eoTLj88svd9VfllYJHlQGTJk1y71ESFNE21cKma6LrLaqM2bBhgytvdf+k+zmVqfqpinrdr+n7rP8LHTp0cOXuY4895t6r4Fmfl8rrxMTEtO6/eo+6bufmk08+cY0DCjD1uamMe+qpp9x+VHmkgE1U5nfr1s0FlPpsFVCqzNc1y4nK7eyuXTCfm5+CY31GuiZq1QsbH1AITJ06VdUXvq+++irL17t37+4rXry478cff0xbtnnzZl/p0qV9p556atqyG2+80ZeQkOBbuXJl2rI//vjDV6FCBbf9jRs3pi3v2LGje/hp31pHxxJI29Ly1157LWznCxR2Dz/8cKbvzKpVq9yya6+9Nt26t956q1s+b968tGV169Z1y2bPnp1u3REjRrjlb7zxRqZ9Hj582P2cP3++W+e4447zHThwIO31J554wi3/9ttvczz2smXL+gYOHJjt6wcPHvRVqVLFd8IJJ/j++uuvtOXvvfee276OMbey6cgjj/T17t0707ZHjhzp1u/fv3/asr///ttXq1YtVzY9+OCDacv//PNPX8mSJdNtR+sGnrN/vapVq/quvvrqdMt1HVQu6vPQOjVr1vS1atXKd+jQobR1PvnkE3c87777bqZjzVgGjhs3zq370ksvpbtW7dq185UqVcq3a9cut+w///mPW0/r+6WmpvpOP/30TGWo/3oEc+1y+9wCHXPMMb6zzjorqHURXbFclvi///5jT0lJcd+5M8880/2f9xs/frxbb8qUKe659lWxYkVf69at030fp02b5tYL/N7l5s0338zx/kje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ASBafvnll7SW/0hR+Zfb2D0A3kBHbACeV7ZsWfdQTXXp0qXTnuuhLkr9+/e3l156KdqHCSDOaDzw3Xff7cqievXquYd+11Qu/i6dAJAXtOQB8DwlFxDdQN16660hd80EgEhQIqY33njDTeeiLt2yePFiu+eee1w38okTJ3LhAeQJQR6AuJHdxOMAEA2aWmXmzJl21llnpS1TciZ12ezZsydBHoA8I8gD4Gknnniim8OufPny1qJFixwz1q1YsaJAjw1AfNP4YPUwyKh+/frMoQkgXwjyAHjaBRdckJZoxT95OQAUBsrQqznz1KXcX04dOHDARo8enW32XgAIBkEegLjpokl3TQCFJduv3yeffOKmM2jWrJl7/vXXX9vBgwetc+fOUTpCAHEb5CkblOaB+vHHH+3yyy932eo2b95sZcqUsVKlSoX/KAEAADxA2TMDXXzxxemeR3IKBQDxI+Qg7+eff7Zu3bpZcnKy61JwxhlnuCBv7Nix7vmkSZMic6QAkAcai5fTOLxA27dv5xoDKJBsvwBQqIK8m2++2Vq1auW6E1SsWDFt+YUXXmj9+vUL9/EBQL6MGzeOKwgAAOJKyEHeZ599ZosWLcqU9UnZoTZt2hTOYwOAfOvduzdXEUCh9frrr9urr77qekhpLF4gMv4CyKsiob7h8OHDlpqammn5r7/+6rptAkBhsmvXrnS/5/QAgIL05JNPWt++fa1q1aq2cuVKa9OmjesltWHDhnRz5wFAxIO8M888M133J4112bNnj8tad/bZZ4d8AAAQ6TF5KSkp7vdy5cq55xkf/uUAUJCefvppmzx5sj311FOuh9Ttt99uH3/8sd100022c+dOPgwABddd89FHH7WuXbta48aNbf/+/S675rp166xSpUr2yiuv5P1IACAC5s2bZxUqVHC/z58/n2sMoNBQF8327du730uWLGm7d+92v1911VV20kkn2fjx46N8hABiVchBnuZyUdKVmTNn2jfffONa8a655hq74oorXAEFAIVJx44ds/wdAKKtWrVqLqtv3bp1rU6dOrZkyRI3X97GjRvN5/NF+/AAxNs8eUWLFrUrr7wy/EcDABH2559/2vPPP2+rV692z9UrQWNi/K19AFBQTj/9dHvnnXesRYsWrhwaPHiwS8SybNmyTJOmA0BEg7wXXnghx9d79eoV6iYBoEB8+umn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PvzwQ/v+++/dXHDly5eP9qEhCJRFlEVeQVkU2yiLKIu84s/CdF+k7JooWG3atPENHDgw7XlqaqqvRo0avjFjxnj+o0hJSVE2V9/ChQt9XrZ7927f0Ucf7fv44499HTt29N18880+L7rjjjt8J598crQPA3lEWURZ5BWURbGNsoiyyCvuKET3RbTkFbCDBw/a8uXLrUuXLmnLNGmyni9evNi8bufOne5nhQoVzMvUennOOeek+5y96J133rFWrVq5+d/UHbdFixb27LPPRvuwEATKIsoiL6Esil2URZRFXvJOIbovIsgrYL///rulpqZa1apV0y3X8y1btpiXHT582I1NUzP2CSecYF41c+ZM1w1X4xG9bsOGDTZx4kQ7+uijbc6cOTZgwAC76aabbPr06dE+NOSCsoiyyEsoi2IXZRFlkZdsKET3RUULfI+IW2rd+u677+zzzz83r/rll1/s5ptvduMPlVTH6xS4q8bqgQcecM9VY6XPeNKkSda7d+9oHx6QJcoi76EsQiyiLPKew4XovoiWvAJWqVIlS0xMtK1bt6ZbrufVqlUzrxo0aJC99957Nn/+fKtVq5Z5lbriKoHOiSeeaEWLFnUPJZ958skn3e9qxfWS6tWrW+PGjdMtO+644yw5OTlqx4TgUBZRFnkJZVHsoiyiLPKS6oXovoggr4AVL17cWrZsaXPnzk0X9et5u3btzGt8Pp8L8N58802bN2+e1a9f37ysc+fO9u2339qqVavSHqrRueKKK9zvCvC9RF1vM06J8cMPP1jdunWjdkwIDmURZZGXUBbFLsoiyiIv6VCY7ouinfklHs2cOdOXlJTkmzZtmu/777/39e/f31euXDnfli1bfF4zYMAAX9myZX0LFizw/fbbb2mPffv2+eKFl7NrLl261Fe0aFHf6NGjfevWrfO9/PLLviOOOML30ksvRfvQEATKIsoir6Asim2URZRFXrG0EN0XEeRFyVNPPeWrU6eOr3jx4i518JIlS3xepHqErB5Tp071xQsvB3ny7rvv+k444QRXcdGoUSPf5MmTo31ICAFlEWWRV1AWxTbKIsoir3i3kNwXJeifgm8/BAAAAABEAmPyAAAAAMBDCPIAAAAAwEMI8gAAAADAQwjyAAAAAMBDCPIAAAAAwEMI8gAAAADAQwjyAAAAAMBDCPIAAAAAwEMI8hA1P/30kyUkJNiqVasKdL/16tWzcePG5WsbCxYscMe+Y8eOsB0XgOihPAJQGFAWIVwI8pAvCnRyetxzzz2F7gp/9dVX1r9//2gfBoAwozwCUBhQFqEwKBrtA0Bs++2339J+nzVrlo0YMcLWrl2btqxUqVJW2FSuXDnahwAgAiiPABQGlEUoDGjJQ75Uq1Yt7VG2bFlXe+V/XqVKFXvsscesVq1alpSUZM2bN7fZs2dnu63U1FS7+uqrrVGjRpacnOyWvf3223biiSdaiRIlrEGDBjZq1Cj7+++/096j/T333HN24YUX2hFHHGFHH320vfPOOyF11wxmGx988IEdc8wxVrJkSTvttNNcd4qMPv/8czvllFPcOrVr17abbrrJ9u7d61574YUXXMC7bt26tPVvuOEGd6779u0L6loDyBnlEeURUBhQFv0P90ZR5APCZOrUqb6yZcumPX/sscd8ZcqU8b3yyiu+NWvW+G6//XZfsWLFfD/88IN7fePGjT79F1y5cqVv//79vgsvvNDXokULX0pKinv9008/de+fNm2a78cff/R99NFHvnr16vnuueeetH3o/bVq1fLNmDHDt27dOt9NN93kK1WqlO+PP/7I9jjr1q3re/zxx4PeRnJysi8pKck3ZMgQdx4vvfSSr2rVqu59f/75p1tn/fr1viOPPNJtV+f3xRdfuHPp06dP2n569Ojha926te/QoUO+9957z12LZcuW8f8PiADKI8ojoDCgLKIsihaCPESsIKtRo4Zv9OjR6dZRkHPDDTekC/I+++wzX+fOnX0nn3yyb8eOHWnratkDDzyQ7v0vvviir3r16v/7D2zmu+uuu9Ke79mzxy378MMPQwryctrG8OHDfY0bN063jTvuuCNdkHfNNdf4+vfvn24dnVeRIkV8f/31l3u+fft2F0wOGDDABYkZrw2A8KE8ojwCCgPKIsqiaGFMHiJi165dtnnzZuvQoUO65Xr+9ddfp1vWs2dP16Vz3rx5rqujn9b74osvbPTo0em6dO7fv991cVTXSmnatGna60ceeaSVKVPGUlJSQjrenLaxevVqa9u2bbr127Vrl+65jvWbb76xl19+OW2Z4sfDhw/bxo0b7bjjjrPy5cvb888/b127drX27dvbsGHDQjpGAHlDeUR5BBQGlEWURQWJIA9Rd/bZZ9tLL71kixcvttNPPz1t+Z49e9wYvIsuuijTezRGz69YsWLpXtMYOwVXocjvNnSs1113nRuHl1GdOnXSfv/0008tMTHRDcrWeL3SpUuHdJwAIovyCEBhQFmE/CLxCiJCLWE1atRwLXGB9Lxx48bplg0YMMAefPBBO//8823hwoVpy5VwRZk6GzZsmOlRpEjB/ddVK9zSpUvTLVuyZEm65zrW77//PstjLV68uFtn0aJFNnbsWHv33XddEpZBgwYV2DkA8YzyiPIIKAwoiyiLChIteYiY2267zUaOHGlHHXWUy6w5depUN/F5YJdGvxtvvNF1xTz33HPtww8/tJNPPtlNx6Dnagm75JJLXGCnbpHfffed3X///QX2yV1//fX26KOPuvO59tprbfny5TZt2rR069xxxx120kknucBN66jLp4K+jz/+2MaPH2+7d++2q666yrX0nXXWWa57auvWre28885z5wYgsiiPKI+AwoCyiLKowERtNCA8P7g4NTXVZcKsWbOmyyTZrFmzdAlRArNr+j366KO+0qVLu+yUMnv2bF/79u19JUuWdJk227Rp45s8eXLa+nr/m2++me44dAw6llASr+S2jXfffdfXsGFDl2XzlFNO8U2ZMiVd4hVZunSp74wzznCZOZVps2nTpmnJVfr27etr0qSJyyIaeK4VKlTw/frrr0FdXwDBozyiPAIKA8oiyqJoSdA/BRdSAgAAAAAiiTF5AAAAAOAhBHkAAAAA4CEEeQAAAADgIQR5AAAAAOAhBHkAAAAA4CEEeQAAAADgIQR5AAAAAOAhBHkAAAAA4CEEeQAAAADgIQR5AAAAAOAhBHkAAAAA4CEEeQAAAABg3vF/sdFwiz2eNLAAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(figsize=(9, 4))\n", "\n", "# Logits\n", "plt.subplot(1, 3, 1)\n", "plt.bar(range(len(logits)), logits, color=\"C0\", alpha=0.7)\n", "plt.title(\"Logits\")\n", "plt.xlabel(\"Token index\")\n", "plt.ylabel(\"Value\")\n", "plt.grid(alpha=0.3)\n", "\n", "# Softmax\n", "plt.subplot(1, 3, 2)\n", "plt.bar(range(len(probas)), probas, color=\"C1\", alpha=0.7)\n", "plt.title(\"torch.softmax(logits)\")\n", "plt.xlabel(\"Token index\")\n", "plt.ylabel(\"Probability\")\n", "plt.ylim(0, 1)\n", "plt.grid(alpha=0.3)\n", "\n", "# Log-softmax\n", "plt.subplot(1, 3, 3)\n", "plt.bar(range(len(log_probas)), log_probas, color=\"C2\", alpha=0.7)\n", "plt.title(\"torch.log_softmax(logits)\")\n", "plt.xlabel(\"Token index\")\n", "plt.ylabel(\"Log-probability\")\n", "plt.grid(alpha=0.3)\n", "\n", "plt.tight_layout()\n", "plt.savefig(\"logits_softmax_log_softmax.pdf\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "c2c1f1b6-6456-449d-a3f5-0e6c824d59b6", "metadata": {}, "source": [ "- The logprobs are in a more reasonable range for numerically stable computing" ] }, { "cell_type": "markdown", "id": "7bd0f658-31f3-478d-bb66-58b2b180120e", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "e9267cce-abf1-401c-b6ca-bf7466397cb5", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "21b1ceeb-7282-41e1-a190-c597efae8506", "metadata": {}, "source": [ "\\begin{align}\n", "\\log P(x_1, x_2, \\dots, x_T \\mid W)\n", "&= \\log \\prod_{t=1}^{T} P(x_t \\mid x_{1:t-1}, W) \\\\\n", "&= \\sum_{t=1}^{T} \\log P(x_t \\mid x_{1:t-1}, W) \\\\\n", "&= \\log P(x_1 \\mid W)\n", " + \\log P(x_2 \\mid x_1, W)\n", " + \\log P(x_3 \\mid x_{1:2}, W)\n", " + \\cdots\n", " + \\log P(x_T \\mid x_{1:T-1}, W).\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 20, "id": "5e141a1d-817c-43b5-9906-8e87ee43e290", "metadata": {}, "outputs": [], "source": [ "@torch.inference_mode()\n", "def calc_next_token_logprobas(model, tokenizer, prompt, device, show=True):\n", "\n", " token_ids = torch.tensor(tokenizer.encode(prompt), device=device)\n", "\n", " logits = model(token_ids.unsqueeze(0)).squeeze(0)\n", " # We now use log_softmax\n", " all_logprobas = torch.log_softmax(logits, dim=-1)\n", "\n", " t_idx = torch.arange(0, token_ids.shape[0] - 1, device=device)\n", " next_ids = token_ids[1:]\n", " next_token_logprobas = all_logprobas[t_idx, next_ids]\n", "\n", " # We replace the product with a sum\n", " sum_next_token_logprobas = torch.sum(next_token_logprobas)\n", "\n", " if show:\n", " print(\"Next-token log-probabilities:\", next_token_logprobas)\n", " print(\"Joint log-probability:\", sum_next_token_logprobas)\n", " else:\n", " return next_token_logprobas, sum_next_token_logprobas" ] }, { "cell_type": "code", "execution_count": 21, "id": "0e02eafa-e934-4bcf-ba51-7c07b002ea08", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token log-probabilities: tensor([-9.6875, -0.7695, -4.0938, -0.3008, -1.7812], dtype=torch.bfloat16)\n", "Joint log-probability: tensor(-16.6250, dtype=torch.bfloat16)\n" ] } ], "source": [ "calc_next_token_logprobas(\n", " model, tokenizer, device=device,\n", " prompt=\"The capital of Germany is Berlin\"\n", " )" ] }, { "cell_type": "code", "execution_count": 22, "id": "762b1da0-f385-4376-a3cb-3267409a1fce", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token log-probabilities: tensor([ -9.6875, -0.7695, -4.0938, -0.3008, -15.0000],\n", " dtype=torch.bfloat16)\n", "Joint log-probability: tensor(-29.8750, dtype=torch.bfloat16)\n" ] } ], "source": [ "calc_next_token_logprobas(\n", " model, tokenizer, device=device,\n", " prompt=\"The capital of Germany is Bridge\"\n", " )" ] }, { "cell_type": "code", "execution_count": 23, "id": "0c123dea-3e4d-48d3-8126-e24220783cce", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token log-probabilities: tensor([-9.6875, -0.7695, -4.0938, -0.3008, -3.5312], dtype=torch.bfloat16)\n", "Joint log-probability: tensor(-18.3750, dtype=torch.bfloat16)\n" ] } ], "source": [ "calc_next_token_logprobas(\n", " model, tokenizer, device=device,\n", " prompt=\"The capital of Germany is Hamburg\"\n", " )" ] }, { "cell_type": "markdown", "id": "c08c4ad5-d7f8-48c7-88c7-4afa73afb68b", "metadata": {}, "source": [ " \n", "## 5.6 Scoring model confidence with log-probabilities" ] }, { "cell_type": "markdown", "id": "3018242b-85dd-48eb-ad0d-4e865880fe87", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "d5a923d9-d209-429b-9734-8c702b0fe007", "metadata": {}, "source": [ "- We turn the logprob scoring into a scoring function that we can use, similar to the heuristic score\n", "- We make a few tweaks compared to the previous section: (1) we only score the answer, not the prompt; (2) we average over the token scores" ] }, { "cell_type": "markdown", "id": "0d9a012f-4dc5-466b-a7a9-552607b66408", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 24, "id": "76f5883b-5f58-4a54-8b82-dbea3b0bda4c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Next-token logprobas: tensor([-0.4512, -0.3418, -8.3125, -0.3906, -3.8125, -3.0469, -1.1719, 0.0000,\n", " -0.0155, 0.0000, -0.0078, -0.0752, -0.1582], dtype=torch.bfloat16)\n", "Joint log-probability: tensor(-17.7500, dtype=torch.bfloat16)\n" ] } ], "source": [ "example_prompt = \"What is the capital of Germany?\"\n", "example_answer = \" The capital of Germany is Berlin.\"\n", "\n", "next_token_logprobas, sum_next_token_logprobas = calc_next_token_logprobas(\n", " model, tokenizer, device=device,\n", " prompt=example_prompt+example_answer,\n", " show=False\n", " )\n", "\n", "print(\"Next-token logprobas:\", next_token_logprobas)\n", "print(\"Joint log-probability:\", sum_next_token_logprobas)" ] }, { "cell_type": "code", "execution_count": 25, "id": "93c0c901-b98c-454c-95d9-73c4c1cbf4d0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "7\n" ] } ], "source": [ "print(len(tokenizer.encode(example_answer)))" ] }, { "cell_type": "code", "execution_count": 26, "id": "b538562a-665c-40ac-94cd-a4b8c2bed7fe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor([-1.1719, 0.0000, -0.0155, 0.0000, -0.0078, -0.0752, -0.1582],\n", " dtype=torch.bfloat16)\n" ] } ], "source": [ "last_7 = next_token_logprobas[-7:]\n", "print(last_7)" ] }, { "cell_type": "code", "execution_count": 27, "id": "a7a9ec60-a111-46f0-a5ec-fea25905f135", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor(-0.2041, dtype=torch.bfloat16)\n" ] } ], "source": [ "print(torch.mean(last_7))" ] }, { "cell_type": "code", "execution_count": 28, "id": "dcf92c17-f78f-4fc8-badc-a55b9edbd5f0", "metadata": {}, "outputs": [], "source": [ "@torch.inference_mode()\n", "def avg_logprob_answer(model, tokenizer, prompt, answer, device=\"cpu\"):\n", "\n", " # Encode prompt and answer tokens separately to get the prompt length later\n", " prompt_ids = tokenizer.encode(prompt)\n", " answer_ids = tokenizer.encode(answer)\n", " full_ids = torch.tensor(prompt_ids + answer_ids, device=device)\n", "\n", " # Same as in calc_next_token_logprobas before\n", " logits = model(full_ids.unsqueeze(0)).squeeze(0)\n", " logprobs = torch.log_softmax(logits, dim=-1)\n", "\n", " # Index range for positions corresponding to answer tokens\n", " start = len(prompt_ids) - 1\n", " end = full_ids.shape[0] - 1\n", "\n", " # Same as before, except for using start and end\n", " t_idx = torch.arange(start, end, device=device)\n", " next_tokens = full_ids[start + 1 : end + 1]\n", " next_token_logps = logprobs[t_idx, next_tokens]\n", "\n", " # Average over the answer token scores\n", " return torch.mean(next_token_logps)" ] }, { "cell_type": "code", "execution_count": 29, "id": "8f988be1-5720-4094-ad1d-8827500d1bce", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor(-0.2041, dtype=torch.bfloat16)\n" ] } ], "source": [ "score_1 = avg_logprob_answer(\n", " model, tokenizer,\n", " prompt=\"What is the capital of Germany?\",\n", " answer=\" The capital of Germany is Berlin.\",\n", " device=device\n", ")\n", "print(score_1)" ] }, { "cell_type": "code", "execution_count": 30, "id": "76122c57-1139-4a4f-95c1-f152f49d40c8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor(-3.8906, dtype=torch.bfloat16)\n" ] } ], "source": [ "score_2 = avg_logprob_answer(\n", " model, tokenizer,\n", " prompt=\"What is the capital of Germany?\",\n", " answer=\" The capital of Germany is Bridge.\",\n", " device=device\n", ")\n", "print(score_2)" ] }, { "cell_type": "code", "execution_count": 31, "id": "6b51a328-95ff-46e0-a1fa-595773427a6b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(-0.0898, dtype=torch.bfloat16)" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "avg_logprob_answer(\n", " model, tokenizer,\n", " prompt=prompt_cot,\n", " answer=response_1,\n", " device=device\n", ")" ] }, { "cell_type": "code", "execution_count": 32, "id": "0ba4e002-d706-4cbb-ab51-2e12ea58bf8d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(-0.1094, dtype=torch.bfloat16)" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "avg_logprob_answer(\n", " model, tokenizer,\n", " prompt=prompt_cot,\n", " answer=response_2,\n", " device=device\n", ")" ] }, { "cell_type": "markdown", "id": "504c5c8a-b105-495a-92f4-6a41226a28ab", "metadata": {}, "source": [ " \n", "## 5.7 Self-refinement through iterative feedback" ] }, { "cell_type": "markdown", "id": "b834b691-dc6a-49ae-b14b-b4efe55381b6", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "8fb0dcd5-88e2-4d8c-92be-29758c42249d", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 33, "id": "09fcc5b8-a08d-492e-81e0-03101f20fc7f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \\boxed{18}" ] } ], "source": [ "raw_prompt = (\n", " \"Half the value of $3x-9$ is $x+37$. \"\n", " \"What is the value of $x$?\"\n", ")\n", "prompt = render_prompt(raw_prompt)\n", "\n", "torch.manual_seed(123)\n", "initial_response = generate_text_stream_concat_flex(\n", " model, tokenizer, prompt, device,\n", " max_new_tokens=2048, verbose=True,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=0.7,\n", " top_p=0.9, \n", ")" ] }, { "cell_type": "code", "execution_count": 34, "id": "e5eb5a2f-4916-4023-870b-c3ea89bdbc1c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " The question seems to have a logical error in its setup. The statement \"Half the value of $3x-9$ is $x+37$\" is incorrect because half of $3x-9$ should be $(3x-9)/2$, not $3x-9$. The equation should be $\\frac{1}{2}(3x-9) = x + 37$.\n", "\n", "Fix Plan:\n", "1. Correct the equation to $\\frac{1}{2}(3x-9) = x + 37$.\n", "2. Multiply both sides by 2 to eliminate the fraction: $3x - 9 = 2(x + 37)$.\n", "3. Distribute the 2 on the right side: $3x - 9 = 2x + 74$.\n", "4. Subtract $2x$ from both sides: $x - 9 = 74$.\n", "5. Add 9 to both sides: $x = 83$." ] } ], "source": [ "def make_critique_prompt(raw_prompt, draft):\n", " return (\n", " \"You are a meticulous reviewer. Identify logical errors, missing \"\n", " \"steps, or arithmetic mistakes. If the answer seems correct, \"\n", " \"say so briefly. Then propose a concise plan to fix issues.\\n\\n\"\n", " f\"Question:\\n{raw_prompt}\\n\\n\"\n", " f\"Draft answer:\\n{draft}\\n\\n\"\n", " \"Write a short critique and bullet-point fix plan \"\n", " \"(under ~120 words).\\n\"\n", " \"Critique:\"\n", " )\n", "\n", "\n", "critique_prompt = make_critique_prompt(raw_prompt, initial_response)\n", "torch.manual_seed(123)\n", "critique = generate_text_stream_concat_flex(\n", " model, tokenizer, critique_prompt, device,\n", " max_new_tokens=2048, verbose=True,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=0.7,\n", " top_p=0.9, \n", ")" ] }, { "cell_type": "code", "execution_count": 35, "id": "6215e5c7-8af1-4b72-b7cb-8e15b96d3d09", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \\boxed{83}\n", "\n", "Final result: The value of $x$ is \\boxed{83}." ] } ], "source": [ "def make_refine_prompt(raw_prompt, draft, critique):\n", " return (\n", " \"Revise the answer using the critique. Keep it concise and \"\n", " \"end with a final boxed result: \\\\boxed{ANSWER}\\n\\n\"\n", " f\"Question:\\n{raw_prompt}\\n\\n\"\n", " f\"Previous answer:\\n{draft}\\n\\n\"\n", " f\"Critique:\\n{critique}\\n\\n\"\n", " \"Revised answer:\"\n", " )\n", "\n", "refine_prompt = make_refine_prompt(raw_prompt, initial_response, critique)\n", "torch.manual_seed(123)\n", "revised_answer = generate_text_stream_concat_flex(\n", " model, tokenizer, refine_prompt, device,\n", " max_new_tokens=2048, verbose=True,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=0.7,\n", " top_p=0.9, \n", ")" ] }, { "cell_type": "markdown", "id": "11e5722c-b229-4d2c-bed5-a2677eefe336", "metadata": {}, "source": [ " \n", "## 5.8 Coding the self-refinement loop" ] }, { "cell_type": "markdown", "id": "d2c2c89c-e440-4bf9-8f1c-4fb978a2a13c", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 36, "id": "06d49baa-d5d3-48d9-8ba1-17bb0bf38204", "metadata": {}, "outputs": [], "source": [ "def self_refinement_loop(\n", " model,\n", " tokenizer,\n", " raw_prompt,\n", " device,\n", " iterations=2,\n", " max_response_tokens=2048,\n", " max_critique_tokens=256,\n", " score_fn=None,\n", " prompt_renderer=render_prompt,\n", " prompt_suffix=\"\",\n", " verbose=False,\n", " temperature=0.7,\n", " top_p=0.9,\n", "):\n", " steps = []\n", "\n", " # Initial response (draft)\n", " prompt = prompt_renderer(raw_prompt) + prompt_suffix\n", " current_full = generate_text_stream_concat_flex(\n", " model=model,\n", " tokenizer=tokenizer,\n", " prompt=prompt,\n", " device=device,\n", " max_new_tokens=max_response_tokens,\n", " verbose=False,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=temperature,\n", " top_p=top_p,\n", " )\n", "\n", " current_extracted = extract_final_candidate(\n", " current_full, fallback=\"number_then_full\"\n", " )\n", " if score_fn:\n", " current_score = score_fn(answer=current_full, prompt=prompt)\n", " else:\n", " current_score = 0.0\n", "\n", " # Run for one or more iterations\n", " for it in range(iterations):\n", " draft_before_full = current_full\n", " draft_before_extracted = current_extracted\n", " score_before = current_score\n", "\n", " # Critique the response\n", " critique_prompt = make_critique_prompt(\n", " raw_prompt, draft_before_full\n", " )\n", " critique_full = generate_text_stream_concat_flex(\n", " model=model,\n", " tokenizer=tokenizer,\n", " prompt=critique_prompt,\n", " device=device,\n", " max_new_tokens=max_critique_tokens,\n", " verbose=False,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=temperature,\n", " top_p=top_p,\n", " )\n", "\n", " # Refine the response\n", " refine_prompt = make_refine_prompt(\n", " raw_prompt, draft_before_full, critique_full\n", " )\n", " revised_full = generate_text_stream_concat_flex(\n", " model=model,\n", " tokenizer=tokenizer,\n", " prompt=refine_prompt,\n", " device=device,\n", " max_new_tokens=max_response_tokens,\n", " verbose=False,\n", " generate_func=generate_text_top_p_stream_cache,\n", " temperature=temperature,\n", " top_p=top_p,\n", " )\n", "\n", " revised_extracted = extract_final_candidate(\n", " revised_full, fallback=\"number_then_full\"\n", " )\n", " if score_fn:\n", " revised_score = score_fn(\n", " answer=revised_full, prompt=prompt # Still use original prompt here\n", " )\n", " else:\n", " revised_score = 0.0\n", " \n", " # Log the results\n", " step = {\n", " \"iteration\": it + 1,\n", " \"draft_full\": draft_before_full,\n", " \"draft_extracted\": draft_before_extracted,\n", " \"critique\": critique_full,\n", " \"revised_full\": revised_full,\n", " \"revised_extracted\": revised_extracted,\n", " \"score_before\": score_before,\n", " \"score_after\": revised_score,\n", " }\n", " steps.append(step)\n", "\n", " if verbose:\n", " print(\n", " f\"[Refinement {it+1}/{iterations}]\"\n", " f\"\\nCurrent: {draft_before_extracted}\"\n", " f\"\\nRevised: {revised_extracted}\"\n", " f\"\\nScore before: {score_before:.3f}\"\n", " f\"\\nScore after: {revised_score:.3f}\"\n", " f\"\\n{'=' * 25}\"\n", " )\n", "\n", " # Accept revised response if it's not worse\n", " if revised_score >= current_score:\n", " current_full = revised_full\n", " current_extracted = revised_extracted\n", " current_score = revised_score\n", "\n", " return {\n", " \"final_full\": current_full,\n", " \"final_extracted\": current_extracted,\n", " \"steps\": steps,\n", " }" ] }, { "cell_type": "code", "execution_count": 37, "id": "446f55db-b535-4822-b814-e4b91601f3e7", "metadata": {}, "outputs": [], "source": [ "from functools import partial\n", "\n", "avg_logprob_score = partial(\n", " avg_logprob_answer,\n", " model=model,\n", " tokenizer=tokenizer,\n", " device=device\n", ")" ] }, { "cell_type": "code", "execution_count": 38, "id": "5141b31a-90a3-4d8e-a146-5583b784b33d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Refinement 1/2]\n", "Current: 10\n", "Revised: 83\n", "Score before: -0.855\n", "Score after: -0.226\n", "=========================\n", "[Refinement 2/2]\n", "Current: 83\n", "Revised: 83\n", "Score before: -0.226\n", "Score after: -1.320\n", "=========================\n" ] } ], "source": [ "torch.manual_seed(1)\n", "\n", "results_logprob = self_refinement_loop(\n", " model=model,\n", " tokenizer=tokenizer,\n", " raw_prompt=raw_prompt,\n", " device=device,\n", " iterations=2,\n", " max_response_tokens=2048,\n", " max_critique_tokens=256,\n", " score_fn=avg_logprob_score,\n", " verbose=True,\n", " temperature=0.7,\n", " top_p=0.9,\n", ")" ] }, { "cell_type": "code", "execution_count": 39, "id": "f278c730-8ef9-463d-a62e-c2c033d4ae74", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "83\n" ] } ], "source": [ "print(results_logprob[\"final_extracted\"])" ] }, { "cell_type": "markdown", "id": "165b54e5-532c-4cc1-9ddc-5589cf0f90e8", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "id": "72781150-84c8-4c03-ba8f-ef90c8a233ef", "metadata": {}, "source": [ " \n", "## 5.9 Summary" ] }, { "cell_type": "markdown", "id": "b6393e16-78d9-431a-9198-74121e06945d", "metadata": {}, "source": [ "- No code in this section" ] } ], "metadata": { "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.16" } }, "nbformat": 4, "nbformat_minor": 5 }