Tokenizace textu: základní operace při zpracování přirozeného jazyka

12. 3. 2024
Doba čtení: 18 minut

Sdílet

 Autor: Depositphotos
Seznámíme se s knihovnou Tiktoken, která umožňuje takzvanou tokenizaci textu. Jedná se o jednu ze základních operací používaných v systémech pro zpracování přirozeného jazyka, což je bouřlivě se rozvíjející oblast.

Obsah

1. Tokenizace textu: základní operace při zpracování přirozeného jazyka

2. Převod slov na tokeny

3. Tokenizace složených slov

4. Knihovna tiktoken

5. Instalace knihovny tiktoken

6. Ukázka tokenizace s využitím kódování cl100k_base

7. Interpunkční znaménka

8. Složená slova a kódování cl100k_base

9. Složená slova zakódovaná jediným tokenem

10. Kódování mezer

11. Kódování dalších speciálních znaků

12. Zpětný převod tokenů na text

13. Od cl100k_base k dalším enkodérům a modulům

14. Porovnání kódování cl100k_base s dalšími kodéry a moduly

15. Rychlost tokenizace

16. Rychlost zpětného převodu sekvence tokenů na text

17. Další tokenizéry

18. Speciální tokeny

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Tokenizace textu: základní operace při zpracování přirozeného jazyka

V současnosti se obor IT poměrně důrazně zaměřuje na problematiku zpracování přirozeného jazyka (Natural language processing, NLP), která byla zpopularizována i mezi širokou veřejností mj. i díky úspěšně nasazeným rozsáhlým jazykovým modelům (Large Language Model, LLM), mezi něž patří například GPT (OpenAI), PaLM, Gemini, LLaMA apod. Při zpracování přirozeného jazyka se používá několik standardních postupů a algoritmů, s nimiž se postupně seznámíme v navazující sérii článků. Na samotném začátku zpracování textu, ale i při ukládání „znalosti“ informací (jazykový korpus), se využívá takzvaná tokenizace. V dnešním článku se seznámíme se základními principy, na nichž je tato operace postavena a ukážeme si ji na příkladu knihovny tiktoken (i když dnes existuje celá řada podobně koncipovaných knihoven, ostatně o některých dalších knihovnách, které jsou používány, se dnes ještě zmíníme).

2. Převod slov na tokeny

Tokenizace má sice v informatice různý význam, ale v kontextu jazykových modelů se tímto slovem označuje převod textu (tedy sekvence znaků) na tokeny (tedy na sekvenci celočíselných hodnot), přičemž výsledek by měl být jednoznačný. Typicky také předpokládáme existenci opačné operace, tj. možnost převodu sekvence tokenů na běžný text. Existuje větší množství algoritmů pro konstrukci tokenů, ovšem mnoho algoritmů převádí jednodušší slova nebo velmi často používaná slova (popř. slova s mezerou) na jediný token. Tento postup uvidíme v praktických příkladech, které využívají enkodér nazvaný cl100k_base. Tento název je odvozen od faktu, že obsahuje slovník s přibližně 100000 tokeny reprezentujícími jednotlivá slova, slova s oddělovači, slovní předpony, přípony, speciální znaky atd.

Tokenizaci si můžete vyzkoušet na stránce https://tiktokenizer.vercel.app/. Pro jednoduchá slova a věty dostaneme:

Obrázek 1: Tokenizace jednoduché anglické věty.

3. Tokenizace složených slov

Některá delší slova, která jsou složena z prefixu, základu, suffixu atd., jsou ovšem (pokud se nejedná o velmi často používaná slova), převedena na několik tokenů, kde typicky jeden z tokenů představuje základ slova a další pak prefix či suffix. Ovšem u složitějších slov, například chemických názvů, názvů léků atd., se slova skládají z menších celků (2–4 znaky). Opět si to můžeme ukázat na příkladu:

Obrázek 2: Tokenizace věty se složenými slovy.

Poznámka: barevně jsou zvýrazněny jednotlivé tokeny, konkrétní hodnoty tokenů pak naleznete v pravé dolní části okna.

4. Knihovna tiktoken

