Wyszukiwanie poprzez powiązania: model mentalny oparty na pamięci do wyszukiwania wektorowego
Dowiedz się, jak embeddingi, podobieństwo semantyczne, przybliżone wyszukiwanie najbliższego sąsiada oraz filtry metadanych współpracują ze sobą, korzystając z ludzkiej pamięci jako przewodnika.
Mogą minąć lata bez żadnego wspomnienia o nauczycielu z dzieciństwa. Nagle zapach kurzu od kredy, woń stołówki szkolnej lub piosenka grająca w autobusie w drodze do domu natychmiast i żywo przywracają tę osobę. Nic nie było szukane pod konkretnym nazwiskiem – pojawiło się coś podobnego, a wspomnienie samo się ujawniło.
To właśnie zachowanie stara się odtworzyć semantyczne wyszukiwanie, generacja wzbogacona o dane z bazy (RAG) oraz systemy rekomendacji oparte na AI, choć nie doskonale, ale z powagą. Elementem umożliwiającym to jest baza wektorowa. Ten artykuł wykorzystuje model mentalny oparty na powiązaniach, aby wyjaśnić, czym są wektory, jak mierzy się podobieństwo, jak skaluje się wyszukiwanie najbliższego sąsiada oraz jak poszczególne elementy tworzą proces odzyskiwania informacji. Wskazuje również, gdzie ta analogia przestaje być trafna, ponieważ to właśnie w tych miejscach systemy produkcyjne najczęściej popełniają błędy.
Zachowywanie według znaczenia zamiast nazwy
Pamięć ludzka nie posiada indeksu alfabetycznego. Nie istnieje żadna mentalna folder o nazwie „Jedzenie”, zawierająca podfolder „Włoskie” z plikiem o nazwie „Pizza”. To ścisła hierarchia służy do organizacji danych w konwencjonalnych bazach danych: każda rekord znajduje się pod znanym adresem i można ją odnaleźć za pomocą dokładnego klucza.
Pamięć jest natomiast organizowana według znaczenia i powiązań: na podstawie kontekstu, uczuć oraz sposobu, w jaki jedna rzecz jest powiązana z innymi. Idea pizzy łączy się z piątkowymi wieczorami, wycieczką do Neapolu, jedzeniem kojącym, rozciągającymi się pasami sera, zapachem oregano, a może także z kolegą z akademika, który zepsuł każdą partię domowego ciasta. Myślenie o pizzy nie otwiera pojedynczego pliku – aktywuje całą grupę powiązanych wspomnień, przy czym najsilniej powiązane pojawiają się pierwsze.
Baza danych wektorowych wykorzystuje dokładnie tę zasadę. Elementy są przechowywane według tego, co oznaczają, a są wyodrębniane na podstawie ich podobieństwa do zapytania, a nie tego, czy klucz pasuje dokładnie.
Co koduje wektor
Aby zrozumieć bazę danych, najpierw trzeba zrozumieć, co w niej przechowywane jest. Wektor to tutaj po prostu uporządkowana lista liczb reprezentująca znaczenie.
Maszyna nie ma żywego pojęcia o tym, czym jest pizza. To, czego może nauczyć się model embeddingowy poprzez przetwarzanie ogromnych ilości tekstu, to kontekst, w którym występuje dane słowo. „Pizza” pojawia się w pobliżu słów takich jak „ser”, „włoski”, „ciasto”, „piec” i „kawałek”. Ten kontekst różni się od kontekstu słowa „sushi”, ale mocno pokrywa się z kontekstem słów „chleb płaski” lub „calzone”.
Model sprowadza te wzorce do listy liczb o stałej długości, zwykle składającej się z kilkuset do kilku tysięcy wartości; typowymi rozmiarami są 384 i 1 536. Żadna pojedyncza liczba nie ma czytelnego oznaczenia, jednak razem umożliwiają one dokładne umieszczenie elementu w przestrzeni wysokowymiarowej. Kluczowa właściwość polega na tym, że dane o podobnym znaczeniu tworzą wektory znajdujące się blisko siebie. „Pizza” znajduje się w pobliżu „chleba płaskiego”, a bardzo daleko od „kwartalnego raportu finansowego”.
Innymi słowy, znaczenie przekształca się w odległość. Wszystko inne w tym artykule wynika z tej jednej koncepcji.
Podobieństwo jako spektrum, a nie dopasowanie
Klasyczne zapytanie ma charakter binarny: wiersz albo spełnia warunek, albo go nie spełnia. Jeśli zastosujesz filtr na „psa”, otrzymasz wiersze zawierające dokładnie słowo „psa”, natomiast żadne wyniki dla „szczeniaka”, „golden retrievera” czy „wiernego czworonogowego towarzysza”.
Podobieństwo semantyczne zastępuje tę odpowiedź typu tak/nie wynikiem, który pokazuje, jak bliskie są dwa znaczenia. Na przykład na skali od 0 do 1 „szczeniak” mógłby uzyskać ocenę 0,94 w porównaniu z „psem”, „wilkiem” około 0,71, a „fakturą” około 0,08. Metryką jest zazwyczaj podobieństwo kosinowe lub pokrewna odległość, takia jak iloczyn skalarny czy odległość euklidesowa; właściwy wybór zależy od sposobu szkolenia modelu embeddingowego.
To odzwierciedla efekt pyłu kredowego: nie jest to dokładne dopasowanie, lecz sygnał aktywujący pobliskie wspomnienia, które z kolei aktywują te obok nich. W bazie danych wektorowych mechanizm jest liczbowy – zapytanie zostaje przekształcone w wektor, a baza zwraca przechowywane elementy, których wektory znajdują się najbliżej niego. „Bliskość” oznacza podobieństwo znaczenia.
Dlatego też wyszukiwanie frazy „jak naprawić wolne zapytanie do bazy danych” może przywołać dokument zatytułowany „optymalizacja wydajności zapytań na dużą skalę”, mimo że obie frazy ledwo mają wspólne słowo. Ich wektory są blisko siebie, ponieważ wyrażają ten sam zamiar.
Uwaga dotycząca liczb
Punkty podobieństwa są względne, a nie bezwzględne. Wartość 0,8 uzyskana przy użyciu jednego modelu embeddingów nie jest porównywalna z wartością 0,8 uzyskaną przy użyciu innego modelu, a nawet w obrębie jednego modelu typowy zakres zależy od dziedziny. Traktuj punkty jako sposób na rankowanie kandydatów, a jeśli stosujesz próg decyzyjny, dostosuj go na podstawie własnych danych, zamiast wybierać liczbę zaokrągloną.
Korekta: zwykły SQL oparty na słowach kluczowych nie może wyrazić podobieństwa semantycznego, ale to nie oznacza, że bazy danych SQL są wykluczone. Rozszerzenia takie jak pgvector, omówione poniżej, dodają kolumny wektorowe i operatorzy podobieństwa do Postgresa, dzięki czemu ta funkcjonalność może istnieć wewnątrz bazy danych relacyjnej.
Poиск najbliższego sąsiada w skali
Jak baza danych faktycznie lokalizuje najbliższe wektory? Podstawową operacją jest poиск najbliższego sąsiada, a jego przyspieszenie to główny powód istnienia baz danych wektorowych.
Wyobraź sobie każdy przechowywany element jako gwiazdę w galaktyce, umieszczoną zgodnie z jej znaczeniem, przy czym powiązane elementy gromadzą się w tej samej strefie. Zapytanie dodaje nową gwiazdę do tej galaktyki i pyta, które pięć istniejących gwiazd jest najbliżej.
Prosty podejście mierzy odległość od zapytania do każdego przechowywanego wektora, sortuje wyniki i zwraca te najlepsze. W przypadku tysięcy wektorów jest to szybkie i w pełni wystarczające. Jednak przy milionach lub miliardach porównywanie ze wszystkimi staje się bardzo szybko kosztowne.
Z tego powodu systemy produkcyjne opierają się na algorytmach przybliżonego najbliższego sąsiada (ANN). Zamiast sprawdzać każdą gwiazdę, wykorzystują indeks, który ogranicza poszukiwania do obiecujących obszarów, akceptując niewielką stratę dokładności w zamian za znaczny wzrost szybkości. Najczęściej używanym obecnie algorytmem jest HNSW, skrót od Hierarchical Navigable Small World. Aby korzystać z bazy danych wektorowej, nie musisz znać jej wewnętrznych mechanizmów, ale powinieneś wiedzieć, że to właśnie dzięki niemu wyszukiwanie wśród setek milionów wektorów może odbywać się w ułamku sekundy.
Słowo „prawdopodobny” zasługuje na uwagę. Indeks ANN może czasami pominąć prawdziwego najbliższego sąsiada, a szybkość znajdowania rzeczywistych najlepszych wyników, nazywana powrotem (recall), zależy od parametrów indeksu, które kompromitują się pomiędzy pamięcią i opóźnieniem a dokładnością. Jeśli jakość wyszukiwania wydaje się w dużych skali niepojęcie nieregularna, warto sprawdzić ustawienia indeksu; nasz przewodnik dostosowywania indeksów HNSW do produkcji RAG szczegółowo omawia te ustawienia.
Wewnątrz magazynu embeddingów
W swojej istocie baza danych wektorowych to magazyn optymalizowany pod jedno zadanie: przechowywanie wektorów oraz bardzo szybkie przeprowadzanie wyszukiwań najbliższych sąsiadów wśród nich.
Załóżmy bibliotekę, która ignoruje System Dziesiętny Deweya i układa książki według tego, jak się one „czują”. Książki o smutku znajdują się obok książek o stratie, które z kolei są obok książek o samotności, potem izolacji, a następnie wspomnień z samodzielnych wypraw. Nikt nie ustalał tych kategorii ręcznie; takie uporządkowanie powstało dlatego, że czytelnicy przyciągnięci jedną tematyką zazwyczaj chcą poznać również inne.
Pinecone, Weaviate, Chroma i Qdrant to przykłady takiej biblioteki. Załadujesz do nich embeddingi dokumentów, opisy produktów, obrazy przekształcone w wektory lub wzorce zachowań użytkowników, a one utrzymują indeks, dzięki czemu wyszukiwania pozostają szybkie w miarę rozrastania się kolekcji.
Każdy przechowywany rekord składa się zazwyczaj z trzech części:
- ID, który jednoznacznie identyfikuje daną pozycję.
- Wektora reprezentującego jej znaczenie.
- Opcjonalnych metadanych, takich jak tytuł, data, kategoria lub adres URL źródła, które mogą być użyte do filtrowania wyników.
Metryki mają większą wartość, niż się na pierwszy rzut oka wydaje. Realistyczna prośba brzmi tak: znaleźć pięć dokumentów najbardziej podobnych do zapytania, ale tylko te z ostatnich 30 dni i wyłącznie z bazy wiedzy zespołu inżynieryjnego. To połączenie wyszukiwania wektorowego z filtrem metryk, od którego zależą większość systemów produkcyjnych. Sposób zastosowania filtru również ma znaczenie: filtrowanie po wyszukiwaniu podobieństwa może przynieść mniej wyników, niż prosiłeś, więc sprawdź, czy twoja baza danych filtrowała dane już podczas samego wyszukiwania.
Całkowity proces pozyskiwania informacji
Niezależnie od tego, czy znajduje się on w systemie RAG, funkcji wyszukiwania semantycznego, czy w dowolnej aplikacji wykorzystującej przechowywaną wiedzę, proces pozyskiwania informacji przebiega według tych samych czterech kroków.
- Zawieranie treści. Każdy dokument, artykuł, opis produktu lub zapis, który ma być dostępny do wyszukiwania, przechodzi przez model zawierania treści, a uzyskany wektor jest przechowywany obok oryginalnej treści. Długie dokumenty zazwyczaj najpierw są dzielone na fragmenty, ponieważ jeden wektor dla całego podręcznika łączy ze sobą zbyt wiele idei.
- Zawieranie zapytania za pomocą tego samego modelu. Nie jest to opcjonalne. Różne modele zawierania treści tworzą wektory w niespowiązanych przestrzeniach, więc porównywanie zapytania z jednego modelu z dokumentami z innego daje bezsensowne odległości. Zmiana modelu oznacza ponowne zawieranie całej kolekcji.
- Wyszukiwanie. Wektor zapytania trafia do bazy danych, gdzie wyszukiwanie najbliższych sąsiadów znajduje najbliższe przechowywane wektory, a jako wynik powracają najważniejsze rezultaty, zazwyczaj wraz z ocenami podobieństwa.
Z punktu widzenia użytkownika istotna odpowiedź pojawia się w ciągu chwili. W tle znaczenie tekstów zostało przekształcone w liczby, te liczby porównano z milionami przechowywanych elementów, wybrano najbliższe dopasowania i stworzono odpowiedź na podstawie autentycznego treści.
Dlaczego wyszukiwanie wektorowe jest teraz wszędzie
Niedawno bazy danych wektorowych były narzędziem niszowym, używanym wyłącznie przy tworzeniu systemów wyszukiwania semantycznego lub specjalistycznych systemów rekomendacji. Obecnie stanowią standardową część wielu aplikacji AI. RAG wymaga miejsca do przechowywania i wyszukiwania embeddingów dokumentów. Agenci przeszukują bazy wiedzy na podstawie znaczenia. Systemy rekomendacji na dużą skalę opierają się na podobieństwie wektorowym. Wyszukiwanie multimodalne, takie jak znajdowanie obrazów na podstawie opisu tekstowego lub produktów z przesłanej fotografii, również funkcjonuje za pomocą wektorów.
Jeśli budujesz aplikacje oparte na dużych modelach językowych w środowisku produkcyjnym, istnieje duże prawdopodobieństwo, że już teraz polegasz na wyszukiwaniu wektorowym lub wkrótce będziesz na to polegać. Pocieszające jest to, że podstawowa idea jest już znana: od zawsze pamięć wydobywa najbliższe powiązania z tym, co właśnie dotarło do naszych zmysłów. Baza danych wektorowych robi to samo z liczbami, w ułamku sekundy, dla milionów elementów.
Gdzie analogia z pamięcią zawodzi
Porównanie z mózgiem jest przydatne do intuicyjnego rozumienia, ale w praktyce istotne są pewne różnice:
- Pamięć dostosowuje się ciągle; model embedding jest nieruchomy. Jeśli słownictwo w danym obszarze się zmienia, wektory same się nie aktualizują.
- Pamięć bez trudu łączy kontekst; wektor rejestruje jedynie to, co do niego trafiło. Słabo podzielony lub zanieczyszczony tekst źródłowy daje słabe „sąsiady” wektorowe.
- Podobieństwo nie oznacza istotności. Dwa fragmenty mogą być podobne pod względem znaczenia, a jedynie jeden faktycznie odpowiada na pytanie – dlatego wiele systemów dodaje funkcję wyszukiwania słów kluczowych lub krok ponownego rankingu.
Praktyczne zastosowanie
Możesz eksperymentować bez konieczności budowania żadnej infrastruktury:
- ChromaDB działa lokalnie w środowisku Pythona bez konieczności tworzenia konta, klucza API czy procesu wdrażania, a jego instalacja i konfiguracja zajmują zaledwie kilka linijek kodu. Nadaje się do nauki oraz małych projektów.
- Qdrant oferuje bezpłatny poziom usług w chmurze oraz przyjaznego użytkownikowi klienta w Pythonie – jest doskonałym wyborem, gdy chcesz coś zbliżonego do środowiska produkcyjnego bez konieczności samodzielnego zarządzania serwerami. Sprawdź aktualne limity przed poleganiem na tych usługach.
- pgvector to rozszerzenie dla Postgresa. Jeśli już używasz Postgresa, umożliwia ono wyszukiwanie wektorowe bez konieczności tworzenia oddzielnego bazy danych, dzięki czemu cała architektura pozostaje prosta.
Niezależnie od wyboru, proces pracy jest identyczny: wybierasz model embeddingów, przekształcasz swój treść w wektory, przechowujesz je, przekształcasz przychodzące zapytania w wektory i przeprowadzasz wyszukiwanie. Koncepcje pozostają takie same; zmienia się jedynie biblioteka klienta.
Ten sam model po obu stronach oceny
Podobieństwo kosinusowe ma sens tylko wtedy, gdy zapytanie i fragment osadza ten sam model.
def cosine(a: list[float], b: list[float]) -> float:
dot = sum(x * y for x, y in zip(a, b))
na = sum(x * x for x in a) ** 0.5
nb = sum(y * y for y in b) ** 0.5
if na == 0 or nb == 0:
return 0.0
return dot / (na * nb)
# query and passage must come from the same embedding model
score = cosine(embed(query), embed(passage))
Filtr metadanych stoi przed wyszukiwaniem sąsiadów i decyduje, kto wchodzi do zbioru kandydatów.
hits = collection.query(
query_embeddings=[embed(query)],
n_results=8,
where={"tenant_id": tenant_id},
)
Główne wnioski
- Embedding przekształca znaczenie w pozycję, dzięki czemu podobne elementy znajdują się blisko siebie.
- Punkty podobieństwa sortują kandydatów; dostosuj próg samodzielnie na podstawie swoich danych.
- Indeksy ANN, takie jak HNSW, oferują niewielką dokładność w zamian za znaczne przyspieszenie przy dużych skaliach.
- Filtrzy metadanych przekształcają surowe dane o podobieństwie w odpowiedzi, które uwzględniają zasady czasowe, źródłowe i dostępowe.
- Zapytania i dokumenty muszą korzystać z tego samego modelu embeddingów, a jakość wyszukiwania zależy równie mocno od sposobu dzielenia na fragmenty i jakości danych, jak i od samej bazy danych.