sweedworks

← all sources

precompute.py

Computes the figures on /tokens/

147 lines. This is the file the build actually runs, copied verbatim at build time.

"""Generate tokens/data.json — every number shown on the page is computed here,
never hand-written, so the prose can't drift from the tokenizer."""

import json
import os

from cjsload import Reference

OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "tokens", "data.json")

# Article 1 of the Universal Declaration of Human Rights, in the UN's official
# translations. Chosen because it's the same meaning in every row — which is the
# whole point: identical content, wildly different token cost.
UDHR = [
    ("English", "en", "Latin",
     "All human beings are born free and equal in dignity and rights. They are "
     "endowed with reason and conscience and should act towards one another in a "
     "spirit of brotherhood."),
    ("Spanish", "es", "Latin",
     "Todos los seres humanos nacen libres e iguales en dignidad y derechos y, "
     "dotados como están de razón y conciencia, deben comportarse fraternalmente "
     "los unos con los otros."),
    ("French", "fr", "Latin",
     "Tous les êtres humains naissent libres et égaux en dignité et en droits. "
     "Ils sont doués de raison et de conscience et doivent agir les uns envers "
     "les autres dans un esprit de fraternité."),
    ("German", "de", "Latin",
     "Alle Menschen sind frei und gleich an Würde und Rechten geboren. Sie sind "
     "mit Vernunft und Gewissen begabt und sollen einander im Geiste der "
     "Brüderlichkeit begegnen."),
    ("Portuguese", "pt", "Latin",
     "Todos os seres humanos nascem livres e iguais em dignidade e direitos. São "
     "dotados de razão e consciência e devem agir uns para com os outros em "
     "espírito de fraternidade."),
    ("Russian", "ru", "Cyrillic",
     "Все люди рождаются свободными и равными в своем достоинстве и правах. Они "
     "наделены разумом и совестью и должны поступать в отношении друг друга в "
     "духе братства."),
    ("Chinese", "zh", "Han",
     "人人生而自由,在尊严和权利上一律平等。他们赋有理性和良心,并应以兄弟关系的精神相对待。"),
    ("Japanese", "ja", "Japanese",
     "すべての人間は、生れながらにして自由であり、かつ、尊厳と権利とについて平等である。"
     "人間は、理性と良心とを授けられており、互いに同胞の精神をもって行動しなければならない。"),
    ("Korean", "ko", "Hangul",
     "모든 인간은 태어날 때부터 자유로우며 그 존엄과 권리에 있어 동등하다. 인간은 천부적으로 "
     "이성과 양심을 부여받았으며 서로 형제애의 정신으로 행동하여야 한다."),
    ("Arabic", "ar", "Arabic",
     "يولد جميع الناس أحراراً متساوين في الكرامة والحقوق. وقد وهبوا عقلاً وضميراً "
     "وعليهم أن يعامل بعضهم بعضاً بروح الإخاء."),
    ("Hindi", "hi", "Devanagari",
     "सभी मनुष्यों को गौरव और अधिकारों के मामले में जन्मजात स्वतन्त्रता और समानता प्राप्त है। "
     "उन्हें बुद्धि और अन्तरात्मा की देन प्राप्त है और परस्पर उन्हें भाईचारे के भाव से बर्ताव करना चाहिए।"),
]

COUNTING_WORDS = ["strawberry", "raspberry", "bookkeeper", "Mississippi", "unsuccessfully"]

NUMBERS = ["1234567890", "1,234,567,890", "3.14159265", "2024", "20240811", "127.0.0.1"]

WHITESPACE = ["strawberry", " strawberry", "strawberry ", "Strawberry", "STRAWBERRY", "  strawberry"]

CODE = 'def total(items):\n    return sum(i.price for i in items)\n'

PROSE = (
    "The tokenizer does not know what a word is. It knows which byte sequences "
    "showed up together often enough during training to deserve their own number."
)


def letter_story(tok, word, letter):
    """How the model sees a word it's being asked to spell."""
    toks = tok.tokens(word)
    return {
        "word": word,
        "letter": letter,
        "true_count": word.lower().count(letter),
        "tokens": toks,
        "token_count": len(toks),
    }


def main():
    o200k = Reference("o200k_base")
    cl100k = Reference("cl100k_base")

    data = {
        "generated_note": "All figures computed by .build/precompute.py from "
                          "gpt-tokenizer's reference (CJS) build, verified against "
                          "the shipped browser bundle by .build/verify.py.",
        "encodings": {
            "o200k_base": {"vocab": int(o200k.ctx.eval("M.vocabularySize")),
                           "used_by": "GPT-4o and o-series models"},
            "cl100k_base": {"vocab": int(cl100k.ctx.eval("M.vocabularySize")),
                            "used_by": "GPT-4 and GPT-3.5-turbo"},
        },
        "counting": [letter_story(o200k, w, l) for w, l in
                     zip(COUNTING_WORDS, ["r", "r", "k", "s", "s"])],
        "numbers": [{"text": n, "tokens": o200k.tokens(n)} for n in NUMBERS],
        "whitespace": [{"text": w, "tokens": o200k.tokens(w)} for w in WHITESPACE],
        "code": {"text": CODE, "tokens": o200k.tokens(CODE)},
        "prose": {"text": PROSE, "tokens": o200k.tokens(PROSE)},
        "languages": [],
    }

    english_tokens = None
    for name, code, script, text in UDHR:
        n_o = o200k.count(text)
        n_c = cl100k.count(text)
        if english_tokens is None:
            english_tokens = n_o
        data["languages"].append({
            "name": name, "code": code, "script": script, "text": text,
            "chars": len(text),
            "o200k": n_o,
            "cl100k": n_c,
            "chars_per_token": round(len(text) / n_o, 2),
            "vs_english": round(n_o / english_tokens, 2),
        })

    # Same text, two model generations. Newer vocabulary, fewer tokens for the
    # same meaning — most dramatically outside English.
    data["encoding_shift"] = [
        {"label": name, "text": text,
         "cl100k": cl100k.count(text), "o200k": o200k.count(text)}
        for name, text in [
            ("English prose", PROSE),
            ("Python", CODE),
            ("Japanese", UDHR[7][3]),
            ("Hindi", UDHR[10][3]),
            ("Arabic", UDHR[9][3]),
        ]
    ]

    with open(OUT, "w", encoding="utf-8") as fh:
        json.dump(data, fh, ensure_ascii=False, indent=1)

    print(f"wrote {OUT}")
    print(f"  o200k_base vocab: {data['encodings']['o200k_base']['vocab']:,}")
    print(f"  cl100k_base vocab: {data['encodings']['cl100k_base']['vocab']:,}")
    print("\n  language          chars  o200k  cl100k  chars/tok  vs EN")
    for r in data["languages"]:
        print(f"  {r['name']:<16}{r['chars']:>6}{r['o200k']:>7}{r['cl100k']:>8}"
              f"{r['chars_per_token']:>11}{r['vs_english']:>7}x")


if __name__ == "__main__":
    main()