Pro tokenizaci a popř. i pro zpětný převod tokenů na text lze využít větší množství knihoven. Tyto knihovny se od sebe odlišují jak svou implementací, tak i tím, zda jsou (či naopak nejsou) součástí větších programových celků pro realizaci systémů pro zpracování přirozeného jazyka (dnes typicky založených na LLM). Dnes se budeme primárně zabývat knihovnou nazvanou tiktoken, jejíž výhodou je fakt, že je velmi snadno použitelná a lze ji nainstalovat a používat samostatně, bez nutnosti instalace frameworků pro LLM. Navíc tato knihovna podporuje několik enkodérů textu na tokeny i několik specifických modelů (například GPT-4 atd.), což je problematika, k níž se ještě několikrát vrátíme.

5. Instalace knihovny tiktoken

Vzhledem k tomu, že knihovna tiktoken je dostupná na PyPI, je její instalace snadná. Buď je možné provést instalaci pro všechny uživatele:

$ pip3 install tiktoken

Nebo pouze pro aktivního uživatele:

$ pip3 install --user tiktoken

V průběhu instalace se stáhnou i další tranzitivní závislosti, ale není jich mnoho:

Collecting tiktoken
  Downloading tiktoken-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)
Collecting regex>=2022.1.18 (from tiktoken)
  Downloading regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (40 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.9/40.9 kB 839.4 kB/s eta 0:00:00
Collecting requests>=2.26.0 (from tiktoken)
  Downloading requests-2.31.0-py3-none-any.whl.metadata (4.6 kB)
Collecting charset-normalizer<4,>=2 (from requests>=2.26.0->tiktoken)
  Downloading charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (33 kB)
Requirement already satisfied: idna<4,>=2.5 in /usr/lib/python3/dist-packages (from requests>=2.26.0->tiktoken) (2.8)
Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/lib/python3/dist-packages (from requests>=2.26.0->tiktoken) (1.25.8)
Requirement already satisfied: certifi>=2017.4.17 in /usr/lib/python3/dist-packages (from requests>=2.26.0->tiktoken) (2019.11.28)
Downloading tiktoken-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.8 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 4.9 MB/s eta 0:00:00
Downloading regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (777 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 777.0/777.0 kB 2.7 MB/s eta 0:00:00
Downloading requests-2.31.0-py3-none-any.whl (62 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.6/62.6 kB 2.1 MB/s eta 0:00:00
Downloading charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (141 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 141.1/141.1 kB 3.2 MB/s eta 0:00:00
Installing collected packages: regex, charset-normalizer, requests, tiktoken
Successfully installed charset-normalizer-3.3.2 regex-2023.12.25 requests-2.31.0 tiktoken-0.6.0

Samotná knihovna tiktoken zabere po instalaci cca 6,5 megabajtu, přičemž největší soubor tvoří nativní knihovna s realizací algoritmu tokenizace a zpětného převodu tokenů na text (tato nativní knihovna je naprogramována v Rustu, výsledkem jsou velmi rychlé realizace všech operací):

~/.local/lib/python3.12/site-packages/tiktoken$ ls -sh1h
 
total 6,5M
 24K core.py
 20K _educational.py
 12K __init__.py
 16K load.py
 12K model.py
4,0K __pycache__
8,0K py.typed
 12K registry.py
6,4M _tiktoken.cpython-312-x86_64-linux-gnu.so

6. Ukázka tokenizace s využitím kódování cl100k_base

Podívejme se nyní na způsob tokenizace jednoduchého textu, konkrétně klasické zprávy „Hello world“, na tokeny. Pro tokenizaci bude využito kódování (resp. možná přesněji řečeno enkodér) cl100k_base. Toto jméno je odvozeno od faktu, že obsahuje přibližně sto tisíc tokenů, resp. přesněji řečeno mapování sekvence znaků na tokeny (z toho plyne, že neobsahuje všechna slova, to však nevadí, jak uvidíme dále).

Samotný skript, který tokenizaci provede, je až triviálně jednoduchý, což je ostatně jedna z dobrých vlastností knihovny tiktoken. Nejprve získáme potřebný enkodér a poté provedeme tokenizaci zavoláním metody encode. Výsledkem je seznam s tokeny, tj. s celočíselnými hodnotami:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
tokens = encoder.encode("Hello world")
print(tokens)

A takto vypadá výsledek:

[9906, 1917]
Poznámka: v tomto případě jsou enkodérem nalezena obě slova a každé je tedy reprezentováno jedinou celočíselnou hodnotou. Ne vždy tomu tak však musí být, jak si ostatně ukážeme v navazující kapitole.

Na tomto místě je vhodné upozornit na to, že pokud například nahradíme slovo „Hello“ za „hello“, bude se z pohledu enkodéru jednat o odlišné slovo, které bude zakódováno do odlišné sekvence tokenů. Ostatně si to můžeme velmi snadno ověřit:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
tokens = encoder.encode("Hello world")
print(tokens)
 
tokens = encoder.encode("hello world")
print(tokens)
 
tokens = encoder.encode("hello World")
print(tokens)
 
tokens = encoder.encode("Hello World")
print(tokens)

Výsledky naznačují, jaké tokeny odpovídají slovům „Hello“, „hello“, „World“ i „world“:

[9906, 1917]
[15339, 1917]
[15339, 4435]
[9906, 4435]
Poznámka: první spuštění bude trvat déle, neboť si knihovna Tiktoken musí stáhnout i specifikovaný enkodér.

7. Interpunkční znaménka

Ve skutečnosti má být zpráva „Hello world“ správně zapsána jako „Hello, world!“. To nás vede k problematice kódování interpunkčních znamének. Zkusme si tedy nechat tokenizovat zprávu i s oběma interpunkčními znaménky:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
tokens = encoder.encode("Hello, world!")
print(tokens)

Výsledkem v tomto případě již nebude sekvence dvou tokenů, ale tokenů čtyř, což znamená, že pokud sekvenci tokenů použijeme v systému pro zpracování přirozeného jazyka, může jazykový model „rozumět“ i těmto důležitým znakům:

[9906, 11, 1917, 0]

Jen pro zajímavost – poslední token má hodnotu 0. Jedná se o token reprezentující samotný vykřičník, o čemž se můžeme velmi snadno přesvědčit:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
tokens = encoder.encode("!")
print(tokens)

Výsledkem bude v tomto případě sekvence obsahující jediný token s hodnotou 0:

[0]
Poznámka: token číslo 1 odpovídá uvozovce.

8. Složená slova a kódování cl100k_base

Mnohá jednoduchá anglická slova jsou transformována do jediného tokenu. Ovšem u slov delších, popř. u slov, která obsahují často používaný základ, je tomu jinak. Taková slova jsou složena z tokenů odpovídajících slovům (či části slov), z nichž jsou složena. Opět si to ukažme, a to například na slovech „black“, „blue“, „bird“, „blackbird“ a „bluebird“:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
compound_words = (
        "bird",
        "black",
        "blue",
        "blackbird",
        "bluebird",
)
 
for word in compound_words:
    tokens = encoder.encode(word)
    print(f"{word:12}", tokens)

Výsledek ukazuje, jak slova „blackbird“ a „bluebird“ vznikla složením jednodušších slov:

bird         [23414]
black        [11708]
blue         [12481]
blackbird    [11708, 23414]
bluebird     [12481, 23414]

Další příklad, tentokráte slov se stejným prefixem:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
compound_words = (
        "inter",
        "intermedial",
        "intermediate",
        "intermediaries",
        "intermediation",
)
 
for word in compound_words:
    tokens = encoder.encode(word)
    print(f"{word:14}", tokens)

U posledních dvou slov prostřední token 4503 znamená „medi“:

inter          [2295]
intermedial    [2295, 2106, 532]
intermediate   [2295, 14978]
intermediaries [2295, 4503, 5548]
intermediation [2295, 4503, 367]

Známé dlouhé slovo je „internationalization“, ale zkusme si tokenizovat ještě delší slovo „counterrevolutionaries“:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
compound_words = (
        "unprofessionally",
        "internationalization",
        "counterrevolutionaries",
)
 
for word in compound_words:
    tokens = encoder.encode(word)
    print(f"{word:22}", tokens)

Kupodivu jsou tato slova stále reprezentována relativně malým množstvím tokenů:

unprofessionally       [359, 97235, 750]
internationalization   [98697, 2065]
counterrevolutionaries [8456, 96822, 5548]
Poznámka: existují i delší anglická slova, například názvy chemických sloučenin atd. Mé oblíbené je „hexakosioihexekontahexaphobia“ (strach z čísla 666), které není tokenizováno na [666] ale na, protože každý token zde představuje dvojici či trojici znaků:
[17757, 587, 437, 822, 7141, 327, 1247, 546, 1494, 327, 1366, 41163]

9. Složená slova zakódovaná jediným tokenem

Některá složená slova jsou používána tak často, že jsou reprezentována jediným tokenem. Příkladem takového slova je (v námi používaném enkodéru!) „firefox“ zatímco slovo „fireplace“ je používáno méně často a proto je zakódováno dvojicí tokenů. Ostatně si to můžeme velmi snadno ověřit spuštěním následujícího skriptu:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
compound_words = (
        "fire",
        "fox",
        "place",
        "fireplace",
        "firefox",
)
 
for word in compound_words:
    tokens = encoder.encode(word)
    print(f"{word:12}", tokens)

Slova „fire“, „fox“ a „place“ jsou reprezentována jediným tokenem, zatímco slovo „fireplace“ vzniklo (podle očekávání) spojením „fire+place“. Ovšem „firefox“ má vlastní token:

fire         [11029]
fox          [15361]
place        [2050]
fireplace    [11029, 2050]
firefox      [99012]

10. Kódování mezer

Většinou se setkáme i s tím, že existují vyhrazené tokeny pro sekvence mezer, které v automaticky zpracovávaných textech taktéž často nalezneme (i když se mnohdy tyto mezery odstraňují při preprocessingu). Jedna mezera mezi slovy je součástí tokenů slov, ale více mezer již potřebuje svůj vlastní token (či tokeny, pokud je mezer opravdu hodně). Opět si to otestujme, a to na známém příkladu textu „Hello world“, do něhož budeme vkládat mezery mezi obě slova:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
spaces = (
        "Hello world",
        "Hello  world",
        "Hello   world",
        "Hello    world",
        "Hello     world",
)
 
for word in spaces:
    tokens = encoder.encode(word)
    print(f"{word:16}", tokens)

Z výsledků je patrné, že větší množství mezer je zakódováno jediným tokenem (samozřejmě s nějakým limitem):

Hello world      [9906, 1917]
Hello  world     [9906, 220, 1917]
Hello   world    [9906, 256, 1917]
Hello    world   [9906, 262, 1917]
Hello     world  [9906, 257, 1917]

Dosažení limitu:

Hello                                                                                         world

Nyní bude tokenizace odlišná:

[9906, 5351, 5218, 1917]
Poznámka: mezera mezi slovy je ve skutečnosti součástí druhého slova. Viz rozdíl mezi tokeny pro slovo „world“ a „ world“:
hello            [15339]
world            [14957]
 world           [1917]
hello world      [15339, 1917]  <- zde se používá token slova " world" s mezerou

11. Kódování dalších speciálních znaků

Enkodér cl100k_base je primárně určen pro anglické texty, ale měl by zvládnout i použití různých speciálních znaků. Vyzkoušejme si tedy poněkud extrémní případ (který je ovšem taktéž nutné v praxi řešit), a to konkrétně tokenizaci části zdrojového kódu. Výsledek pravděpodobně nebude úsporný, ale minimálně by měl být korektní:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
source_code = '''
    """Ackermannova funkce."""
    if m == 0:
        return n + 1
    if n == 0:
        return A(m - 1, 1)
    return A(m - 1, A(m, n - 1))
'''
 
tokens = encoder.encode(source_code)
print("Tokens: ", len(tokens))
print(tokens)

Získáme sekvenci 57 tokenů, což je pro tak malý vstup poměrně vysoká hodnota naznačující, že tokenizér není pro tento vstup určen:

Tokens:  57
[198, 262, 4304, 56659, 92550, 12949, 69392, 346,
14781, 262, 422, 296, 624, 220, 15, 512, 286, 471,
308, 489, 220, 16, 198, 262, 422, 308, 624, 220,
15, 512, 286, 471, 362, 1278, 482, 220, 16, 11,
220, 16, 340, 262, 471, 362, 1278, 482, 220, 16,
11, 362, 1278, 11, 308, 482, 220, 16, 1192]
Poznámka: v případě, že je token uložen ve čtyřech bajtech, je délka tokenizovaného textu rovna 57×4=228 bajtům, což je více, než původní zdrojový text. U anglických textů je tomu přesně naopak.

12. Zpětný převod tokenů na text

Knihovna Tiktoken umožňuje provádět i zpětný převod tokenů na text. Může se přitom jednat o libovolnou sekvenci tokenů – ovšem ne vždy je (pochopitelně) výsledkem čitelný a gramaticky správný text. Ukažme si tedy jednoduchou de-tokenizaci:

import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
tokens = [9906, 1917]
text = encoder.decode(tokens)
print(text)
 
tokens = [9906, 11, 1917, 0]
text = encoder.decode(tokens)
print(text)
 
tokens = [9906, 11, 1917, 0, 0, 0, 0]
text = encoder.decode(tokens)
print(text)
 
tokens = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
text = encoder.decode(tokens)
print(text)
 
tokens = [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010]
text = encoder.decode(tokens)
print(text)

Výsledky vypsané po spuštění tohoto skriptu:

Hello world
Hello, world!
Hello, world!!!!
!"#$%&'()*+
indowlementpectash[i use.Fpec adoveception

Poslední dva řádky ukazují obsah tokenů 0–10 (běžné znaky, které v textu nalezneme) a 1000 až 1010 (což jsou části slov, takže výsledek nedává smysl).

13. Od cl100k_base k dalším enkodérům a modulům

cl100k_base je jen jedním z enkodérů umožňujících tokenizaci textu. Těchto enkodérů existuje více a některé jsou ve výchozím nastavení používány určitými modely. cl100k_base je používán v modelech GPT-3.4 i GPT-4. Naproti tomu modely DaVinci a Babbage (pro větší zmatek se tyto modely navíc číslují/verzují) používají buď taktéž cl100k_base nebo p50k_base či r50k_base, na základě zvolené konfigurace. Jak uvidíme v dalším článku, může být využití dekodérů s menším celkovým množstvím tokenů v některých případech výhodné (ostatně i proto se také nepoužívají enkodéry s naprosto všemi anglickými slovy reprezentovanými unikátními tokeny, i když je to teoreticky možné), to však již poněkud předbíháme.

14. Porovnání kódování cl100k_base s dalšími kodéry a moduly

Jednotlivé kodéry si pochopitelně můžeme mezi sebou snadno porovnat. V následujícím skriptu, který byl inspirován přímo dokumentací ke knihovně Tiktoken, je tokenizována věta a následně slovo „firefox“, a to s využitím různých „slovníků tokenů“. Ten nejčastěji používaný cl100k_base již známe (obsahuje cca 100000 tokenů pro slova i jejich části), ale zajímavé bude zjistit, jak vypadá tokenizace v případě použití r50k_base a p50k_base s menším množstvím tokenů ve slovníku. Lze předpokládat, že pro menší slovníky budou některá slova rozdělena na více tokenů:

import tiktoken
 
def compare_encodings(example_string: str) -> None:
    print(f'\nExample string: "{example_string}"')
 
    for encoding_name in ["r50k_base", "p50k_base", "cl100k_base"]:
        encoding = tiktoken.get_encoding(encoding_name)
        tokens = encoding.encode(example_string)
        num_tokens = len(tokens)
        bytes = [encoding.decode_single_token_bytes(token) for token in tokens]
 
        print()
        print(f"{encoding_name}: {num_tokens} tokens")
        print(f"token integers: {tokens}")
        print(f"token bytes: {bytes}")
    print()
 
 
compare_encodings("The quick brown fox jumps over the lazy dog.")
 
compare_encodings("firefox")

Výsledky pro anglickou větu vypadají stejně (co slovo, to token, následuje token pro tečku), ovšem hodnoty některých tokenů se od sebe odlišují:

r50k_base: 10 tokens
token integers: [464, 2068, 7586, 21831, 18045, 625, 262, 16931, 3290, 13]
token bytes: [b'The', b' quick', b' brown', b' fox', b' jumps', b' over', b' the', b' lazy', b' dog', b'.']
 
p50k_base: 10 tokens
token integers: [464, 2068, 7586, 21831, 18045, 625, 262, 16931, 3290, 13]
token bytes: [b'The', b' quick', b' brown', b' fox', b' jumps', b' over', b' the', b' lazy', b' dog', b'.']
 
cl100k_base: 10 tokens
token integers: [791, 4062, 14198, 39935, 35308, 927, 279, 16053, 5679, 13]
token bytes: [b'The', b' quick', b' brown', b' fox', b' jumps', b' over', b' the', b' lazy', b' dog', b'.']

U slova „firefox“ je však patrné, že toto slovo jako celek existuje pouze v cl100k_base, zatímco v případě menších slovníků muselo být slovo reprezentováno dvěma tokeny pro „fire“ a „fox“ (bez mezery):

Example string: "firefox"
 
r50k_base: 2 tokens
token integers: [6495, 12792]
token bytes: [b'fire', b'fox']
 
p50k_base: 2 tokens
token integers: [6495, 12792]
token bytes: [b'fire', b'fox']
 
cl100k_base: 1 tokens
token integers: [99012]
token bytes: [b'firefox']

15. Rychlost tokenizace

V některých oblastech je důležitá i rychlost tokenizace, i když v případě jazykových modelů jsou časově mnohem náročnější odlišné operace. Knihovna Tiktoken je z větší části naprogramována v Rustu a tak by rychlost tokenizace či zpětné tokenizace měla být poměrně vysoká (záleží ovšem na vnitřní struktuře slovníku tokenů). Ovšem i tento předpoklad si můžeme ověřit. Budeme opakovaně tokenizovat větu, která se převede na deset tokenů. Celkem tedy provedeme tokenizaci textu s výsledkem 1 milion tokenů a budeme přitom měřit dosažený čas:

from time import perf_counter
import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
start_time = perf_counter()
 
token_count = 0
for i in range(100000):
    tokens = encoder.encode("The quick brown fox jumps over the lazy dog.")
    token_count += len(tokens)
 
end_time = perf_counter()
 
seconds = end_time-start_time
 
print("Tokens:", token_count)
print("Time:", seconds, "seconds")
 
speed = int(token_count / seconds)
print(speed, "tokens per second")

Na počítači s mikroprocesorem řady i7 lze dosáhnout rychlosti větší než milion tokenů za sekundu:

Tokens: 1000000
Time: 0.7104394719935954 seconds
1407579 tokens per second
Poznámka: tokenizace běží na jediném jádře; podpora pro souběžné zpracování na více jádrech není v této knihovně dostupná.

16. Rychlost zpětného převodu sekvence tokenů na text

Zajímat nás může i rychlost zpětného převodu sekvence tokenů (celých čísel) na text. Teoreticky by se mělo jednat o rychlejší operaci, protože se jedná o přímé mapování. Ale vyzkoušejme si to v praxi:

from time import perf_counter
import tiktoken
 
encoder = tiktoken.get_encoding("cl100k_base")
 
start_time = perf_counter()
 
tokens = encoder.encode("The quick brown fox jumps over the lazy dog.")
 
characters_count = 0
for i in range(100000):
    text = encoder.decode(tokens)
    characters_count += len(text)
 
end_time = perf_counter()
 
seconds = end_time-start_time
 
print("Characters:", characters_count)
print("Time:", seconds, "seconds")
 
speed = int(characters_count / seconds)
print(speed, "characters per second")

Rychlost je skutečně vyšší, než v případě tokenizace (i když vlastně měříme dvě navzájem inverzní operace). Nicméně tokenizace 100000 vět trvala mnohem déle, než de-tokenizace stejné věty, opět 100000×:

Characters: 4400000
Time: 0.14141017908696085 seconds
31115157 characters per second
Poznámka: tato operace prakticky nikdy není úzkým hrdlem celého řetězce operací pro porozumění textu.

17. Další tokenizéry

V praxi se nepoužívá pouze knihovna Tiktoken, ale i relativně velké množství dalších knihoven určených pro tokenizaci. Velmi často se setkáme s knihovnou nazvanou BertTokenizer, která je součástí známého jazykového modelu Bidirectional Encoder Representations from Transformers (BERT). Jak pravděpodobně správně tušíte, je tokenizace BERTem stejně snadnou operací, jako tomu je v případě Tiktokenu:

bitcoin_skoleni

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("cesta")
text = "Hello, world!"
encoding = tokenizer.encode(text)
print(encoding)
print(tokenizer.convert_ids_to_tokens(encoding))

18. Speciální tokeny

Do textu, který je tokenizován, lze vkládat i různé značky, které jsou zpracovány specifickým způsobem souvisejícím s jazykovými modely. S tímto důležitým konceptem se seznámíme příště, ovšem alespoň zmínit se o něm musíme již dnes.

19. Repositář s demonstračními příklady

Zdrojové kódy všech popsaných demonstračních příkladů určených pro programovací jazyk Python 3 a knihovnu tiktoken byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs:

# Demonstrační příklad Stručný popis příkladu Cesta
1 tokenize_hello_world1.py tokenizace textu „Hello world“ https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/to­kenize_hello_world1.py
2 tokenize_hello_world2.py tokenizace textu „Hello world“ při změně velikosti písmen https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/to­kenize_hello_world2.py
3 tokenize_hello_world3.py tokenizace textu „Hello, world!“ (i s interpunkčními znaménky) https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/to­kenize_hello_world3.py
       
4 exclamation_mark.py tokenizace jediného znaku (vykřičníku) https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/ex­clamation_mark.py
5 compound_words1.py tokenizace složených slov https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/com­pound_words1.py
6 compound_words2.py tokenizace složených slov https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/com­pound_words2.py
7 compound_words3.py tokenizace složených slov https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/com­pound_words3.py
8 compound_words4.py složená slova zakódovaná jediným tokenem https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/com­pound_words4.py
9 spaces1.py tokenizace mezer https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/spaces1.py
10 spaces2.py tokenizace mezer https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/spaces2.py
11 special_characters.py tokenizace speciálních znaků https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/spe­cial_characters.py
       
12 tokens_to_text.py převod tokenů zpět na text https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/to­kens_to_text.py
       
13 speed_tokenization.py změření rychlosti tokenizace https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/spe­ed_tokenization.py
14 speed_to_text.py změření rychlosti generování textu z tokenů https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/spe­ed_to_text.py
       
15 different_encoders.py použití odlišných enkodérů https://github.com/tisnik/most-popular-python-libs/blob/master/tiktoken/dif­ferent_encoders.py

20. Odkazy na Internetu

  1. tiktoken na GitHubu
    https://github.com/openai/tiktoken
  2. tiktoken na PyPi
    https://pypi.org/project/tiktoken/
  3. Byte pair encoding (Wikipedie)
    https://en.wikipedia.org/wi­ki/Byte_pair_encoding
  4. A Beginner’s Guide to Tokens, Vectors, and Embeddings in NLP
    https://medium.com/@saschametzger/what-are-tokens-vectors-and-embeddings-how-do-you-create-them-e2a3e698e037
  5. 5 Simple Ways to Tokenize Text in Python
    https://towardsdatascience.com/5-simple-ways-to-tokenize-text-in-python-92c6804edfc4
  6. How to count tokens with Tiktoken
    https://cookbook.openai.com/e­xamples/how_to_count_token­s_with_tiktoken
  7. An Explanatory Guide to BERT Tokenizer
    https://www.analyticsvidhy­a.com/blog/2021/09/an-explanatory-guide-to-bert-tokenizer/
  8. Tiktokenizer
    https://tiktokenizer.vercel.app/
  9. Mastering BERT Tokenization and Encoding
    https://albertauyeung.git­hub.io/2020/06/19/bert-tokenization.html
  10. The amazing power of word vectors
    https://blog.acolyer.org/2016/04/21/the-amazing-power-of-word-vectors/
  11. Which embedding tokenizer should I use?
    https://community.openai.com/t/which-embedding-tokenizer-should-i-use/82483
  12. Getting Started With Embeddings
    https://huggingface.co/blog/getting-started-with-embeddings
  13. Lexical analysis (Wikipedia)
    https://en.wikipedia.org/wi­ki/Lexical_analysis#Token
  14. Lexikální analýza (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Lexik%C3%A1ln%C3%AD_anal%C3%BDza
  15. Jazykový korpus
    https://cs.wikipedia.org/wi­ki/Jazykov%C3%BD_korpus
  16. AP8, IN8 Regulární jazyky
    http://statnice.dqd.cz/home:inf:ap8
  17. AP9, IN9 Konečné automaty
    http://statnice.dqd.cz/home:inf:ap9
  18. AP10, IN10 Bezkontextové jazyky
    http://statnice.dqd.cz/home:inf:ap10
  19. AP11, IN11 Zásobníkové automaty, Syntaktická analýza
    http://statnice.dqd.cz/home:inf:ap11
  20. PaLM 2 (Pathways Language Model)
    https://ai.google/discover/palm2/
  21. Gemini
    https://deepmind.google/techno­logies/gemini/#introducti­on
  22. LLaMA (Large Language Model Meta AI)
    https://en.wikipedia.org/wiki/LLaMA
  23. GPT-4
    https://openai.com/gpt-4

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.