Egy belső IT-hibajegyrendszerben dolgozol. A felhasználó beír egy rövid hibaleírást, például: „Nem tudok nyomtatni, hálózati csatlakozási gond van.” A rendszer feladata, hogy visszaadja a korábbi, már megoldott jegyek közül a leghasznosabbakat, hogy a támogató kolléga gyorsan lássa a korábbi megoldást. Ez klasszikus visszakeresési (retrieval) feladat: nem osztályozunk, hanem rangsorolunk.
A megfigyelési egység egy-egy dokumentum, azaz egy korábbi hibajegy szövege. A bemenet egy új lekérdezés szövege, amelyet a rendszer ugyanabba a vektortérbe képez le, mint a korábbi jegyeket. A kimenet a dokumentumok hasonlóság szerint csökkenő sorba rendezett listája, amelyből a top találatot mutatjuk meg. Az ellenőrzés két szinten történik: (1) numerikusan, a koszinusz-hasonlóság kézzel számolt értékeivel; (2) tartalmilag, emberi relevanciajelöléssel, amely megmondja, hogy a találat valóban hasznos volt-e.
Ebben az alfejezetben kis, előre megadott vektorokon dolgozunk, hogy a műveleteket kézzel is ellenőrizni tudd. Az ilyen vektorokat a gyakorlatban egy beágyazó modell (embedding model) állítja elő, de a keresési logika ugyanaz, függetlenül attól, hogy a vektor honnan származik.
Mielőtt a megoldást megírnád, becsüld meg fejben: ha a lekérdezés a vektor, és a dokumentumok között van egy vektor, akkor várhatóan ez lesz-e az első találat? A számítás után térj vissza ehhez a hipotézishez.
A Szöveges baseline és ritka mátrix alfejezetben látott TF-IDF a szöveget szavankénti ritka vektorrá alakította: minden dimenzió egy-egy szó, és a legtöbb érték nulla. A sűrű beágyazás ezzel szemben a szöveget egy fix hosszúságú, jellemzően 100–1000 dimenziós valós vektorrá képezi le, ahol minden komponens általában nullától különböző, és a dimenziók nem feleltethetők meg közvetlenül egy-egy szónak. A vektor a szöveg használati mintázatát tömöríti: a hasonló kontextusban előforduló, hasonló jelentésű szövegek hasonló vektort kapnak.
A vektor dimenzióját -vel jelöljük, tehát . A dokumentumhalmazban dokumentum van, így a teljes mátrix alakja : minden sor egy dokumentum, minden oszlop egy dimenzió. A vektorkomponenseknek itt nincs fizikai mértékegységük; a modell belső reprezentációjának tekintjük őket.
Két -dimenziós vektor hasonlóságát a köztük lévő szög koszinuszaként mérjük. A képlet:
ahol
A koszinusz nem függ a vektorok hosszától, csak az irányuktól. Ha és , akkor a kézi számítás:
Az érték azonos irányt jelent. Ha a vektorok merőlegesek, például és , akkor a skalárszorzat , így a koszinusz is . Ha ellentétes irányúak, az érték .
A kereső gyakorlatban előre normalizáljuk a vektorokat: . Ekkor , és a koszinusz egyszerűen a normalizált vektorok skalárszorzata. Ez azért kényelmes, mert a keresés egyetlen mátrix-skalárszorzássá válik.
A legközelebbi szomszéd keresés formálisan: adott a lekérdezési vektor és a dokumentumvektorok; a cél a legnagyobb értékű dokumentumok megtalálása. A top találatot rangsorolva adjuk vissza.
A visszakeresés (retrieval) ennél tágabb feladat: a releváns dokumentumok megtalálása egy gyűjteményben. Fontos különválasztani két kérdést:
Ez a fejezet elsősorban az első kérdést tanítja; a második a nagy adatos, elosztott környezetekben válik kritikussá.
A pontos (exact) keresés minden dokumentumvektort összehasonlít a lekérdezéssel. A költség : dokumentum, dimenzió. Néhány ezer dokumentumig ez tökéletesen vállalható egy CPU-s környezetben, és garantáltan megtalálja a legjobb találatot.
Nagyobb gyűjteménynél ( milliós nagyságrend) a közelítő (approximate) módszerek jönnek szóba: fa-, gráf- vagy hash-alapú indexek, mint a HNSW vagy az LSH. Ezek gyorsabbak, de nem garantálják, hogy az abszolút legközelebbi szomszédot találják meg; a sebesség és a pontosság között trade-off van. A könyv fő útja kis adatos, ezért itt pontos keresést használunk, és az ANN-t csak megemlítjük.
A visszakeresés értékeléséhez emberi relevanciajelölés kell. Egy adott lekérdezéshez minden dokumentumra megmondjuk, hogy releváns-e (1) vagy sem (0). A leggyakoribb metrika a találati pontosság (precision@k):
Ez a metrika a Keresztvalidáció a függőségi szerkezethez alfejezetben tanult elvhez hasonlóan a feladat célját méri, nem a modell belső hibáját. Egyetlen lekérdezésre a precision@k értéke közül kerül ki; több lekérdezésre átlagolunk. A pontosság mellett érdemes a Standard error és konfidenciaintervallum alfejezetben tanult módon intervallumot is számolni, mert 10–20 lekérdezés kis minta.
A példa bemenete öt szintetikus hibajegy, amelyeket kézzel rendelünk hozzá háromdimenziós vektorokhoz. A dimenziók itt mesterségesek; a módszer mechanizmusát mutatják, nem egy valódi embedding kimenetét.
A dokumentumok és vektoraik:
| Azonosító | Szöveg | Vektor |
|---|---|---|
| d1 | „A nyomtató nem csatlakozik a hálózathoz” | |
| d2 | „Hálózati hiba a nyomtatónál” | |
| d3 | „A számla értéke hibás” | |
| d4 | „Számlázási probléma” | |
| d5 | „A képernyő villog” |
A lekérdezés: „Nem tudok nyomtatni, hálózati csatlakozási gond van”, vektora .
A keresést NumPy-val hajtjuk végre. A normalizált vektorokon a mátrix-skalárszorzat adja a koszinusz-hasonlóságokat.
import numpy as np
# Dokumentumvektorok, shape: (5, 3)
doc_vectors = np.array([
[1.0, 0.1, 0.0], # d1
[0.9, 0.15, 0.05], # d2
[0.0, 1.0, 0.1], # d3
[0.05, 0.95, 0.0], # d4
[0.1, 0.0, 1.0], # d5
])
query = np.array([0.8, 0.2, 0.0])
# Normalizálás: minden sor hossza 1 lesz
doc_norm = doc_vectors / np.linalg.norm(doc_vectors, axis=1, keepdims=True)
query_norm = query / np.linalg.norm(query)
# Normalizált vektorokon a skalárszorzat = koszinusz
cos_sim = doc_norm @ query_norm # shape: (5,)
ranking = np.argsort(-cos_sim) # csökkenő sorrend
print("Normalizált dokumentumnormák:", np.round(np.linalg.norm(doc_norm, axis=1), 6))
print("Koszinusz-hasonlóság:", np.round(cos_sim, 3))
print("Rangsor (0-indexelt dokumentum pozíció):", ranking)Szemléltető, elméletileg várt kimenet:
Normalizált dokumentumnormák: [1. 1. 1. 1. 1.]
Koszinusz-hasonlóság: [0.989 0.986 0.241 0.238 0.096]
Rangsor (0-indexelt dokumentum pozíció): [0 1 2 3 4]
A dokumentumvektorok szándékosan úgy vannak kialakítva, hogy a d1 és d2 a „nyomtatás-hálózat” témához, a d3 és d4 a „számlázás” témához tartozzon, a d5 pedig a „képernyő” témához. A lekérdezés a nyomtatási témához áll közel, ezért az első két találatnak d1-nek és d2-nek kell lennie.
A kimenet első sora a normalizálási invariant ellenőrzése: minden normalizált dokumentumvektor hossza (a lebegőpontos kerekítésből adódó tolerancián belül). Ez garantálja, hogy a mátrix-skalárszorzat valóban koszinusz-hasonlóságot ad.
A második sor a hasonlósági értékek. A d1-re kapott kézi kontrollja:
A d3-ra hasonlóan: , , így . A d5-re: , így . A rangsor tehát d1, d2, d3, d4, d5 — ez megfelel a tematikus elrendezésnek: a nyomtatási témájú dokumentumok elöl, a számlázási témájúak középen, a képernyő téma hátul.
A hasonlósági pontszám nem relevanciaítélet. Azt, hogy a d1 vagy a d2 valóban hasznos-e a felhasználó számára, emberi jelölés dönti el. A numerikus sorrend csak jelölteket ad.
Hasonlósági keresésnél különböztessük meg a koszinusz-hasonlóságot és az euklideszi távolságot; normalizált vektorokra a rangsoruk összefügg, nyers vektorokra nem általánosan.
1. Értelmezés. Igazold képlettel, hogy ha mindkét vektort ugyanazzal a pozitív skalárral szorozzuk, a koszinusz-hasonlóság nem változik. Miért fontos ez a gyakorlatban, ha az embedding-modell különböző hosszúságú vektorokat ad vissza?
2. Megvalósítás. Írj egy top_k_search
függvényt, amely megkapja a dokumentumvektorok
mátrixát, a lekérdezésvektort és a
értéket, majd visszaadja a legnagyobb koszinusz-hasonlóságú
dokumentum azonosítóját (például a "d1" sztringet)
csökkenő pontszám szerint. Futtasd a fenti példán
értékkel; az elvárt eredmény ["d1", "d2"].
3. Hibadiagnózis. Egy kolléga a hasonlóságot
normalizálás nélkül, np.dot(a, b) értékkel számolta. Adj
konkrét ellenpéldát, ahol ez a módszer téves rangsort ad. Használd a
lekérdezést és a
,
dokumentumokat. Számítsd ki mindkét módszerrel a sorrendet, és magyarázd
meg az eltérést.
4. Megváltozott feltétel és relevanciajelölési terv. Cseréld a lekérdezést a „számlázási gond” témára, például -ra. Számítsd ki kézzel a koszinusz-hasonlóságot d3-ra és d4-re, és add meg az új várható rangsort. Ezután írj egy emberi relevanciajelölési tervet: 10 lekérdezés, mindegyikhez a top 3 találat, bináris relevanciajelölés (0/1). Számítsd ki a precision@3 értékét az alábbi fiktív jelölési eredményekre: 10 lekérdezésből 4 esetben 3 releváns találat, 3 esetben 2, 2 esetben 1, 1 esetben 0 releváns találat volt a top 3-ban.
1. megoldás. Legyen skalár. A skalárszorzat és a norma homogenitásából:
A mind a számlálóban, mind a nevezőben kiesik. A gyakorlatban ez azt jelenti, hogy ha az embedding-modell egy dokumentumot hosszabb vagy rövidebb vektorral reprezentál, az a hasonlósági sorrendet nem befolyásolja, amíg az irány azonos.
2. megoldás. Függvény azonosítókkal; a dokumentumazonosítókat a mátrixszal párhuzamos listában adjuk át.
import numpy as np
def top_k_search(doc_vectors, doc_ids, query, k):
doc_norm = doc_vectors / np.linalg.norm(doc_vectors, axis=1, keepdims=True)
q_norm = query / np.linalg.norm(query)
cos_sim = doc_norm @ q_norm
top_indices = np.argsort(-cos_sim)[:k]
return [(doc_ids[i], round(float(cos_sim[i]), 3)) for i in top_indices]
doc_ids = ["d1", "d2", "d3", "d4", "d5"]
print(top_k_search(doc_vectors, doc_ids, query, 2))Szemléltető, elméletileg várt kimenet:
[('d1', 0.989), ('d2', 0.986)]
Ellenőrző invariant: a visszaadott lista hossza ; a pontszámok monoton csökkenőek; minden pontszám -ben van.
3. megoldás. A normalizálatlan skalárszorzat: , , tehát a téves rangsor d1 előbb, mint d2. A koszinusz-hasonlóság:
A helyes rangsor d2 előbb, mint d1. A normalizálatlan skalárszorzatot a vektorok hossza torzítja: a d1 hossza , a d2 hossza , ezért a hosszú d1 látszólag jobb, pedig az iránya távolabb van a lekérdezéstől. A koszinusz a hosszhatást kiküszöböli.
4. megoldás. A lekérdezés d3-ra és d4-re:
Az új várható rangsor: d4, d3, majd a többi alacsony értékkel. A d4 azért kerül d3 elé, mert a és a d4 iránya közelebb áll egymáshoz, mint a és a d3 iránya.
A relevanciajelölési terv: minden lekérdezéshez előre rögzítjük a top 3 találatot; egy annotátor minden találatra 1-et ad, ha a dokumentum megoldja a hibát, 0-t egyébként. A fiktív eredmények: lekérdezésnél releváns, -nál , -nél , -nél . A releváns találatok száma:
A precision@3:
A pontbecslés mellett érdemes megjegyezni, hogy 10 lekérdezés kicsi minta; a Standard error és konfidenciaintervallum alfejezetben tanultak szerint a konfidenciaintervallum széles lesz.
A leggyakoribb félreértés az, hogy a magas koszinusz-hasonlóság ténybeli igazságot bizonyít. Ez nem igaz. Az embedding a szövegek nyelvi-fogalmi közelségét tömöríti, nem a világra vonatkozó állítások igazságát. Ellenpélda: a „Franciaország fővárosa Párizs” és a „Franciaország fővárosa Róma” mondatok vektorai nagyon közel lehetnek egymáshoz, mert ugyanaz a nyelvi séma, ugyanazok a szavak — mégis az egyik állítás hamis. A retrieval rendszer visszaadhatja a hamis mondatot, ha a formai hasonlóság nagy. A relevanciaítélet ezért mindig emberi ellenőrzést kíván, és a hasonlósági pontszámot nem szabad kalibrált valószínűségként kezelni, ahogyan azt Kalibrált valószínűség és predikciós bizonytalanság alfejezetben is láttuk.
Az embedding a szöveget sűrű, fix dimenziós valós vektorrá képezi; a hasonlóságot a vektorok közötti szög koszinusza méri. A koszinusz a skalárszorzat és a normák hányadosa, értéke ; normalizált vektorokon egyszerű skalárszorzat. A legközelebbi szomszéd keresés a dokumentumvektorok és a lekérdezés közötti hasonlóságot rendezi csökkenő sorrendbe. Kis adatra a pontos keresés megfelelő; nagy adatra közelítő indexek kellenek, amelyek pontosságot áldoznak a sebességért. A retrieval minőségét emberi relevanciajelöléssel, például precision@k-val mérjük. A következő alfejezetben az LLM-mint sztochasztikus komponens és a külső modellek értékelése felé lépünk tovább.