Embedding je převod textu na seznam čísel (vektor) tak, že podobné významy leží blízko sebe. Je to most mezi jazykem a matematikou: jakmile je text bod v prostoru, můžeš počítat vzdálenosti, hledat nejbližší sousedy a shlukovat. Bez embeddingů by neexistovalo sémantické vyhledávání ani RAG, a to jsou dvě věci, které v praxi postavíš nejdřív.
Pár pojmů na úvod
- Vektor = uspořádaný seznam čísel, třeba 768 nebo 1536 hodnot. Nic víc.
- Dimenze = kolik čísel vektor má. Víc dimenzí = jemnější rozlišení, ale víc místa a času.
- Embedding model = model, který text na vektor převádí. Je to jiný model než ten, co generuje odpovědi (a bývá mnohonásobně levnější).
- Kosinová podobnost = číslo od −1 do 1, které říká, jak podobné dva vektory jsou. 1 = totéž téma, 0 = nesouvisí.
Význam jako souřadnice
Představ si mapu, kde má každý text svoje místo. Věty o stejné věci skončí ve stejné čtvrti, i když nemají společné ani jedno slovo:
"Jak zruším objednávku?" ─┐
"Chci vrátit zboží" ├─ tahle část mapy = reklamace a vratky
"Storno nákupu je možné do..." ─┘
"Jaké máte otevírací hodiny?" ── úplně jiná čtvrť
Tohle je zásadní rozdíl proti hledání podle slov. Klasické fulltextové hledání by u dotazu „zruším objednávku“ nenašlo dokument, který mluví o „stornu nákupu“, nemají společné slovo. Embeddingy ho najdou, protože jsou si blízké významem.
Všimni si, že A a B nesdílejí ani jedno stejné slovo. Klasické vyhledávání podle klíčových slov by je nespojilo, embeddingy ano.
Jak se s tím prakticky pracuje
1. text ──embedding model──▶ [0.021, -0.184, 0.77, ...] (vektor)
2. vektor + text ──────────▶ uložíš do vektorové databáze
3. dotaz ──embedding model──▶ [0.019, -0.201, 0.75, ...]
4. databáze vrátí nejbližší vektory ─▶ odpovídající texty
Klíčové pravidlo: dokumenty i dotaz musí projít stejným embedding modelem. Vektory z různých modelů spolu nejde porovnávat, je to jako měřit vzdálenost mezi bodem v Praze a bodem na Marsu.
⚠️ Když vyměníš embedding model, musíš přegenerovat celou databázi. Počítej s tím dopředu: ulož si u každého záznamu, jakým modelem a kdy byl vytvořen.
Vyrob si embeddingy z vlastního textu
Nepotřebuješ k tomu ani neuronovou síť. Stačí spočítat, která slova se vyskytují v podobném
okolí, a udělat z toho souřadnice. Do text.txt dej co nejvíc souvislého textu, ideálně aspoň
kolem megabajtu, jinak budou výsledky jen šum:
▶ Spustitelné. Ulož jako
embeddingy.pya pusťpython3 embeddingy.py. Potřebuješ jen NumPy a soubortext.txt.
"""Embeddingy z ničeho: co se vyskytuje v podobném okolí, to skončí blízko."""
import numpy as np, re, collections
text = open('text.txt', encoding='utf8').read().lower()
words = re.findall(r'[a-záčďéěíňóřšťúůýž]{3,}', text)
top = [w for w, _ in collections.Counter(words).most_common(3000)]
idx = {w: i for i, w in enumerate(top)}
N = len(top)
C = np.zeros((N, N)) # kolikrát se slova potkají v okně
W = 4
ids = [idx.get(w, -1) for w in words]
for i, a in enumerate(ids):
if a < 0: continue
for b in ids[i+1:i+1+W]:
if b >= 0: C[a, b] += 1; C[b, a] += 1
# PPMI: kolikrát častěji se potkávají, než kdyby byla slova nezávislá
tot = C.sum(); row = C.sum(1, keepdims=True)
with np.errstate(divide='ignore', invalid='ignore'):
P = np.log((C * tot) / (row * row.T))
P[~np.isfinite(P)] = 0
P = np.maximum(P, 0)
U, S, _ = np.linalg.svd(P, full_matrices=False)
E = U[:, :100] * np.sqrt(S[:100]) # embedding: 100 čísel na slovo
E /= np.linalg.norm(E, axis=1, keepdims=True) + 1e-9
def nejblizsi(w, n=5):
sim = E @ E[idx[w]] # kosinová podobnost
return ", ".join(f'{top[i]} {sim[i]:.2f}' for i in np.argsort(-sim)[1:n+1])
for w in ['token', 'paměť', 'server', 'chyba', 'soubor', 'model']:
if w in idx: print(f'{w:8s} -> {nejblizsi(w)}')
Na milionu znaků textu téhle učebnice vypadne tohle:
token -> pravděpodobnosti 0.63, tokenu 0.57, pravděpodobnost 0.54, další 0.54, softmax 0.53
paměť -> paměti 0.65, experti 0.60, vram 0.57, karty 0.55, zero 0.53
server -> klient 0.62, serveru 0.53, tvůj 0.48, prohlížeč 0.48, přiloží 0.47
soubor -> souboru 0.66, soubory 0.62, složky 0.57, txt 0.56, složce 0.53
model -> modelu 0.57, natrénovaný 0.53, tréninku 0.52, textu 0.51, text 0.49
Nikde v tom kódu není ani slovo o významu, gramatice nebo češtině. Přesto se soubor,
souboru a soubory sesypaly k sobě, protože se objevují ve stejných větách, a u paměť
vyskočilo vram a zero, tedy věci, které s pamětí souvisí, ale nejsou to synonyma.
To je přesně to, co embeddingy dělají a co je na nich užitečné i nebezpečné zároveň:
měří kontextovou příbuznost, ne totožnost a už vůbec ne pravdivost. server a klient
jsou si blízko právě proto, že jsou to protiklady vyskytující se v jedné větě.
Podobnost není totéž co pravda
Embedding hledá téma, ne správnost. Věty „Server běží“ a „Server neběží“ jsou si vektorově velmi blízké, protože jsou o tomtéž, přestože si odporují. Z toho plynou dvě pasti:
- Negace vyhledávání mate. Dotaz „produkty bez lepku“ může vytáhnout i texty o lepku.
- Čísla a přesné identifikátory embedding nezachytí. Objednávku
#48213hledej klasicky podle čísla, ne významem.
Proto se v praxi používá hybridní hledání: embeddingy najdou tematicky blízké kandidáty a klasický fulltext (klíčová slova, čísla, jména) je doplní. Detaily ve vektorové databázi.
Na co se embeddingy hodí kromě hledání
- Deduplikace: najdi téměř stejné texty (duplicitní tikety, opakované dotazy).
- Klasifikace bez trénování: porovnej dotaz s popisy kategorií a vyber nejbližší.
- Doporučování: „podobné články“ bez ručních štítků.
- Shlukování: o čem si zákazníci nejčastěji píšou, aniž bys to musel číst.
- Detekce odchylek: dotaz, který je daleko od všeho známého, si zaslouží pozornost člověka.
Na všechny tyhle úlohy platí totéž: embeddingy jsou levné a rychlé. Generativní model je řádově dražší, tak ho nezaměstnávej něčím, co zvládne vzdálenost dvou vektorů.
Shrnutí
- Embedding = text převedený na vektor tak, že podobný význam leží blízko.
- Embedding model je jiný (a levnější) než generativní; dotaz i dokumenty musí projít stejným.
- Změna modelu znamená přegenerovat celý index.
- Podobnost říká „je to o tomtéž“, ne „je to pravda“, negace a čísla řeš fulltextem.
Proč fulltextové hledání nenajde odpověď na dotaz „zruším objednávku“ v textu o „stornu nákupu“, ale embeddingy ano?
Fulltext porovnává slova a tyhle dva texty nemají žádné společné. Embeddingy porovnávají význam: oba texty se převedou na vektory, které leží blízko sebe, protože pojednávají o tomtéž.
Vyměnil jsi embedding model za novější. Co musíš udělat s daty?
Přegenerovat všechny uložené vektory. Vektory z různých modelů nejsou porovnatelné, takže míchání starých a nových by dalo nesmyslné výsledky. Proto se u záznamů eviduje, jakým modelem vznikly.
Zákazník hledá „produkty bez lepku“ a vyhledávání mu vrátí i pečivo s lepkem. Čím to je?
Embedding zachytí téma (lepek), ale ne negaci, vektor „bez lepku“ je velmi blízko vektoru „s lepkem“. Řeší se to hybridním hledáním s filtry: příznak bezlepkovosti patří do strukturovaných metadat, ne do sémantického hledání.
