Clémentine
commited on
Commit
·
a1e35e5
1
Parent(s):
3d13ae7
editing text
Browse files- app/src/content/article.mdx +19 -8
- app/src/content/chapters/automated-benchmarks/designing-your-automatic-evaluation.mdx +32 -0
- app/src/content/chapters/general-knowledge/model-inference-and-evaluation.mdx +57 -103
- app/src/content/chapters/general-knowledge/picking-your-evaluation.mdx +3 -3
- app/src/content/chapters/intro.mdx +25 -32
- app/src/content/chapters/troubleshooting/troubleshooting-inference.mdx +0 -81
- app/src/content/chapters/troubleshooting/troubleshooting-reproducibility.mdx +0 -2
- app/src/content/embeds/d3-tokenization-timeline.html +266 -0
- app/src/content/embeds/d3-tokenization.html +168 -0
app/src/content/article.mdx
CHANGED
|
@@ -24,7 +24,6 @@ import Intro from "./chapters/intro.mdx";
|
|
| 24 |
import DesigningAutomaticEvaluation from "./chapters/automated-benchmarks/designing-your-automatic-evaluation.mdx";
|
| 25 |
import PickingYourEval from "./chapters/general-knowledge/picking-your-evaluation.mdx";
|
| 26 |
import EvalsIn2025 from "./chapters/general-knowledge/2025-evaluations-for-useful-models.mdx"
|
| 27 |
-
import TroubleshootingInference from "./chapters/troubleshooting/troubleshooting-inference.mdx";
|
| 28 |
import TroubleshootingReproducibility from "./chapters/troubleshooting/troubleshooting-reproducibility.mdx";
|
| 29 |
import ModelInferenceAndEvaluation from "./chapters/general-knowledge/model-inference-and-evaluation.mdx";
|
| 30 |
|
|
@@ -32,19 +31,19 @@ import ModelInferenceAndEvaluation from "./chapters/general-knowledge/model-infe
|
|
| 32 |
|
| 33 |
## LLM basics to understand evaluation
|
| 34 |
|
| 35 |
-
Now that you have
|
|
|
|
| 36 |
|
| 37 |
<ModelInferenceAndEvaluation />
|
| 38 |
|
| 39 |
## Evaluating with existing benchmarks
|
| 40 |
|
|
|
|
|
|
|
| 41 |
### Benchmarks to know in 2025
|
| 42 |
|
| 43 |
<EvalsIn2025 />
|
| 44 |
|
| 45 |
-
### Selecting good benchmarks automatically for model training
|
| 46 |
-
|
| 47 |
-
<PickingYourEval />
|
| 48 |
|
| 49 |
### Understanding what's in there
|
| 50 |
|
|
@@ -66,11 +65,16 @@ In other words, is your dataset consistent?
|
|
| 66 |
#### Samples inspection
|
| 67 |
Take 50 random samples and manually inspect them; and I mean do it yourself, not "prompt an LLM to find unusual stuff in the data for you".
|
| 68 |
|
| 69 |
-
First, you want to check the content quality.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
Then, you want to check for relevance to your task. Are these questions the kind of questions you want to evaluate an LLM on? Are these examples relevant to your use case?
|
| 72 |
|
| 73 |
-
You might also want to check the samples consistency (especially if you're planning on using few shots or computing aggregated statistics): do all samples have the same number of choices if it's a multiple choice evaluation? Is the spacing consistent before and after the prompt? If your evaluation comes with an additional environment, ideally you want to use it to understand
|
| 74 |
|
| 75 |
Lastly, you also want to quickly check how many samples are present there (to make sure results are statistically significant - 100 samples is usually a minimum for automatic benchmarks).
|
| 76 |
|
|
@@ -84,16 +88,23 @@ You want to check what metrics are used: are they automatic, functional, or usin
|
|
| 84 |
|
| 85 |
<TroubleshootingReproducibility />
|
| 86 |
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
## Creating your own evaluation
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
<DesigningAutomaticEvaluation />
|
| 93 |
|
|
|
|
| 94 |
|
| 95 |
|
| 96 |
-
<TroubleshootingInference />
|
| 97 |
|
| 98 |
|
| 99 |
|
|
|
|
| 24 |
import DesigningAutomaticEvaluation from "./chapters/automated-benchmarks/designing-your-automatic-evaluation.mdx";
|
| 25 |
import PickingYourEval from "./chapters/general-knowledge/picking-your-evaluation.mdx";
|
| 26 |
import EvalsIn2025 from "./chapters/general-knowledge/2025-evaluations-for-useful-models.mdx"
|
|
|
|
| 27 |
import TroubleshootingReproducibility from "./chapters/troubleshooting/troubleshooting-reproducibility.mdx";
|
| 28 |
import ModelInferenceAndEvaluation from "./chapters/general-knowledge/model-inference-and-evaluation.mdx";
|
| 29 |
|
|
|
|
| 31 |
|
| 32 |
## LLM basics to understand evaluation
|
| 33 |
|
| 34 |
+
Now that you have a better (but broad) idea of why evaluation is important and how it's done, let's look at how we prompt models to get some answers out in order to evaluate them. You can skim this section if you have already done evaluation and mostly look for the notes and sidenotes.
|
| 35 |
+
|
| 36 |
|
| 37 |
<ModelInferenceAndEvaluation />
|
| 38 |
|
| 39 |
## Evaluating with existing benchmarks
|
| 40 |
|
| 41 |
+
Now that you've gotten (re)acquainted with required basics on how tokenization and inference work, and what are the caveats when doing evalution, let's look at actual benchmarking! We'll first do a small tour of 2025 evaluations, then discuss what to look at in a benchamrk, and why you probably can't reproduce announcements scores. Lastly, we'll cover the special case of selecting good benchmark to evaluate training with the FineWeb team
|
| 42 |
+
|
| 43 |
### Benchmarks to know in 2025
|
| 44 |
|
| 45 |
<EvalsIn2025 />
|
| 46 |
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
### Understanding what's in there
|
| 49 |
|
|
|
|
| 65 |
#### Samples inspection
|
| 66 |
Take 50 random samples and manually inspect them; and I mean do it yourself, not "prompt an LLM to find unusual stuff in the data for you".
|
| 67 |
|
| 68 |
+
First, you want to check the content quality.
|
| 69 |
+
- Are the prompts clear and unambiguous?
|
| 70 |
+
- Are the answers correct? (*Eg: TriviaQA contains several gold answers (aliases field) per question, sometimes conflicting.*)
|
| 71 |
+
- Is information missing? (*Eg: MMLU references absent schematics in a number of questions.*)
|
| 72 |
+
|
| 73 |
+
It's important to keep in mind that it's not because a dataset is a standard that it's a good one - and this happens because most people skip this step.
|
| 74 |
|
| 75 |
Then, you want to check for relevance to your task. Are these questions the kind of questions you want to evaluate an LLM on? Are these examples relevant to your use case?
|
| 76 |
|
| 77 |
+
You might also want to check the samples consistency (especially if you're planning on using few shots or computing aggregated statistics): do all samples have the same number of choices if it's a multiple choice evaluation? Is the spacing consistent before and after the prompt? If your evaluation comes with an additional environment, ideally you want to use it to understand what gets called.
|
| 78 |
|
| 79 |
Lastly, you also want to quickly check how many samples are present there (to make sure results are statistically significant - 100 samples is usually a minimum for automatic benchmarks).
|
| 80 |
|
|
|
|
| 88 |
|
| 89 |
<TroubleshootingReproducibility />
|
| 90 |
|
| 91 |
+
### Selecting good benchmarks automatically for model training
|
| 92 |
+
|
| 93 |
+
<PickingYourEval />
|
| 94 |
|
| 95 |
|
| 96 |
|
| 97 |
## Creating your own evaluation
|
| 98 |
|
| 99 |
+
At this stage, you likely have a good idea of why people do evaluation, which benchmarks exist and are relevant for different model stages (training, inference of base and tuned models), but what if nothing exists for your specific use case?
|
| 100 |
+
|
| 101 |
+
This is precisely when you could want to create your own evaluation.
|
| 102 |
+
|
| 103 |
<DesigningAutomaticEvaluation />
|
| 104 |
|
| 105 |
+
## Conclusion
|
| 106 |
|
| 107 |
|
|
|
|
| 108 |
|
| 109 |
|
| 110 |
|
app/src/content/chapters/automated-benchmarks/designing-your-automatic-evaluation.mdx
CHANGED
|
@@ -496,6 +496,38 @@ On the other hand they:
|
|
| 496 |
- Tracking of win rates or probabilities over training, e.g. as in [this](https://arxiv.org/abs/2410.11677v1) paper, can allow you to detect model degradation and select optimal checkpoints.
|
| 497 |
</Note>
|
| 498 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
### Calibration and confidence
|
| 500 |
|
| 501 |
When reporting evaluation results, it's critical to include **confidence intervals** alongside point estimates.
|
|
|
|
| 496 |
- Tracking of win rates or probabilities over training, e.g. as in [this](https://arxiv.org/abs/2410.11677v1) paper, can allow you to detect model degradation and select optimal checkpoints.
|
| 497 |
</Note>
|
| 498 |
|
| 499 |
+
### Constraining model outputs
|
| 500 |
+
In a number of cases, we might want the model to output a prediction which follows a very specific format to simplify evaluation.
|
| 501 |
+
|
| 502 |
+
#### Using a prompt
|
| 503 |
+
The easiest way to do this is to add a task prompt which contains very specific instructions as to how the model should answer (`Provide numerical answers in digits.`,`Use no abbreviation.`, etc).
|
| 504 |
+
|
| 505 |
+
It won't necessarily work all the time but should be good enough for high capability models. That's the approach we followed in the [GAIA](https://huggingface.co/papers/2311.12983) paper for example.
|
| 506 |
+
|
| 507 |
+
#### Few shots and in context learning
|
| 508 |
+
The next way to do so is to constrain the model through what is called "in context learning". By providing examples in the prompt (what is called `few-shot prompting`), the model is implicitly biased towards following the repeated prompt shape for the actual sample.
|
| 509 |
+
|
| 510 |
+
<Note>
|
| 511 |
+
It's a method which was overall working quite well until end of 2023!
|
| 512 |
+
|
| 513 |
+
However, the widespread adoption of instruction-tuning methods and the addition of instruction data in later stages of model pre-training (continuous pre-training) has biased more recent models towards specific output formats (what is being called [here](https://arxiv.org/abs/2407.07890) *Training on the test task*, and what I would call *overfitting the prompt format*). Reasoning models are also not playing that well with few shot examples because of the reasoning trace.
|
| 514 |
+
|
| 515 |
+
It's also a method which can be limited for older models with smaller context sizes, as some few-shot examples can not fit into the context window.
|
| 516 |
+
</Note>
|
| 517 |
+
|
| 518 |
+
#### Structured text generation
|
| 519 |
+
Structured text generation constrains the outputs to follow a given path, defined by a grammar or by regular expressions, for example. The `outlines` library implements this using finite state machines, which is very neat. (Other approaches exist, such as using interleaved generation for json generation, but the FSM one is my favorite).
|
| 520 |
+
|
| 521 |
+
To understand more about what happens when using structured generation, you can check the [blog](https://huggingface.co/blog/evaluation-structured-outputs) we wrote together: structured generation reduce prompt variance in evaluation, and make results and rankings more stable. You can also check the overall `outlines` [blog](https://blog.dottxt.co/) for interesting implementations and observations linked to structured generation.
|
| 522 |
+
|
| 523 |
+
However, some recent [research](https://arxiv.org/abs/2408.02442) seems to show that structured generation can lower model performance on some tasks (like reasoning), by moving the prior too far away from the expected probability distribution.
|
| 524 |
+
|
| 525 |
+
<Note title="Going further" emoji="📚" variant="warning">
|
| 526 |
+
- ⭐ [Understanding how Finite State Machine when using structured generation](https://blog.dottxt.co/coalescence.html), by Outlines. Super clear guide on how their method works!
|
| 527 |
+
- [The outlines method paper](https://arxiv.org/abs/2307.09702), a more academic explanation of the above
|
| 528 |
+
- [Interleaved generation](https://github.com/guidance-ai/guidance?tab=readme-ov-file#guidance-acceleration), another method to constrain generations for some specific output formats
|
| 529 |
+
</Note>
|
| 530 |
+
|
| 531 |
### Calibration and confidence
|
| 532 |
|
| 533 |
When reporting evaluation results, it's critical to include **confidence intervals** alongside point estimates.
|
app/src/content/chapters/general-knowledge/model-inference-and-evaluation.mdx
CHANGED
|
@@ -10,12 +10,16 @@ import Image from '../../../components/Image.astro';
|
|
| 10 |
import Note from "../../../components/Note.astro";
|
| 11 |
import Sidenote from "../../../components/Sidenote.astro";
|
| 12 |
import Accordion from "../../../components/Accordion.astro";
|
|
|
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
This is done in two steps.
|
| 17 |
### Tokenization
|
| 18 |
-
The input text (called a *prompt* at inference) is first split into *tokens*, small units of texts (which can be one or several characters, up to the word level) each associated with a number. The whole range of tokens a model can parse is called its *vocabulary*.
|
| 19 |
|
| 20 |
#### Basics of tokenization: Why and how do we tokenize text?
|
| 21 |
Since large language models are actually big mathematical functions, they eat numbers, not text.
|
|
@@ -31,63 +35,27 @@ Some people therefore had the idea to cut words into sub-words, and assign index
|
|
| 31 |
|
| 32 |
This was initially done using morpho-syntactic rules (*morpho-syntax* is like the grammar of word creation). Now most people use byte pair encoding (BPE), a smart statistical method to create the sub-words automatically depending on their frequency in a reference text.
|
| 33 |
|
| 34 |
-
So as a summary: tokenization is a way to map small units of texts (which can be one or several characters, up to the word level) to numbers (similar to an index). When you want to process text, your input text (called a *prompt* at inference) is split into these *tokens* by a tokenizer. The whole range of tokens a model or tokenizer can parse is called its *vocabulary*.
|
| 35 |
|
| 36 |
<Note title="Going further: Understanding tokenization" emoji="📚" variant="warning">
|
| 37 |
- ⭐ [Explanation of different tokenization methods in the 🤗 NLP Course](https://huggingface.co/learn/nlp-course/en/chapter2/4)
|
| 38 |
- ⭐ [Conceptual guide about tokenization in the 🤗 doc](https://huggingface.co/docs/transformers/en/tokenizer_summary)
|
| 39 |
-
- [Course by Jurafsky on tokenization (and other things)](https://web.stanford.edu/~jurafsky/slp3/2.pdf) -
|
| 40 |
</Note>
|
| 41 |
|
| 42 |
<Note title="Going further: Byte Pair Encoding" emoji="📚" variant="warning">
|
| 43 |
-
|
| 44 |
-
- [Paper introducing BPE to NLP](https://aclanthology.org/P16-1162/)
|
| 45 |
-
</Note>
|
| 46 |
-
|
| 47 |
-
#### Using your own tokenizer? Don't forget to consider the following
|
| 48 |
-
I recommend making sure you understand BPE before this section, see above for some references!
|
| 49 |
-
|
| 50 |
-
**Choosing the correct vocabulary size**
|
| 51 |
-
|
| 52 |
-
The size of the vocabulary indicates how many individual tokens (for example, sub-words) the model will have to learn. A vocabulary which is **too big** might contain some very rare words as full tokens (for example: `aardvark`), which can lead to 2 problems. If such a rare word almost never appears in the training data, it can be hard to connect to other concepts, and the model might be unable to infer what it is about. On the other hand, if it appears rarely and only in specific contexts, it can be linked to some very specific other words: for example, if you train on forum data, and your tokenizer mapped a username as one single token in its vocabulary, your model might then associate this token to the specific user's content.
|
| 53 |
-
|
| 54 |
-
A vocabulary which is **too small** will present 2 other problems: worst representation capabilities, and increased cost at inference.
|
| 55 |
-
|
| 56 |
-
Let's go back to our above example, where we tokenized words derived from `similar`. Using a pseudo BPE approach (large vocabulary) to tokenize `similarly` has split the word into 2 tokens (`similar`, `ly`). If we had used instead character level tokenization (therefore with a very small vocabulary, the size of an alphabet), the same word would be cut into 9 tokens (`s`, `i`, `m`, `i`, `l`, `a`, `r`, `l`, `y`). Where the first method splits `similarly` into tokens which have an individual semantic meaning, it's not the case in the second method: with too small a vocabulary, we lost some semantic representation. The difference in representations length also means that it's many times as costly to generate our word with a smaller vocabulary (takes 9 tokens instead of 2, so 5 times more costly!).
|
| 57 |
-
|
| 58 |
-
At the moment, most people seem to use heuristics for vocabulary size, which seems correlated to number of languages covered and model size, so it's likely that using a number of tokens close to the reference models of a similar size could work for you.
|
| 59 |
|
| 60 |
-
|
| 61 |
-
- [
|
| 62 |
-
- [Fishing for Magikarp, paper by Cohere](https://arxiv.org/abs/2405.05417): Follow up work on to detect these tokens
|
| 63 |
</Note>
|
| 64 |
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
When building or choosing your tokenizer, you construct your vocabulary from reference text. This means that your tokenizer will know vocabulary words and characters from this reference text. Usually, it means using data in English, with a Latin script.
|
| 68 |
-
|
| 69 |
-
If you want to add new language, and your new language uses the same script and share some roots, you could theoretically hope that some of your original language semantics transfer to the new language.
|
| 70 |
|
| 71 |
-
|
| 72 |
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
<iframe
|
| 76 |
-
src="https://OpenEvals-tokenizers-languages.hf.space"
|
| 77 |
-
frameborder="0"
|
| 78 |
-
width="850"
|
| 79 |
-
height="450"
|
| 80 |
-
></iframe>
|
| 81 |
|
| 82 |
-
<Note title="Going further: Language and tokenization" emoji="📚" variant="warning">
|
| 83 |
-
- ⭐ [A beautiful breakdown and demo by Yennie Jun on tokenization issues across languages](https://www.artfish.ai/p/all-languages-are-not-created-tokenized): The breakdown in itself is very clear, and the embedded space comes from her work.
|
| 84 |
-
- ⭐ [A demo by Aleksandar Petrov on unfairness of tokenization](https://aleksandarpetrov.github.io/tokenization-fairness/): I recommend looking at `Compare tokenization of sentences` to get a feel for the differences in cost of inference depending on languages
|
| 85 |
-
</Note>
|
| 86 |
-
|
| 87 |
-
**What about numbers?**
|
| 88 |
-
|
| 89 |
-
When building your tokenizer, you need to decide what to do about numbers. Do you only index 0 to 9, and assume all other numbers will be compositions of digits, or do you want to store numbers up to, say, one billion, individually? Current well known models display a range of approaches to this, but it's unclear what works better to allow mathematical reasoning. Maybe new approaches to tokenization, such as hierarchical tokenization, might be needed for this.
|
| 90 |
-
<Note title="Going further: Number tokenization" emoji="📚" variant="warning">
|
| 91 |
- ⭐ [A nice visual demo by Yennie Jun of how tokenizers of Anthropic, Meta, OpenAI, and Mistral models split numbers](https://www.artfish.ai/p/how-would-you-tokenize-or-break-down)
|
| 92 |
- [Small history by Beren Millidge of the evolution of number tokenization through the years](https://www.beren.io/2024-05-11-Integer-tokenization-is-now-much-less-insane/)
|
| 93 |
</Note>
|
|
@@ -95,14 +63,16 @@ When building your tokenizer, you need to decide what to do about numbers. Do yo
|
|
| 95 |
#### How tokenization can mess up your evaluation
|
| 96 |
**Managing fine-tuned models, system prompts and chat templates**
|
| 97 |
|
| 98 |
-
Pre-2022, models used to simply be pretrained: text in, text out, nothing else. Then, we got instruction tuning and chat models in 2023, and in 2025 reasoning models. This means that we went from using text
|
| 99 |
|
| 100 |
-
|
| 101 |
-
1. add their system prompt at the very beginning of inference
|
| 102 |
-
2. prompt them using a chat template if they require it (usually adding `Assistant` and `User` prefixes to the dialogue turns - learn more about this in [this cool guide](https://huggingface.co/docs/transformers/main/en/chat_templating))
|
| 103 |
-
3. remove the thinking trace from the model answer before processing it (you can usually regex to remove what's between the `<think>` tags)
|
| 104 |
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
<Note title="Critical: Chat templates and tokenization" emoji="⚡" variant="danger">
|
| 107 |
|
| 108 |
<Image src={chatTemplatesTokenisation} alt="Spacing, tokenization and template" />
|
|
@@ -110,40 +80,40 @@ This means a number of models are going to perform terribly if you do not make s
|
|
| 110 |
Different tokenizers behave differently with spacing and special tokens. See this [visualization](https://x.com/danielhanchen/status/1796952220619157694) showing how spacing, tokenization, and templates interact. Never assume tokenizers behave identically!
|
| 111 |
</Note>
|
| 112 |
|
| 113 |
-
**
|
| 114 |
-
|
| 115 |
-
When looking at an MCQA evaluation, in general, you want to tokenize the context together with the choices, as it creates a succession of tokens which is likely/natural for the model.
|
| 116 |
-
|
| 117 |
-
<Note title="Should you tokenize the context with the choices always?">
|
| 118 |
-
Some tokenizers (like the [Llama one](https://github.com/EleutherAI/lm-evaluation-harness/pull/531#issuecomment-1595586257)) do not satisfy `enc(context + choice) = enc(context) + enc(choice)` (and add or remove spacing). This means that comparing the logprobabilities of the choices only is not trivial, as the context tokens can "bleed out" into them, messing up the comparison.
|
| 119 |
|
| 120 |
-
|
| 121 |
|
| 122 |
-
|
| 123 |
-
If you tokenize the context with the choices, you compare `C1C2` (one token) with `C1+C3` (two tokens). Even if you normalize the logprobs by length, you are not comparing the same thing.
|
| 124 |
-
Comparing after tokenizing the context and choices separately means you compare `C1+C2` and `C1+C3`. But since `C1C2` is a token, the occurence of `C1+C2` is likely rare in the data your encoder saw, so it is an unlikely succession for your model, which can mess up your logprobabilities.
|
| 125 |
-
|
| 126 |
-
If this is the case for your model, the solution is usually to go for the least worst option, comparing the comparable: compute the tokens of context and choice separately and then concatenate them after removing the special start/end of sentence tokens which might have been added.
|
| 127 |
-
</Note>
|
| 128 |
|
|
|
|
| 129 |
|
| 130 |
-
|
| 131 |
|
| 132 |
-
|
| 133 |
|
| 134 |
-
|
| 135 |
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
-
|
| 139 |
|
| 140 |
-
**Code evaluations and end of sentence tokens**
|
| 141 |
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
|
| 145 |
### Inference
|
| 146 |
|
|
|
|
|
|
|
| 147 |
From this input text, the LLM generates a probability distribution of the most likely next tokens over all the vocabulary. To get a continued generation, we can take the most probable token (give or take some added randomness to get more interesting outputs) as the next one, then repeat the operation, using the new token as the end of the prompt, etc.
|
| 148 |
|
| 149 |
<Image src={llmTk1} alt="LLM tokenization and prediction process" />
|
|
@@ -177,6 +147,7 @@ This allows us to apply one of the following metrics:
|
|
| 177 |
To learn more about calibration, you can check [this paper](https://arxiv.org/abs/2207.05221) from Anthropic, on what it is, how to detect it, and how to train models to be well calibrated, and [this paper](https://arxiv.org/abs/2311.14648) on some possible limits of calibration).
|
| 178 |
</Sidenote>
|
| 179 |
|
|
|
|
| 180 |
<Note>
|
| 181 |
A multiple choice question answer can be expressed as a free form generative evaluation too! For this reason, you'll sometimes see a mention of the task **formulation**.
|
| 182 |
|
|
@@ -195,6 +166,20 @@ The point at which MMLU MCF becomes non-random depends on the model size and tra
|
|
| 195 |
|
| 196 |
</Note>
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
#### Generative evaluations
|
| 199 |
For a generative evaluation, we want the text generated by the model given an input prompt.
|
| 200 |
|
|
@@ -210,34 +195,3 @@ We can then compare this generation with references and score the distance betwe
|
|
| 210 |
</Note>
|
| 211 |
|
| 212 |
|
| 213 |
-
### Constraining model outputs
|
| 214 |
-
In a number of cases, we want the model output to follow a specific format, for example to compare them to a reference.
|
| 215 |
-
|
| 216 |
-
#### Using a prompt
|
| 217 |
-
The easiest way to do this is to add a task prompt which contains very specific instructions as to how the model should answer (`Provide numerical answers in digits.`,`Use no abbreviation.`, etc).
|
| 218 |
-
|
| 219 |
-
It won't necessarily work all the time but should be good enough for high capability models. That's the approach we followed in the [GAIA](https://huggingface.co/papers/2311.12983) paper for example.
|
| 220 |
-
|
| 221 |
-
#### Few shots and in context learning
|
| 222 |
-
The next way to do so is to constrain the model through what is called "in context learning". By providing examples in the prompt (what is called `few-shot prompting`), the model is implicitly biased towards following the repeated prompt shape for the actual sample.
|
| 223 |
-
|
| 224 |
-
<Note>
|
| 225 |
-
It's a method which was overall working quite well until end of 2023!
|
| 226 |
-
|
| 227 |
-
However, the widespread adoption of instruction-tuning methods and the addition of instruction data in later stages of model pre-training (continuous pre-training) has biased more recent models towards specific output formats (what is being called [here](https://arxiv.org/abs/2407.07890) *Training on the test task*, and what I would call *overfitting the prompt format*). Reasoning models are also not playing that well with few shot examples because of the reasoning trace.
|
| 228 |
-
|
| 229 |
-
It's also a method which can be limited for older models with smaller context sizes, as some few-shot examples can not fit into the context window.
|
| 230 |
-
</Note>
|
| 231 |
-
|
| 232 |
-
#### Structured text generation
|
| 233 |
-
Structured text generation constrains the outputs to follow a given path, defined by a grammar or by regular expressions, for example. The `outlines` library implements this using finite state machines, which is very neat. (Other approaches exist, such as using interleaved generation for json generation, but the FSM one is my favorite).
|
| 234 |
-
|
| 235 |
-
To understand more about what happens when using structured generation, you can check the [blog](https://huggingface.co/blog/evaluation-structured-outputs) we wrote together: structured generation reduce prompt variance in evaluation, and make results and rankings more stable. You can also check the overall `outlines` [blog](https://blog.dottxt.co/) for interesting implementations and observations linked to structured generation.
|
| 236 |
-
|
| 237 |
-
However, some recent [research](https://arxiv.org/abs/2408.02442) seems to show that structured generation can lower model performance on some tasks (like reasoning), by moving the prior too far away from the expected probability distribution.
|
| 238 |
-
|
| 239 |
-
<Note title="Going further" emoji="📚" variant="warning">
|
| 240 |
-
- ⭐ [Understanding how Finite State Machine when using structured generation](https://blog.dottxt.co/coalescence.html), by Outlines. Super clear guide on how their method works!
|
| 241 |
-
- [The outlines method paper](https://arxiv.org/abs/2307.09702), a more academic explanation of the above
|
| 242 |
-
- [Interleaved generation](https://github.com/guidance-ai/guidance?tab=readme-ov-file#guidance-acceleration), another method to constrain generations for some specific output formats
|
| 243 |
-
</Note>
|
|
|
|
| 10 |
import Note from "../../../components/Note.astro";
|
| 11 |
import Sidenote from "../../../components/Sidenote.astro";
|
| 12 |
import Accordion from "../../../components/Accordion.astro";
|
| 13 |
+
import HtmlEmbed from "../../../components/HtmlEmbed.astro";
|
| 14 |
|
| 15 |
+
In this section, we'll look at two steps for models: how input is preprocessed to be given to the model (`tokenization`), and how the model generates a prediction from it (`inference`).
|
| 16 |
+
|
| 17 |
+
<Sidenote> If you want to learn more about how to actually train a model, you should go read the [Smol Training Guidebook!](https://huggingface.co/spaces/HuggingFaceTB/smol-training-playbook)</Sidenote>
|
| 18 |
+
|
| 19 |
+
<HtmlEmbed src="d3-tokenization.html" frameless />
|
| 20 |
|
|
|
|
| 21 |
### Tokenization
|
| 22 |
+
The input text (called a *prompt* at inference) is first split into *tokens*, small units of texts (which can be one or several characters, up to the word level) each associated with a number. The whole range of tokens a model can parse is called its *vocabulary*.
|
| 23 |
|
| 24 |
#### Basics of tokenization: Why and how do we tokenize text?
|
| 25 |
Since large language models are actually big mathematical functions, they eat numbers, not text.
|
|
|
|
| 35 |
|
| 36 |
This was initially done using morpho-syntactic rules (*morpho-syntax* is like the grammar of word creation). Now most people use byte pair encoding (BPE), a smart statistical method to create the sub-words automatically depending on their frequency in a reference text.
|
| 37 |
|
| 38 |
+
So as a summary: tokenization is a way to map small units of texts (which can be one or several characters, up to the word level) to numbers (similar to an index). When you want to process text, your input text (called a *prompt* at inference) is split into these *tokens* by a tokenizer. The whole range of tokens a model or tokenizer can parse is called its *vocabulary*.
|
| 39 |
|
| 40 |
<Note title="Going further: Understanding tokenization" emoji="📚" variant="warning">
|
| 41 |
- ⭐ [Explanation of different tokenization methods in the 🤗 NLP Course](https://huggingface.co/learn/nlp-course/en/chapter2/4)
|
| 42 |
- ⭐ [Conceptual guide about tokenization in the 🤗 doc](https://huggingface.co/docs/transformers/en/tokenizer_summary)
|
| 43 |
+
- [Course by Jurafsky on tokenization (and other things)](https://web.stanford.edu/~jurafsky/slp3/2.pdf) - skip to 2.5 and 2.6
|
| 44 |
</Note>
|
| 45 |
|
| 46 |
<Note title="Going further: Byte Pair Encoding" emoji="📚" variant="warning">
|
| 47 |
+
I would strongly recommend reading a longer explanation on how BPE works, as it's really a base of modern LLMs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
- ⭐ [Explanation of BPE in the 🤗 NLP Course](https://huggingface.co/learn/nlp-course/en/chapter6/5)
|
| 50 |
+
- [BPE Paper (for text, as the method existed before in other fields)](https://aclanthology.org/P16-1162/)
|
|
|
|
| 51 |
</Note>
|
| 52 |
|
| 53 |
+
When building a tokenizer require making more choices than one would expect. For example, to tokenize numbers, you don't want to use a basic BPE, but do you only index 0 to 9, and assume all other numbers will be compositions of digits? Do you want to store numbers up to, say, one billion, individually?
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
+
Current well known models display a range of approaches to this, but it's unclear what works better to allow mathematical reasoning. This will affect some mathematical evaluation (and is the reason why almost no evaluation is pure arithmetics).
|
| 56 |
|
| 57 |
+
<Note title="Going further: Tokenizing numbers" emoji="📚" variant="warning">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
- ⭐ [A nice visual demo by Yennie Jun of how tokenizers of Anthropic, Meta, OpenAI, and Mistral models split numbers](https://www.artfish.ai/p/how-would-you-tokenize-or-break-down)
|
| 60 |
- [Small history by Beren Millidge of the evolution of number tokenization through the years](https://www.beren.io/2024-05-11-Integer-tokenization-is-now-much-less-insane/)
|
| 61 |
</Note>
|
|
|
|
| 63 |
#### How tokenization can mess up your evaluation
|
| 64 |
**Managing fine-tuned models, system prompts and chat templates**
|
| 65 |
|
| 66 |
+
Pre-2022, models used to simply be pretrained: text in, text out, nothing else. Then, we got instruction tuning and chat models in 2023, and in 2025 reasoning models. This means that we went from using raw text to using more and more formatting.
|
| 67 |
|
| 68 |
+
<HtmlEmbed src="d3-tokenization-timeline.html" frameless />
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
+
This means a number of models are going to perform terribly if you do not make sure to:
|
| 72 |
+
1. respect the format the model expectes
|
| 73 |
+
2. adds a system prompt at the very beginning of inference if your model requires one
|
| 74 |
+
3. remove the thinking trace from reasoning models answers before processing them (you can usually regex to remove what's between the `<think>` tags)
|
| 75 |
+
|
| 76 |
<Note title="Critical: Chat templates and tokenization" emoji="⚡" variant="danger">
|
| 77 |
|
| 78 |
<Image src={chatTemplatesTokenisation} alt="Spacing, tokenization and template" />
|
|
|
|
| 80 |
Different tokenizers behave differently with spacing and special tokens. See this [visualization](https://x.com/danielhanchen/status/1796952220619157694) showing how spacing, tokenization, and templates interact. Never assume tokenizers behave identically!
|
| 81 |
</Note>
|
| 82 |
|
| 83 |
+
**Paying attention to start and end of sentence tokens**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
+
Some pretrained models, like the `Gemma` ones, are extremely sensitive to the [inclusion of start of sentence tokens](https://github.com/EleutherAI/lm-evaluation-harness/pull/1465) at inference. You might need to do a couple of experiments to see if that happens for you, and add these tokens manually when evaluating if they are not in your dataset.
|
| 86 |
|
| 87 |
+
You can also encounter some issues where your model won't stop on an end of sentence token like you would expect. Code models usually have been trained with `\n\t` as a single token. This means that when generating text, they will often generate `\n\t` in one step. A task which defines `\n` as an end of sentence token (= to stop the generation) will let the model continue generating after a `\n\t`, if predicted as one token, since it's not the same as `\n`. But you would actually still want the model to stop. In these cases, you either need to update your end of sentence tokens, or define a mechanism to backtrack on the character representation of the latest tokens to stop (and cut) the generation a posteriori.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
**Multilinguality and tokenization**
|
| 90 |
|
| 91 |
+
When looking at multilingual evaluations, you'll encounter two issues.
|
| 92 |
|
| 93 |
+
First, as some languages do not always use spacing as a word separator (Korean, Thai, Japanese, Chinese, to cite a few), they will require language specific tokenizers to be split properly, else it will affect their scores on metrics such as [BLEU](https://github.com/EleutherAI/lm-evaluation-harness/issues/212), F1 scores, etc.
|
| 94 |
|
| 95 |
+
Then, tokenizers in general might be unfair to non-English languages. When training a BPE tokenizer, you use data from the different languages you want to cover, but most of the time, though, this data is unbalanced between languages (with, for example, an order of magnitude more English than Thai, or Burmese). Since BPE tokenizers create their vocabulary tokens based on the most frequent words seen, most of the long tokens will be English words - and most of the words from the less frequent languages will only be split at the character level. This effect leads to an unfairness in multilingual tokenization: some (less frequent, or *lower-resourced*) languages require orders of magnitude more tokens to generate a sentence of equivalent length as English.
|
| 96 |
|
| 97 |
+
<iframe
|
| 98 |
+
src="https://OpenEvals-tokenizers-languages.hf.space"
|
| 99 |
+
frameborder="0"
|
| 100 |
+
width="850"
|
| 101 |
+
height="450"
|
| 102 |
+
></iframe>
|
| 103 |
|
| 104 |
+
If you are in this case, the number of tokens that the model is allowed to generate for an evaluation should also be language dependent, as not all languages are tokenized in similar amount of tokens.
|
| 105 |
|
|
|
|
| 106 |
|
| 107 |
+
<Note title="Going further: Language and tokenization" emoji="📚" variant="warning">
|
| 108 |
+
- ⭐ [A beautiful breakdown and demo by Yennie Jun on tokenization issues across languages](https://www.artfish.ai/p/all-languages-are-not-created-tokenized): The breakdown in itself is very clear, and the embedded space comes from her work.
|
| 109 |
+
- ⭐ [A demo by Aleksandar Petrov on unfairness of tokenization](https://aleksandarpetrov.github.io/tokenization-fairness/): I recommend looking at `Compare tokenization of sentences` to get a feel for the differences in cost of inference depending on languages
|
| 110 |
+
</Note>
|
| 111 |
|
| 112 |
|
| 113 |
### Inference
|
| 114 |
|
| 115 |
+
Now that we know how to convert our input text into something the LLMs can parse, let's look at how models process this text.
|
| 116 |
+
|
| 117 |
From this input text, the LLM generates a probability distribution of the most likely next tokens over all the vocabulary. To get a continued generation, we can take the most probable token (give or take some added randomness to get more interesting outputs) as the next one, then repeat the operation, using the new token as the end of the prompt, etc.
|
| 118 |
|
| 119 |
<Image src={llmTk1} alt="LLM tokenization and prediction process" />
|
|
|
|
| 147 |
To learn more about calibration, you can check [this paper](https://arxiv.org/abs/2207.05221) from Anthropic, on what it is, how to detect it, and how to train models to be well calibrated, and [this paper](https://arxiv.org/abs/2311.14648) on some possible limits of calibration).
|
| 148 |
</Sidenote>
|
| 149 |
|
| 150 |
+
|
| 151 |
<Note>
|
| 152 |
A multiple choice question answer can be expressed as a free form generative evaluation too! For this reason, you'll sometimes see a mention of the task **formulation**.
|
| 153 |
|
|
|
|
| 166 |
|
| 167 |
</Note>
|
| 168 |
|
| 169 |
+
<Accordion title="Should you tokenize the context with the choices always?">
|
| 170 |
+
When looking at multiple choices MCQA evaluation, in general, you want to tokenize the context together with the choices, as it creates a succession of tokens which is likely/natural for the model.
|
| 171 |
+
|
| 172 |
+
However, some tokenizers (like the [Llama one](https://github.com/EleutherAI/lm-evaluation-harness/pull/531#issuecomment-1595586257)) do not satisfy `tok(context + choice) = tok(context) + tok(choice)` (and add or remove spacing). This means that comparing the logprobabilities of the choices only is not trivial, as the context tokens can "bleed out" into them, messing up the comparison.
|
| 173 |
+
|
| 174 |
+
To give a concrete example, say you have characters `C1`, `C2`, and `C3` as base tokens of your vocabulary, and `C1C2` also happens to be a single token learned during BPE.
|
| 175 |
+
|
| 176 |
+
Say your context is C1, and the choices C2 and C3.
|
| 177 |
+
If you tokenize the context with the choices, you compare `C1C2` (one token) with `C1+C3` (two tokens). Even if you normalize the logprobs by length, you are not comparing the same thing.
|
| 178 |
+
Comparing after tokenizing the context and choices separately means you compare `C1+C2` and `C1+C3`. But since `C1C2` is a token, the occurence of `C1+C2` is likely rare in the data your encoder saw, so it is an unlikely succession for your model, which can mess up your logprobabilities.
|
| 179 |
+
|
| 180 |
+
If this is the case for your model, the solution is usually to go for the least worst option, comparing the comparable: compute the tokens of context and choice separately and then concatenate them after removing the special start/end of sentence tokens which might have been added.
|
| 181 |
+
</Accordion>
|
| 182 |
+
|
| 183 |
#### Generative evaluations
|
| 184 |
For a generative evaluation, we want the text generated by the model given an input prompt.
|
| 185 |
|
|
|
|
| 195 |
</Note>
|
| 196 |
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/src/content/chapters/general-knowledge/picking-your-evaluation.mdx
CHANGED
|
@@ -6,11 +6,11 @@ import Note from "../../../components/Note.astro";
|
|
| 6 |
import Sidenote from "../../../components/Sidenote.astro";
|
| 7 |
import HtmlEmbed from "../../../components/HtmlEmbed.astro";
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
|
| 12 |
|
| 13 |
-
Then, we began task selection with two primary goals: ensuring **evaluation diversity**, and making sure each task provided a **reliable signal** during pre-training.
|
| 14 |
|
| 15 |
For evaluation diversity, we aimed to assess a broad range of model capabilities, including:
|
| 16 |
|
|
|
|
| 6 |
import Sidenote from "../../../components/Sidenote.astro";
|
| 7 |
import HtmlEmbed from "../../../components/HtmlEmbed.astro";
|
| 8 |
|
| 9 |
+
In some cases, you don't want to "just" reproduce existing scores a posteriori, but you actually need to understand how well your model is training while it's happening. Evaluations you need then have different properties than evaluations for the final performance of models, as you need tasks which will provide good signal even when the model is not yet very good.
|
| 10 |
|
| 11 |
+
So the FineWeb team designed a method to select the best evaluations for pre-training ablations, across 9 languages - let's listen to their wise advice.
|
| 12 |
|
| 13 |
+
For these languages, we collected and implemented all available tasks that we could find, a total of **185 tasks**. Then, we began task selection with two primary goals: ensuring **evaluation diversity**, and making sure each task provided a **reliable signal** during pre-training.
|
| 14 |
|
| 15 |
For evaluation diversity, we aimed to assess a broad range of model capabilities, including:
|
| 16 |
|
app/src/content/chapters/intro.mdx
CHANGED
|
@@ -37,40 +37,33 @@ There are 3 main reasons for which people do evaluation, which tend to be confla
|
|
| 37 |
|
| 38 |
When you select a setup to train models, you want to test something very similar, and make sure that your changes (choosing different training data, architecture, parameters, etc) have not "broken" the expected performance for a model of these properties.
|
| 39 |
|
| 40 |
-
In ML,
|
| 41 |
|
| 42 |
-
For
|
| 43 |
|
| 44 |
-
|
| 45 |
|
| 46 |
-
|
| 47 |
|
| 48 |
-
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
If you have a leaderboard for your domain and task, take the best model, and it's not working for you, it's unlikely the next best model will work.
|
| 51 |
<Sidenote>
|
| 52 |
In [their paper](https://arxiv.org/pdf/2404.02112) about lessons learned on benchmarking and dataset design from the ImageNet era, the authors argue that, since scores are susceptible to instability, the only robust way to evaluate models is through rankings, and more specifically by finding broad groups of evaluations which provide consistent and stable rankings. I believe looking for ranking stability is indeed an extremely interesting approach to model benchmarking, as we have shown that LLMs *scores* on automated benchmarks are extremely susceptible to [minute changes in prompting](https://huggingface.co/blog/evaluation-structured-outputs), and that human evaluations are not more consistent - where *rankings* are actually more stable when using robust evaluation methods.
|
| 53 |
</Sidenote>
|
| 54 |
|
|
|
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
<Note>
|
| 59 |
-
"How do you know for sure if models can do X?" is a question which comes up a lot, and it is a very valid one. However, for any complex capability, **we cannot at the moment just say "this model is the best at this", but instead "this model is the best on this task that we hope is a good proxy for this capability, without any guarantee"**.
|
| 60 |
</Note>
|
| 61 |
|
| 62 |
-
|
| 63 |
#### When will we finally reach AGI?
|
| 64 |
|
| 65 |
-
We are strongly missing any kind of good definitions and framework on what intelligence is for machine learning models (though some people have tried, for example [Chollet](https://arxiv.org/abs/1911.01547) in 2019 and [Hendrycks et al](https://www.agidefinition.ai/paper.pdf) this year).
|
| 66 |
-
|
| 67 |
-
To solve this, we should look at social sciences, as in these fields, people are used to thinking seriously about confounding factors in data gathering and results analysis, which I'm not seeing a lot in "intelligence evaluation" in ML for now.
|
| 68 |
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
<Sidenote>
|
| 72 |
-
I also believe that this question is a bad one, as targeting "general intelligence" is much more blurry, risky, and less useful than targetting good tools with specific capabilities for actual problems that humans encounter in their daily life.
|
| 73 |
-
</Sidenote>
|
| 74 |
|
| 75 |
### So how do people evaluate models, then?
|
| 76 |
|
|
@@ -81,22 +74,24 @@ To my knowledge, at the moment, people use 3 main ways to do evaluation: automat
|
|
| 81 |
Automated benchmarking usually works the following way: you'd like to know how well your model performs on something. This something can be a well-defined concrete **task**, such as *How well can my model classify spam from non spam emails?*, or a more abstract and general **capability**, such as *How good is my model at math?*.
|
| 82 |
|
| 83 |
From this, you construct an evaluation, usually made of two things:
|
| 84 |
-
- a collection of *samples*, given as input to the model to see what comes out as output, sometimes coupled with a reference (called gold) to compare with. Samples are usually designed to try to emulate what you want to test the model on: for example, if you are looking at
|
| 85 |
-
- a *metric*, which is a way to compute a score for the model. For example, how accurately can your model classify
|
| 86 |
|
| 87 |
This is more interesting to do on data that was not included in the model training set, because you want to test if it **generalizes** well. You don't want a model which can only classify emails it has already "seen", that would not be very useful!
|
| 88 |
|
| 89 |
<Note>
|
| 90 |
-
A model which can only predict well on its training data (and has not latently learnt more high-level general patterns) is said to be **overfitting**. In less extreme cases, you still want to test if your model is able to generalize to data patterns which were not in the training set's distribution (for example, classify
|
| 91 |
</Note>
|
| 92 |
|
| 93 |
-
This works quite well for very well-defined tasks, where performance is "easy" to assess and measure: when you are literally testing your model on
|
| 94 |
|
| 95 |
-
For capabilities however, it's hard to decompose them into well-defined and precise tasks: what does "good at math" mean? good at arithmetic? at logic? able to reason on mathematical concepts?
|
| 96 |
|
| 97 |
-
In this case, people tend to do more "holistic" evaluations, by not decomposing the capability in actual tasks, but assuming that performance on general samples will be a **good proxy** for what we aim to measure. For example, GSM8K is made of actual high school math problems, which require a whole set of capabilities to solve. It also means that both failure and success are very hard to interpret. Some capabilities or topics, such as "is this model good at writing poetry?" or "are the model outputs helpful?" are even harder to evaluate with automatic metrics - and at the same time, models now seem to have more and more **generalist** capabilities, so we need to evaluate their abilities in a broader manner.
|
| 98 |
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
|
| 101 |
<Note>
|
| 102 |
The case were an evaluation dataset ends up in the training set is called **contamination**, and a model which was contaminated will have a high benchmark performance that does not generalize well to the underlying task (an extensive description of contamination can be found [here](https://aclanthology.org/2023.findings-emnlp.722/), and here is a fun way to [detect it](https://arxiv.org/abs/2311.06233)). A way to address contamination is to run [**dynamic benchmarks**](https://arxiv.org/abs/2104.14337) (evaluations on datasets which are regularly refreshed to provide scores on systematically unseen new data), but this approach is costly in the long term.
|
|
@@ -110,22 +105,20 @@ This is usually done by tasking humans with first, prompting models, then, gradi
|
|
| 110 |
|
| 111 |
Different approaches exist to evaluate models with humans in the loop.
|
| 112 |
|
| 113 |
-
**Vibes-checks** is the name given to manual evaluations done individually by some members of the community, usually on undisclosed prompts, to get an overall "feeling" of how well models perform on many use cases, which range from coding to quality of smut written. (I've also seen the term "canary-testing" used for this, in reference to high signal canary in a coalmine approach). Often shared on Twitter and Reddit, they mostly constitute anecdotal evidence, and tend to be highly sensitive to confirmation bias (in other words, people tend to find what they look for).
|
| 114 |
|
| 115 |
Using community feedback to establish massive model rankings is what we call an **arena**. A well known example of this is the [LMSYS chatbot arena](https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard), where community users are asked to chat with models until they find one is better than the other. Votes are then aggregated in an Elo ranking (a ranking of matches) to select which model is "the best". The obvious problem of such an approach is the high subjectivity - it's hard to enforce a consistent grading from many community members using broad guidelines, especially since annotators preferences tend to be [culturally bound](https://arxiv.org/abs/2404.16019v1) (with different people favoring different discussion topics, for example). One can hope that this effect is smoothed over by the sheer scale of the votes, through a "wisdom of the crowd" effect (this effect was found by a statistician named Galton, who observed that individual answers trying to estimate a numerical value, like the weight of a hog, could be modeled as a probability distribution centered around the actual answer).
|
| 116 |
|
| 117 |
The last approach is **systematic annotations**, where you provide extremely specific guidelines to paid selected annotators, in order to remove as much as the subjectivity bias as possible (this is the approach used by most data annotation companies). However, it can get extremely expensive fast, as you have to keep on doing evaluations in a continuous and non automatic manner for every new model you want to evaluate, and it can still fall prey to human bias (this [study](https://arxiv.org/abs/2205.00501) showed that people with different identities tend to rate model answer toxicity very differently).
|
| 118 |
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
These biases are not unexpected, but they must be taken into account: not all use cases should rely on using human annotators, especially crowdsourced, unexpert ones - any task requiring factuality (such as code writing, evaluation of model knowledge, etc) should include another, more robust, type of evaluation to complete the benchmark.
|
| 122 |
|
| 123 |
#### Model as a judge
|
| 124 |
|
| 125 |
To mitigate the cost of human annotators, some people have looked into using models or derived artifacts (preferably aligned with human preferences) to evaluate models' outputs. This approach is not new, as you can find techniques to measure summarization quality from [model embeddings](https://arxiv.org/abs/1904.09675) in 2019.
|
| 126 |
|
| 127 |
-
Two approach exist for grading: using [generalist, high capability models](https://arxiv.org/abs/2306.05685v4) or using [small specialist models](https://arxiv.org/pdf/2405.01535) trained specifically to discriminate from preference data.
|
| 128 |
|
| 129 |
-
|
| 130 |
|
| 131 |
My main personal gripe with using models as judges is that they introduce very subtle and un-interpretable bias in the answer selection. I feel that, much like when crossbreeding too much in genetics studies, you end up with dysfunctional animals or plants, by using LLMs to select and train LLMs, we are just as likely to introduce minute changes that will have bigger repercussions a couple generations down the line. I believe this type of bias is less likely to occur in smaller and more specialized models as judges (such as toxicity classifiers), but this remains to be rigorously tested and proven.
|
|
|
|
| 37 |
|
| 38 |
When you select a setup to train models, you want to test something very similar, and make sure that your changes (choosing different training data, architecture, parameters, etc) have not "broken" the expected performance for a model of these properties.
|
| 39 |
|
| 40 |
+
In ML, experiments which test the impact of small changes on model performance are to as **ablations**, and the core of them is actually having a good set of evaluations: with a strong enough signal while relatively cheap to run as you'll be running them **a lot**.
|
| 41 |
|
| 42 |
+
For ablations, you also need to look at both **trajectories** (is the performance better now that when starting training) and scores **ranges** (is the performance within what's expected). You actually... don't really care about the precise score themselves! This evaluation is therefore not here to tell you anything about actual model capabilities, but instead just here to confirm that your training approach is "as sound or better" as the other training approach, and that your model behaves in similar ways.
|
| 43 |
|
| 44 |
+
#### Which model is the best on \<task\>?
|
| 45 |
|
| 46 |
+
The next role of evaluation is simply to sort models to find and select the best model for a given use case.
|
| 47 |
|
| 48 |
+
For common topics like math, code, or knowledge, there are likely several leaderboards comparing and ranking models using different datasets, and you usually just have to test the top contenders to find the best model for you (if they are not working for you, it's unlikely the next best models will work).
|
| 49 |
+
|
| 50 |
+
You could want to run the evaluation and comparision yourself (by reusing existing benchmarks) to get more details to analyse on the model successes and failures, which we will cover below.
|
| 51 |
|
|
|
|
| 52 |
<Sidenote>
|
| 53 |
In [their paper](https://arxiv.org/pdf/2404.02112) about lessons learned on benchmarking and dataset design from the ImageNet era, the authors argue that, since scores are susceptible to instability, the only robust way to evaluate models is through rankings, and more specifically by finding broad groups of evaluations which provide consistent and stable rankings. I believe looking for ranking stability is indeed an extremely interesting approach to model benchmarking, as we have shown that LLMs *scores* on automated benchmarks are extremely susceptible to [minute changes in prompting](https://huggingface.co/blog/evaluation-structured-outputs), and that human evaluations are not more consistent - where *rankings* are actually more stable when using robust evaluation methods.
|
| 54 |
</Sidenote>
|
| 55 |
|
| 56 |
+
For less common topics, you might even need to think about designing your own evaluations, which is our last section.
|
| 57 |
|
| 58 |
+
<Note title="Small caveat">
|
| 59 |
+
Despite often grandiose claims, for any complex capability, we cannot at the moment just say "this model is the best at this", but should instead say **"this model is the best on this task that we hope is a good proxy for this capability, without any guarantee"**.
|
|
|
|
|
|
|
| 60 |
</Note>
|
| 61 |
|
|
|
|
| 62 |
#### When will we finally reach AGI?
|
| 63 |
|
| 64 |
+
We are strongly missing any kind of good definitions and framework on what intelligence is for machine learning models, and how to evaluate it (though some people have tried, for example [Chollet](https://arxiv.org/abs/1911.01547) in 2019 and [Hendrycks et al](https://www.agidefinition.ai/paper.pdf) this year). Difficulty in defining intelligence is not a problem specific to machine learning! In human and animal studies, it is also quite hard to define, and metrics which try to provide precise scores (IQ and EQ for example) are hotly debated and controversial, with reason.
|
|
|
|
|
|
|
| 65 |
|
| 66 |
+
There are, however, some issues with focusing on intelligence as a target. 1) Intelligence tends to end up being a moving target, as any time we reach a capability which was thought to be human specific, we redefine the term. 2) Our current frameworks are made with the human (or animal) in mind, and will most likely not transfer well to models, as the underlying behaviors and assumptions are not the same. 3) It is kind of a useless target too - we should target making models good at specific, well defined, purposeful and useful tasks (think accounting, reporting, etc) instead of aiming for AGI for the sake of it.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
### So how do people evaluate models, then?
|
| 69 |
|
|
|
|
| 74 |
Automated benchmarking usually works the following way: you'd like to know how well your model performs on something. This something can be a well-defined concrete **task**, such as *How well can my model classify spam from non spam emails?*, or a more abstract and general **capability**, such as *How good is my model at math?*.
|
| 75 |
|
| 76 |
From this, you construct an evaluation, usually made of two things:
|
| 77 |
+
- a collection of *samples*, given as input to the model to see what comes out as output, sometimes coupled with a reference (called gold) to compare with. Samples are usually designed to try to emulate what you want to test the model on: for example, if you are looking at toxicity classification, you create a dataset of toxic and non toxic sentences, try to include some hard edge cases, etc.
|
| 78 |
+
- a *metric*, which is a way to compute a score for the model. For example, how accurately can your model classify toxicity (score of well classified sample = 1, badly classified = 0).
|
| 79 |
|
| 80 |
This is more interesting to do on data that was not included in the model training set, because you want to test if it **generalizes** well. You don't want a model which can only classify emails it has already "seen", that would not be very useful!
|
| 81 |
|
| 82 |
<Note>
|
| 83 |
+
A model which can only predict well on its training data (and has not latently learnt more high-level general patterns) is said to be **overfitting**. In less extreme cases, you still want to test if your model is able to generalize to data patterns which were not in the training set's distribution (for example, classify toxicity on stack overflow after having seen only toxicity on reddit).
|
| 84 |
</Note>
|
| 85 |
|
| 86 |
+
This works quite well for very well-defined tasks, where performance is "easy" to assess and measure: when you are literally testing your model on classification, you can say "the model classified correctly n% of these samples". For LLMs benchmarks, some issues can arise, such as models [favoring specific choices based on the order in which they have been presented for multi-choice evaluations](https://arxiv.org/abs/2309.03882), and generative evaluations relying on normalisations which can easily [be unfair if not designed well](https://huggingface.co/blog/open-llm-leaderboard-drop), but overall they still provide signal at the task level.
|
| 87 |
|
| 88 |
+
For **capabilities** however, it's hard to decompose them into well-defined and precise tasks: what does "good at math" mean? good at arithmetic? at logic? able to reason on mathematical concepts?
|
| 89 |
|
| 90 |
+
In this case, people tend to do more "holistic" evaluations, by not decomposing the capability in actual tasks, but assuming that performance on general samples will be a **good proxy** for what we aim to measure. For example, GSM8K is made of actual high school math problems, which require a whole set of capabilities to solve. It also means that both failure and success are very hard to interpret. Some capabilities or topics, such as "is this model good at writing poetry?" or "are the model outputs helpful?" are even harder to evaluate with automatic metrics - and at the same time, models now seem to have more and more **generalist** capabilities, so we need to evaluate their abilities in a broader manner.
|
| 91 |
|
| 92 |
+
<Sidenote> For example, there was a debate in the scientific community as to whether LLMs [can draw](https://arxiv.org/abs/2303.12712) unicorns [or not](https://twitter.com/DimitrisPapail/status/1719119242186871275). A year later, seems like most can! </Sidenote>
|
| 93 |
+
|
| 94 |
+
Automatic benchmarks also tend to have another problem: once they are published publicly in plain text, they are very likely to end up (often accidentally) in the training datasets of models. Some benchmarks creators, like the authors of BigBench, have tried to mitigate this by adding a *canary string* (a very specific combination of characters) for people to look for, and remove from training sets, but not everybody is aware of the mechanism nor trying to do this removal. There is also a non negligible quantity of benchmarks, so looking for accidental copies of absolutely all of them in data is costly. Other options include providing benchmarks in an [**encrypted** form](https://arxiv.org/pdf/2309.16575), or behind a [**gating** system](https://huggingface.co/datasets/Idavidrein/gpqa). However, when evaluating closed models (that are behind APIs), there is no guarantee that the prompts you give won’t be later used internally for training or fine-tuning.
|
| 95 |
|
| 96 |
<Note>
|
| 97 |
The case were an evaluation dataset ends up in the training set is called **contamination**, and a model which was contaminated will have a high benchmark performance that does not generalize well to the underlying task (an extensive description of contamination can be found [here](https://aclanthology.org/2023.findings-emnlp.722/), and here is a fun way to [detect it](https://arxiv.org/abs/2311.06233)). A way to address contamination is to run [**dynamic benchmarks**](https://arxiv.org/abs/2104.14337) (evaluations on datasets which are regularly refreshed to provide scores on systematically unseen new data), but this approach is costly in the long term.
|
|
|
|
| 105 |
|
| 106 |
Different approaches exist to evaluate models with humans in the loop.
|
| 107 |
|
| 108 |
+
**Vibes-checks** is the name given to manual evaluations done individually by some members of the community, usually on undisclosed prompts, to get an overall "feeling" of how well models perform on many use cases, which range from coding to quality of smut written. (I've also seen the term "canary-testing" used for this, in reference to high signal canary in a coalmine approach). Often shared on Twitter and Reddit, they mostly constitute anecdotal evidence, and tend to be highly sensitive to confirmation bias (in other words, people tend to find what they look for).
|
| 109 |
|
| 110 |
Using community feedback to establish massive model rankings is what we call an **arena**. A well known example of this is the [LMSYS chatbot arena](https://huggingface.co/spaces/lmsys/chatbot-arena-leaderboard), where community users are asked to chat with models until they find one is better than the other. Votes are then aggregated in an Elo ranking (a ranking of matches) to select which model is "the best". The obvious problem of such an approach is the high subjectivity - it's hard to enforce a consistent grading from many community members using broad guidelines, especially since annotators preferences tend to be [culturally bound](https://arxiv.org/abs/2404.16019v1) (with different people favoring different discussion topics, for example). One can hope that this effect is smoothed over by the sheer scale of the votes, through a "wisdom of the crowd" effect (this effect was found by a statistician named Galton, who observed that individual answers trying to estimate a numerical value, like the weight of a hog, could be modeled as a probability distribution centered around the actual answer).
|
| 111 |
|
| 112 |
The last approach is **systematic annotations**, where you provide extremely specific guidelines to paid selected annotators, in order to remove as much as the subjectivity bias as possible (this is the approach used by most data annotation companies). However, it can get extremely expensive fast, as you have to keep on doing evaluations in a continuous and non automatic manner for every new model you want to evaluate, and it can still fall prey to human bias (this [study](https://arxiv.org/abs/2205.00501) showed that people with different identities tend to rate model answer toxicity very differently).
|
| 113 |
|
| 114 |
+
However, humans can be biased: for example, they tend to estimate the quality of answers [based on first impressions](https://arxiv.org/pdf/2309.16349), instead of actual factuality or faithfulness, and are very sensitive to tone, and underestimate the number of factual or logical errors in an assertive answer. These are only some of the many biases that human judges can fall prey to (as we'll see below). They are not unexpected, but they must be taken into account: not all use cases should rely on using human annotators, especially crowdsourced, unexpert ones - any task requiring factuality (such as code writing, evaluation of model knowledge, etc) should include another, more robust, type of evaluation to complete the benchmark.
|
|
|
|
|
|
|
| 115 |
|
| 116 |
#### Model as a judge
|
| 117 |
|
| 118 |
To mitigate the cost of human annotators, some people have looked into using models or derived artifacts (preferably aligned with human preferences) to evaluate models' outputs. This approach is not new, as you can find techniques to measure summarization quality from [model embeddings](https://arxiv.org/abs/1904.09675) in 2019.
|
| 119 |
|
| 120 |
+
Two approach exist for grading: using [generalist, high capability models](https://arxiv.org/abs/2306.05685v4) or using [small specialist models](https://arxiv.org/pdf/2405.01535) trained specifically to discriminate from preference data.
|
| 121 |
|
| 122 |
+
Model as judges have several strong limitations, because they are as biased as humans but along different axes (they can't [provide consistent score ranges](https://twitter.com/aparnadhinak/status/1748368364395721128), are actually not that consistent [with human rankings](https://arxiv.org/pdf/2308.15812), etc, as we'll see below).
|
| 123 |
|
| 124 |
My main personal gripe with using models as judges is that they introduce very subtle and un-interpretable bias in the answer selection. I feel that, much like when crossbreeding too much in genetics studies, you end up with dysfunctional animals or plants, by using LLMs to select and train LLMs, we are just as likely to introduce minute changes that will have bigger repercussions a couple generations down the line. I believe this type of bias is less likely to occur in smaller and more specialized models as judges (such as toxicity classifiers), but this remains to be rigorously tested and proven.
|
app/src/content/chapters/troubleshooting/troubleshooting-inference.mdx
DELETED
|
@@ -1,81 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: "Troubleshooting inference"
|
| 3 |
-
---
|
| 4 |
-
|
| 5 |
-
import Note from "../../../components/Note.astro";
|
| 6 |
-
import Sidenote from "../../../components/Sidenote.astro";
|
| 7 |
-
|
| 8 |
-
## Troubleshooting inference
|
| 9 |
-
|
| 10 |
-
### My results are very bad
|
| 11 |
-
|
| 12 |
-
The first thing to do is always to inspect your model generations in detail.
|
| 13 |
-
|
| 14 |
-
Some frequent problems you should look for when troubleshooting are:
|
| 15 |
-
- Is your model output parsing too strict before computing the metric? It can lead to the answer being lost (obvious fix is to make it less strict, but you'll get more false positives!)
|
| 16 |
-
- Is your model struggling to follow your output format in few shot? This frequently happens in recent models trained on too specific evaluation formats, and you can either adapt your prompt format, or just state that models should be able to follow it and that the ones struggling are not good enough for the task you are considering.
|
| 17 |
-
- Is your model exceedingly verbose? In this case, it likely never gets to the correct answer - this is more frequent in long context models (we observed it with Qwen and Command R models in 2024) and reasoning models, especially if the tasks stops generation too soon. You can either increase the allowed context length, add instructions to be concise in the task prompt, or just assume that models should be able to answer succinctly.
|
| 18 |
-
|
| 19 |
-
### My model is very slow!
|
| 20 |
-
➡️ Changing the batch size
|
| 21 |
-
|
| 22 |
-
If you want absolute reproducibility (given a specific hardware and a specific evaluation prompt), you're probably using a batch size of one. However, moving to higher batch sizes will likely make your evaluation faster (given that it fits within the memory requirements of your hardware)
|
| 23 |
-
|
| 24 |
-
➡️ Data parallelism
|
| 25 |
-
|
| 26 |
-
You can also duplicate your model on several GPUs instead of loading it on one single GPU, and provide subsets of the data to each GPU copy, then aggregate the computation results.
|
| 27 |
-
This means that each data stream will be handled in parallel, at the same time as the others, which divides your total execution time by the number of GPUs.
|
| 28 |
-
However, if you can, all GPUs should be on a single node to avoid inter-node bottlenecks.
|
| 29 |
-
|
| 30 |
-
➡️ Changing the inference code
|
| 31 |
-
|
| 32 |
-
Not all inference libraries run at the same speed, and some code is more optimized than other. You'll need to experiment a bit to find which libraries have the fastest inference, and if you are using pytorch, I recommend looking at the model inference optimization checklist [here](https://pytorch.org/serve/performance_checklist.html).
|
| 33 |
-
|
| 34 |
-
➡️ Changing the precision
|
| 35 |
-
|
| 36 |
-
If your model is very slow, you can reduce its size by reducing the precision of the computations. A model stored in float32 does very precise computations (using 32bits per number stored!) that are also very memory and compute heavy - moving to `blfoat16` or `float16` (half the precision) should make the model twice as fast at a loss of precision which should almost not matter. If you want bumps in speed, you can quantize it even more, to 8 or 4 bits (using `gptq` or `bitsandbytes` for example), as n-bit matrix computations should be faster and your model will take even less space in memory (however, some quantization libraries might be a bit slow, so test things out for your use cases!).
|
| 37 |
-
|
| 38 |
-
### My model is very big!
|
| 39 |
-
You can estimate the minimal theoretical memory required to load a given model (and therefore hardware) with the **following formula**:
|
| 40 |
-
|
| 41 |
-
`<memory (in GB)> = <number of parameters (in G)> * <precision factor>`
|
| 42 |
-
|
| 43 |
-
Since you can store 8 bits in a Byte, the memory required is the total number of parameters times the number of Bytes required to store one parameter. The precision factor is therefore 4 for `float32`, 2 for `float16` or `bfoat16`, 1 for `8bit`, and 0.5 for `4bit` models, etc.
|
| 44 |
-
|
| 45 |
-
And that's it!
|
| 46 |
-
|
| 47 |
-
I would actually recommend using `<memory (in GB)> = <number of parameters (in G)> * (<precision factor> * 110%)`, to be on the safer side, as inference will require a bit more memory than just loading the model (you'll also need to load the batches).
|
| 48 |
-
|
| 49 |
-
### My model does not fit on a GPU
|
| 50 |
-
➡️ Quantization
|
| 51 |
-
|
| 52 |
-
The first obvious thing is to play on the `<precision factor>` above: going from float32 to 4 bits reduces memory requirements by 8!
|
| 53 |
-
However, using too low a precision can give worse results, so for some models (especially medium range), you might want to stay in float16 or 8bit. (Quantization seems to affect very big models performance less, possibly because of information redundancy).
|
| 54 |
-
|
| 55 |
-
➡️ Model parallelism
|
| 56 |
-
|
| 57 |
-
Model parallelism includes a range of techniques which cut your model in smaller sub-model pieces, to load and run each of these smaller pieces on a single different GPU. This requires less memory since you never load the full model at once, but can be slower.
|
| 58 |
-
|
| 59 |
-
<Note title="Model parallelism strategies" emoji="🔀" variant="info">
|
| 60 |
-
|
| 61 |
-
The 2 main types of model parallelism are
|
| 62 |
-
- **Pipeline parallelism**, where the model is split at the whole layer level, and the layers are dispatched on different GPUs. Since layer 1's output is layer 2's input, this leads to a slower execution, as GPUs will be idle while waiting, which is called a "bubble" (and data must be transferred from one GPU to the next). The bubble can be reduced by splitting the inputs into smaller batches. It's being natively added to PyTorch with the `PiPPy` [lib](https://github.com/pytorch/PiPPy), and this is what `accelerate` uses under the hood for parallelism.
|
| 63 |
-
- **Tensor parallelism**, where the model is split at the matrix computation level. This means that the matrices will be split on rows or columns, and the total result aggregated. This is incredibly efficient as long as all GPUs are on the same node (to avoid inter node network bottlenecks), but can be hard to code. You'll find cool implementations of this in the `vllm` lib. It provides **insane speedups**.
|
| 64 |
-
</Note>
|
| 65 |
-
|
| 66 |
-
The best document on the different kinds of parallelism (including data parallelism, for speedups) is [here](https://huggingface.co/docs/transformers/v4.15.0/en/parallelism).
|
| 67 |
-
|
| 68 |
-
➡️ CPU offloading
|
| 69 |
-
|
| 70 |
-
CPU offloading moves some of the computations and models parts to CPU, in order to reduce GPU memory usage. It's **considerably slower** than any other method here, mostly because you need to move data from one device to another all the time.
|
| 71 |
-
|
| 72 |
-
An example of this is [ZeRO-Offload](https://arxiv.org/abs/2101.06840) by Deepspeed, which distributes parameters between CPU and GPU (on top of using other optimization described in the ZeRO-2 paper). On CPU are passed gradients, optimizer states and fp32 model parameter computations during optimisation, whereas on GPU, you'll find fp16 parameters and forward/backward pass, to leverage CPU memory used and GPU computations while minimizing communication between both.
|
| 73 |
-
|
| 74 |
-
➡️ My model fits on a GPU but I still get OOMs!
|
| 75 |
-
|
| 76 |
-
You likely have a problem with your context size, then.
|
| 77 |
-
|
| 78 |
-
I recommend:
|
| 79 |
-
1) testing if your model truly does fit on a GPU with some dummy inference data loaded. This dummy inference data should have a big enough context size (representative of your task)
|
| 80 |
-
2) lowering the batch size, or removing the auto-batch size search which could lead to an accidental OOM error, if you have this enabled
|
| 81 |
-
3) more generally, making sure that samples are presented to your model in inverse context size order, to be sure that your model will fail directly if the context size is too big, and not after having run for X hours.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/src/content/chapters/troubleshooting/troubleshooting-reproducibility.mdx
CHANGED
|
@@ -17,8 +17,6 @@ Usually, this means either using the evaluation default code as provided by the
|
|
| 17 |
|
| 18 |
If you want to easily understand what kind of discrepancies happen when using different implementations, you can explore [this blog](https://huggingface.co/blog/open-llm-leaderboard-mmlu) (⭐) we wrote with the eval team at HuggingFace. It studies the differences we observed between 3 common implementations of the MMLU evaluation (in `lm_eval`, `helm`, and in the original author implementation), and how they change model scores.
|
| 19 |
|
| 20 |
-
*Note: This is precisely for this reason that a Hugging Face team decided to launch the [Open LLM Leaderboard](https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard), to get unified and homogeneous comparisons of models scores in order to compare them to internal experiments.*
|
| 21 |
-
|
| 22 |
#### Subtle implementation or loading difference
|
| 23 |
We've observed that the following were easy things to mess up, even when using the same code base:
|
| 24 |
- **Different random seeds.**
|
|
|
|
| 17 |
|
| 18 |
If you want to easily understand what kind of discrepancies happen when using different implementations, you can explore [this blog](https://huggingface.co/blog/open-llm-leaderboard-mmlu) (⭐) we wrote with the eval team at HuggingFace. It studies the differences we observed between 3 common implementations of the MMLU evaluation (in `lm_eval`, `helm`, and in the original author implementation), and how they change model scores.
|
| 19 |
|
|
|
|
|
|
|
| 20 |
#### Subtle implementation or loading difference
|
| 21 |
We've observed that the following were easy things to mess up, even when using the same code base:
|
| 22 |
- **Different random seeds.**
|
app/src/content/embeds/d3-tokenization-timeline.html
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div class="d3-prompt-evolution">
|
| 2 |
+
<svg viewBox="0 0 900 500" xmlns="http://www.w3.org/2000/svg">
|
| 3 |
+
<defs>
|
| 4 |
+
<marker id="arrowhead-prompt" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
| 5 |
+
<polygon points="0 0, 10 3, 0 6" fill="currentColor" />
|
| 6 |
+
</marker>
|
| 7 |
+
</defs>
|
| 8 |
+
|
| 9 |
+
<!-- Stage 1: Raw Text -->
|
| 10 |
+
<g class="stage">
|
| 11 |
+
<rect x="30" y="40" width="240" height="140" rx="8" class="stage-box stage-1"/>
|
| 12 |
+
<text x="150" y="65" text-anchor="middle" class="stage-title">Raw Text</text>
|
| 13 |
+
<text x="150" y="85" text-anchor="middle" class="stage-subtitle">Early Models (before 2022)</text>
|
| 14 |
+
|
| 15 |
+
<foreignObject x="45" y="95" width="210" height="80">
|
| 16 |
+
<div xmlns="http://www.w3.org/1999/xhtml" class="code-example">
|
| 17 |
+
<div class="code-line">Translate to French:</div>
|
| 18 |
+
<div class="code-line">Hello world</div>
|
| 19 |
+
</div>
|
| 20 |
+
</foreignObject>
|
| 21 |
+
</g>
|
| 22 |
+
|
| 23 |
+
<!-- Arrow 1 -->
|
| 24 |
+
<path d="M 270 110 L 320 110" class="arrow" marker-end="url(#arrowhead-prompt)"/>
|
| 25 |
+
<text x="295" y="100" text-anchor="middle" class="arrow-label">Evolution</text>
|
| 26 |
+
|
| 27 |
+
<!-- Stage 2: JSON + Chat Templates -->
|
| 28 |
+
<g class="stage">
|
| 29 |
+
<rect x="320" y="20" width="260" height="180" rx="8" class="stage-box stage-2"/>
|
| 30 |
+
<text x="450" y="45" text-anchor="middle" class="stage-title">Chat Templates (in JSON)</text>
|
| 31 |
+
<text x="450" y="65" text-anchor="middle" class="stage-subtitle">Chat Models (2022-2025)</text>
|
| 32 |
+
|
| 33 |
+
<foreignObject x="335" y="75" width="230" height="120">
|
| 34 |
+
<div xmlns="http://www.w3.org/1999/xhtml" class="code-example">
|
| 35 |
+
<div class="code-line json-brace">{</div>
|
| 36 |
+
<div class="code-line indent">"role": "system",</div>
|
| 37 |
+
<div class="code-line indent">"content": "You are..."</div>
|
| 38 |
+
<div class="code-line json-brace">},</div>
|
| 39 |
+
<div class="code-line json-brace">{</div>
|
| 40 |
+
<div class="code-line indent">"role": "user",</div>
|
| 41 |
+
<div class="code-line indent">"content": "Hello"</div>
|
| 42 |
+
<div class="code-line json-brace">}</div>
|
| 43 |
+
</div>
|
| 44 |
+
</foreignObject>
|
| 45 |
+
</g>
|
| 46 |
+
|
| 47 |
+
<!-- Arrow 2 -->
|
| 48 |
+
<path d="M 580 110 L 630 110" class="arrow" marker-end="url(#arrowhead-prompt)"/>
|
| 49 |
+
<text x="605" y="100" text-anchor="middle" class="arrow-label">Evolution</text>
|
| 50 |
+
|
| 51 |
+
<!-- Stage 3: JSON + XML (Reasoning) -->
|
| 52 |
+
<g class="stage">
|
| 53 |
+
<rect x="630" y="10" width="240" height="200" rx="8" class="stage-box stage-3"/>
|
| 54 |
+
<text x="750" y="35" text-anchor="middle" class="stage-title">JSON + XML</text>
|
| 55 |
+
<text x="750" y="55" text-anchor="middle" class="stage-subtitle">Reasoning Models (2025+)</text>
|
| 56 |
+
|
| 57 |
+
<foreignObject x="645" y="65" width="210" height="140">
|
| 58 |
+
<div xmlns="http://www.w3.org/1999/xhtml" class="code-example">
|
| 59 |
+
<div class="code-line json-brace">{</div>
|
| 60 |
+
<div class="code-line indent">"role": "assistant",</div>
|
| 61 |
+
<div class="code-line indent">"content": [</div>
|
| 62 |
+
<div class="code-line indent2 xml-tag"><thinking></div>
|
| 63 |
+
<div class="code-line indent2">reasoning...</div>
|
| 64 |
+
<div class="code-line indent2 xml-tag"></thinking></div>
|
| 65 |
+
<div class="code-line indent2 xml-tag"><output></div>
|
| 66 |
+
<div class="code-line indent2">response</div>
|
| 67 |
+
<div class="code-line indent2 xml-tag"></output></div>
|
| 68 |
+
<div class="code-line indent">]</div>
|
| 69 |
+
<div class="code-line json-brace">}</div>
|
| 70 |
+
</div>
|
| 71 |
+
</foreignObject>
|
| 72 |
+
</g>
|
| 73 |
+
|
| 74 |
+
<!-- Key Features Labels -->
|
| 75 |
+
<g class="features">
|
| 76 |
+
<text x="150" y="200" text-anchor="middle" class="feature-label">• Simple prompts</text>
|
| 77 |
+
<text x="150" y="220" text-anchor="middle" class="feature-label">• Generally no structure</text>
|
| 78 |
+
<text x="150" y="240" text-anchor="middle" class="feature-label">• Completion-based</text>
|
| 79 |
+
</g>
|
| 80 |
+
|
| 81 |
+
<g class="features">
|
| 82 |
+
<text x="450" y="220" text-anchor="middle" class="feature-label">• Role separation</text>
|
| 83 |
+
<text x="450" y="240" text-anchor="middle" class="feature-label">• Chat/Turn-based</text>
|
| 84 |
+
</g>
|
| 85 |
+
|
| 86 |
+
<g class="features">
|
| 87 |
+
<text x="750" y="230" text-anchor="middle" class="feature-label">• Chat/Turn-based</text>
|
| 88 |
+
<text x="750" y="250" text-anchor="middle" class="feature-label">with added tags for control</text>
|
| 89 |
+
</g>
|
| 90 |
+
|
| 91 |
+
<!-- Timeline -->
|
| 92 |
+
<line x1="50" y1="320" x2="850" y2="320" class="timeline"/>
|
| 93 |
+
<circle cx="150" cy="320" r="6" class="timeline-dot"/>
|
| 94 |
+
<circle cx="450" cy="320" r="6" class="timeline-dot"/>
|
| 95 |
+
<circle cx="750" cy="320" r="6" class="timeline-dot"/>
|
| 96 |
+
|
| 97 |
+
<text x="150" y="345" text-anchor="middle" class="timeline-label">Before 2022</text>
|
| 98 |
+
<text x="450" y="345" text-anchor="middle" class="timeline-label">2022-2025</text>
|
| 99 |
+
<text x="750" y="345" text-anchor="middle" class="timeline-label">2025+</text>
|
| 100 |
+
</svg>
|
| 101 |
+
</div>
|
| 102 |
+
<style>
|
| 103 |
+
.d3-prompt-evolution {
|
| 104 |
+
position: relative;
|
| 105 |
+
width: 100%;
|
| 106 |
+
}
|
| 107 |
+
.d3-prompt-evolution svg {
|
| 108 |
+
display: block;
|
| 109 |
+
width: 100%;
|
| 110 |
+
height: auto;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
/* Stage boxes */
|
| 114 |
+
.d3-prompt-evolution .stage-box {
|
| 115 |
+
stroke-width: 2;
|
| 116 |
+
}
|
| 117 |
+
.d3-prompt-evolution .stage-1 {
|
| 118 |
+
fill: #e3f2fd;
|
| 119 |
+
stroke: #1976d2;
|
| 120 |
+
}
|
| 121 |
+
.d3-prompt-evolution .stage-2 {
|
| 122 |
+
fill: #f3e5f5;
|
| 123 |
+
stroke: #7b1fa2;
|
| 124 |
+
}
|
| 125 |
+
.d3-prompt-evolution .stage-3 {
|
| 126 |
+
fill: #e8f5e9;
|
| 127 |
+
stroke: #388e3c;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
[data-theme="dark"] .d3-prompt-evolution .stage-1 {
|
| 131 |
+
fill: rgba(25, 118, 210, 0.15);
|
| 132 |
+
}
|
| 133 |
+
[data-theme="dark"] .d3-prompt-evolution .stage-2 {
|
| 134 |
+
fill: rgba(123, 31, 162, 0.15);
|
| 135 |
+
}
|
| 136 |
+
[data-theme="dark"] .d3-prompt-evolution .stage-3 {
|
| 137 |
+
fill: rgba(56, 142, 60, 0.15);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
/* Text styles */
|
| 141 |
+
.d3-prompt-evolution .stage-title {
|
| 142 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 143 |
+
font-size: 16px;
|
| 144 |
+
font-weight: 700;
|
| 145 |
+
fill: var(--text-color, #333);
|
| 146 |
+
}
|
| 147 |
+
.d3-prompt-evolution .stage-subtitle {
|
| 148 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 149 |
+
font-size: 11px;
|
| 150 |
+
fill: var(--muted-color, #666);
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/* Code examples */
|
| 154 |
+
.d3-prompt-evolution .code-example {
|
| 155 |
+
font-family: 'Monaco', 'Courier New', monospace;
|
| 156 |
+
font-size: 9px;
|
| 157 |
+
line-height: 1.4;
|
| 158 |
+
color: var(--text-color, #333);
|
| 159 |
+
padding: 4px;
|
| 160 |
+
}
|
| 161 |
+
.d3-prompt-evolution .code-line {
|
| 162 |
+
margin: 1px 0;
|
| 163 |
+
}
|
| 164 |
+
.d3-prompt-evolution .indent {
|
| 165 |
+
padding-left: 12px;
|
| 166 |
+
}
|
| 167 |
+
.d3-prompt-evolution .indent2 {
|
| 168 |
+
padding-left: 24px;
|
| 169 |
+
}
|
| 170 |
+
.d3-prompt-evolution .json-brace {
|
| 171 |
+
color: var(--primary-color, #1976d2);
|
| 172 |
+
font-weight: 600;
|
| 173 |
+
}
|
| 174 |
+
.d3-prompt-evolution .xml-tag {
|
| 175 |
+
color: #d32f2f;
|
| 176 |
+
font-weight: 600;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/* Arrows */
|
| 180 |
+
.d3-prompt-evolution .arrow {
|
| 181 |
+
fill: none;
|
| 182 |
+
stroke: var(--muted-color, #999);
|
| 183 |
+
stroke-width: 2;
|
| 184 |
+
color: var(--muted-color, #999);
|
| 185 |
+
}
|
| 186 |
+
.d3-prompt-evolution .arrow-label {
|
| 187 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 188 |
+
font-size: 10px;
|
| 189 |
+
font-style: italic;
|
| 190 |
+
fill: var(--muted-color, #666);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
/* Feature labels */
|
| 194 |
+
.d3-prompt-evolution .feature-label {
|
| 195 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 196 |
+
font-size: 11px;
|
| 197 |
+
fill: var(--text-color, #555);
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
/* Timeline */
|
| 201 |
+
.d3-prompt-evolution .timeline {
|
| 202 |
+
stroke: var(--border-color, #ddd);
|
| 203 |
+
stroke-width: 2;
|
| 204 |
+
}
|
| 205 |
+
.d3-prompt-evolution .timeline-dot {
|
| 206 |
+
fill: var(--primary-color, #1976d2);
|
| 207 |
+
}
|
| 208 |
+
.d3-prompt-evolution .timeline-label {
|
| 209 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 210 |
+
font-size: 12px;
|
| 211 |
+
font-weight: 600;
|
| 212 |
+
fill: var(--text-color, #333);
|
| 213 |
+
}
|
| 214 |
+
.d3-prompt-evolution .model-example {
|
| 215 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 216 |
+
font-size: 11px;
|
| 217 |
+
font-style: italic;
|
| 218 |
+
fill: var(--muted-color, #666);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
/* Benefits section */
|
| 222 |
+
.d3-prompt-evolution .benefits-box {
|
| 223 |
+
fill: var(--surface-bg, #fafafa);
|
| 224 |
+
stroke: var(--border-color, #e0e0e0);
|
| 225 |
+
stroke-width: 1.5;
|
| 226 |
+
}
|
| 227 |
+
.d3-prompt-evolution .benefits-title {
|
| 228 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 229 |
+
font-size: 13px;
|
| 230 |
+
font-weight: 700;
|
| 231 |
+
fill: var(--text-color, #333);
|
| 232 |
+
}
|
| 233 |
+
.d3-prompt-evolution .benefit-text {
|
| 234 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 235 |
+
font-size: 11px;
|
| 236 |
+
fill: var(--text-color, #555);
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
[data-theme="dark"] .d3-prompt-evolution .benefits-box {
|
| 240 |
+
fill: rgba(255, 255, 255, 0.05);
|
| 241 |
+
}
|
| 242 |
+
</style>
|
| 243 |
+
<script>
|
| 244 |
+
(() => {
|
| 245 |
+
const bootstrap = () => {
|
| 246 |
+
const scriptEl = document.currentScript;
|
| 247 |
+
let container = scriptEl ? scriptEl.previousElementSibling : null;
|
| 248 |
+
if (!(container && container.classList && container.classList.contains('d3-prompt-evolution'))) {
|
| 249 |
+
const candidates = Array.from(document.querySelectorAll('.d3-prompt-evolution'))
|
| 250 |
+
.filter((el) => !(el.dataset && el.dataset.mounted === 'true'));
|
| 251 |
+
container = candidates[candidates.length - 1] || null;
|
| 252 |
+
}
|
| 253 |
+
if (!container) return;
|
| 254 |
+
if (container.dataset) {
|
| 255 |
+
if (container.dataset.mounted === 'true') return;
|
| 256 |
+
container.dataset.mounted = 'true';
|
| 257 |
+
}
|
| 258 |
+
};
|
| 259 |
+
|
| 260 |
+
if (document.readyState === 'loading') {
|
| 261 |
+
document.addEventListener('DOMContentLoaded', bootstrap, { once: true });
|
| 262 |
+
} else {
|
| 263 |
+
bootstrap();
|
| 264 |
+
}
|
| 265 |
+
})();
|
| 266 |
+
</script>
|
app/src/content/embeds/d3-tokenization.html
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div class="d3-tokenization">
|
| 2 |
+
<svg viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg">
|
| 3 |
+
<defs>
|
| 4 |
+
<marker id="arrowhead-tok" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
|
| 5 |
+
<polygon points="0 0, 10 3, 0 6" fill="currentColor" />
|
| 6 |
+
</marker>
|
| 7 |
+
</defs>
|
| 8 |
+
|
| 9 |
+
<!-- Input Text -->
|
| 10 |
+
<rect x="50" y="50" width="200" height="80" rx="5" class="box"/>
|
| 11 |
+
<text x="150" y="75" text-anchor="middle" class="text title">Input Text</text>
|
| 12 |
+
<text x="150" y="100" text-anchor="middle" class="text label">"Hello, world!"</text>
|
| 13 |
+
|
| 14 |
+
<!-- Arrow 1 -->
|
| 15 |
+
<path d="M 250 90 L 290 90" class="arrow" marker-end="url(#arrowhead-tok)"/>
|
| 16 |
+
|
| 17 |
+
<!-- Tokenizer -->
|
| 18 |
+
<rect x="290" y="60" width="120" height="60" rx="5" class="process"/>
|
| 19 |
+
<text x="350" y="85" text-anchor="middle" class="text title">Tokenizer</text>
|
| 20 |
+
<text x="350" y="105" text-anchor="middle" class="text label" font-size="10">Split into tokens</text>
|
| 21 |
+
|
| 22 |
+
<!-- Arrow 2 -->
|
| 23 |
+
<path d="M 410 90 L 450 90" class="arrow" marker-end="url(#arrowhead-tok)"/>
|
| 24 |
+
|
| 25 |
+
<!-- Tokens -->
|
| 26 |
+
<rect x="450" y="30" width="280" height="120" rx="5" class="box"/>
|
| 27 |
+
<text x="590" y="55" text-anchor="middle" class="text title">Tokens</text>
|
| 28 |
+
|
| 29 |
+
<!-- Token boxes -->
|
| 30 |
+
<rect x="470" y="70" width="60" height="30" rx="3" class="token-box"/>
|
| 31 |
+
<text x="500" y="90" text-anchor="middle" class="text token">Hello</text>
|
| 32 |
+
|
| 33 |
+
<rect x="540" y="70" width="40" height="30" rx="3" class="token-box"/>
|
| 34 |
+
<text x="560" y="90" text-anchor="middle" class="text token">,</text>
|
| 35 |
+
|
| 36 |
+
<rect x="590" y="70" width="60" height="30" rx="3" class="token-box"/>
|
| 37 |
+
<text x="620" y="90" text-anchor="middle" class="text token">world</text>
|
| 38 |
+
|
| 39 |
+
<rect x="660" y="70" width="40" height="30" rx="3" class="token-box"/>
|
| 40 |
+
<text x="680" y="90" text-anchor="middle" class="text token">!</text>
|
| 41 |
+
|
| 42 |
+
<!-- Token IDs -->
|
| 43 |
+
<text x="500" y="125" text-anchor="middle" class="text token-id">[5425]</text>
|
| 44 |
+
<text x="560" y="125" text-anchor="middle" class="text token-id">[11]</text>
|
| 45 |
+
<text x="620" y="125" text-anchor="middle" class="text token-id">[1917]</text>
|
| 46 |
+
<text x="680" y="125" text-anchor="middle" class="text token-id">[0]</text>
|
| 47 |
+
|
| 48 |
+
<!-- Arrow 3 -->
|
| 49 |
+
<path d="M 590 150 L 590 190" class="arrow" marker-end="url(#arrowhead-tok)"/>
|
| 50 |
+
|
| 51 |
+
<!-- Model -->
|
| 52 |
+
<rect x="480" y="190" width="220" height="100" rx="5" class="model"/>
|
| 53 |
+
<text x="590" y="215" text-anchor="middle" class="text title">Language Model</text>
|
| 54 |
+
|
| 55 |
+
<!-- Model internal representation -->
|
| 56 |
+
<g transform="translate(520, 230)">
|
| 57 |
+
<circle cx="20" cy="15" r="8" class="node-circle"/>
|
| 58 |
+
<circle cx="50" cy="15" r="8" class="node-circle"/>
|
| 59 |
+
<circle cx="80" cy="15" r="8" class="node-circle"/>
|
| 60 |
+
<circle cx="110" cy="15" r="8" class="node-circle"/>
|
| 61 |
+
<circle cx="140" cy="15" r="8" class="node-circle"/>
|
| 62 |
+
</g>
|
| 63 |
+
<text x="590" y="275" text-anchor="middle" class="text label" font-size="10">Process & Generate</text>
|
| 64 |
+
|
| 65 |
+
<!-- Arrow 4 -->
|
| 66 |
+
<path d="M 590 290 L 590 330" class="arrow" marker-end="url(#arrowhead-tok)"/>
|
| 67 |
+
|
| 68 |
+
<!-- Output -->
|
| 69 |
+
<rect x="490" y="330" width="200" height="50" rx="5" class="box"/>
|
| 70 |
+
<text x="590" y="360" text-anchor="middle" class="text label">Output / Prediction</text>
|
| 71 |
+
</svg>
|
| 72 |
+
</div>
|
| 73 |
+
<style>
|
| 74 |
+
.d3-tokenization {
|
| 75 |
+
position: relative;
|
| 76 |
+
width: 100%;
|
| 77 |
+
}
|
| 78 |
+
.d3-tokenization svg {
|
| 79 |
+
display: block;
|
| 80 |
+
width: 100%;
|
| 81 |
+
height: auto;
|
| 82 |
+
}
|
| 83 |
+
.d3-tokenization .box {
|
| 84 |
+
fill: var(--surface-bg, #f0f4ff);
|
| 85 |
+
stroke: var(--primary-color, #4169e1);
|
| 86 |
+
stroke-width: 2;
|
| 87 |
+
}
|
| 88 |
+
.d3-tokenization .process {
|
| 89 |
+
fill: #fff8e1;
|
| 90 |
+
stroke: #ff9800;
|
| 91 |
+
stroke-width: 2;
|
| 92 |
+
}
|
| 93 |
+
.d3-tokenization .model {
|
| 94 |
+
fill: #e8f5e9;
|
| 95 |
+
stroke: #4caf50;
|
| 96 |
+
stroke-width: 2;
|
| 97 |
+
}
|
| 98 |
+
.d3-tokenization .text {
|
| 99 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
| 100 |
+
fill: var(--text-color, #333);
|
| 101 |
+
}
|
| 102 |
+
.d3-tokenization .title {
|
| 103 |
+
font-size: 14px;
|
| 104 |
+
font-weight: 600;
|
| 105 |
+
}
|
| 106 |
+
.d3-tokenization .label {
|
| 107 |
+
font-size: 12px;
|
| 108 |
+
}
|
| 109 |
+
.d3-tokenization .token {
|
| 110 |
+
font-size: 11px;
|
| 111 |
+
font-family: 'Monaco', 'Courier New', monospace;
|
| 112 |
+
}
|
| 113 |
+
.d3-tokenization .token-id {
|
| 114 |
+
font-size: 9px;
|
| 115 |
+
fill: var(--muted-color, #666);
|
| 116 |
+
}
|
| 117 |
+
.d3-tokenization .arrow {
|
| 118 |
+
fill: none;
|
| 119 |
+
stroke: var(--muted-color, #666);
|
| 120 |
+
stroke-width: 2;
|
| 121 |
+
color: var(--muted-color, #666);
|
| 122 |
+
}
|
| 123 |
+
.d3-tokenization .token-box {
|
| 124 |
+
fill: white;
|
| 125 |
+
stroke: var(--primary-color, #4169e1);
|
| 126 |
+
stroke-width: 1.5;
|
| 127 |
+
}
|
| 128 |
+
.d3-tokenization .node-circle {
|
| 129 |
+
fill: #81c784;
|
| 130 |
+
opacity: 0.7;
|
| 131 |
+
}
|
| 132 |
+
[data-theme="dark"] .d3-tokenization .box {
|
| 133 |
+
fill: rgba(65, 105, 225, 0.1);
|
| 134 |
+
}
|
| 135 |
+
[data-theme="dark"] .d3-tokenization .token-box {
|
| 136 |
+
fill: var(--surface-bg, #1a1a1a);
|
| 137 |
+
}
|
| 138 |
+
[data-theme="dark"] .d3-tokenization .process {
|
| 139 |
+
fill: rgba(255, 152, 0, 0.15);
|
| 140 |
+
}
|
| 141 |
+
[data-theme="dark"] .d3-tokenization .model {
|
| 142 |
+
fill: rgba(76, 175, 80, 0.15);
|
| 143 |
+
}
|
| 144 |
+
</style>
|
| 145 |
+
<script>
|
| 146 |
+
(() => {
|
| 147 |
+
const bootstrap = () => {
|
| 148 |
+
const scriptEl = document.currentScript;
|
| 149 |
+
let container = scriptEl ? scriptEl.previousElementSibling : null;
|
| 150 |
+
if (!(container && container.classList && container.classList.contains('d3-tokenization'))) {
|
| 151 |
+
const candidates = Array.from(document.querySelectorAll('.d3-tokenization'))
|
| 152 |
+
.filter((el) => !(el.dataset && el.dataset.mounted === 'true'));
|
| 153 |
+
container = candidates[candidates.length - 1] || null;
|
| 154 |
+
}
|
| 155 |
+
if (!container) return;
|
| 156 |
+
if (container.dataset) {
|
| 157 |
+
if (container.dataset.mounted === 'true') return;
|
| 158 |
+
container.dataset.mounted = 'true';
|
| 159 |
+
}
|
| 160 |
+
};
|
| 161 |
+
|
| 162 |
+
if (document.readyState === 'loading') {
|
| 163 |
+
document.addEventListener('DOMContentLoaded', bootstrap, { once: true });
|
| 164 |
+
} else {
|
| 165 |
+
bootstrap();
|
| 166 |
+
}
|
| 167 |
+
})();
|
| 168 |
+
</script>
|