Obsah
1. pgvector, embedding a sémantické vyhledávání (3. část: binární vektory)
3. Jaké typy vektorů se používají v běžných modelech?
4. Kvantování a embedding modely s binárními vektory
6. Jak interpretovat výsledek kvantování do binárního vektoru?
8. Podpora pro práci s binárními vektory v databázi PostgreSQL
9. Vytvoření tabulky obsahující sloupec s binárním vektorem
10. Vytvoření tabulky, naplnění daty a běžné dotazy nad tabulkou
11. Dotazy založené na výpočtu vzdálenosti vektorů
12. Podpora binárních vektorů a rozšíření pgvector v Pythonu
13. Programové vytvoření tabulky se sloupcem s binárními vektory
14. Zápis binárních vektorů do tabulky
15. Vyhledávání vektorů na základě jejich vzdálenosti
16. Využití odlišné metriky: Jaccardův index
17. Zjištění vzdálenosti vektorů přečtených z databáze
18. Vyhledávání vět s podobným významem
19. Repositář s demonstračními příklady
1. pgvector, embedding a sémantické vyhledávání (3. část: binární vektory)
Ve třetím článku o rozšíření databáze PostgreSQL nazvaném pgvector jsme si kromě dalších informací řekli, že toto rozšíření podporuje zpracování vektorů s prvky několika typů. Tyto typy jsou vypsány v následující tabulce i s jejich základními vlastnostmi:
| Jméno typu | Způsob uložení | Maximální počet dimenzí | Stručný popis |
|---|---|---|---|
| vector | 4×počet_dimenzí+8 | 2000 | prvky vektorů jsou typu float32/single (4 bajty) |
| halfvec | 2×počet_dimenzí+8 | 4000 | prvky vektorů jsou typu half float (2 bajty) |
| bit | počet_dimenzí/8+8 | 64000 | každý prvek je uložen v jediném bitu |
| sparsevec | 8×nenulové_prvky+16 | 1000 nenulových prvků | uloženy jsou jen nenulové prvky |
Práci s běžnými vektory (tedy typem vector) již známe. Prakticky stejným způsobem se pracuje i s vektory, které obsahují hodnoty reprezentované v systému plovoucí řádové čárky, ovšem s poloviční přesností (ovšem teoreticky dvojnásobnou délkou). Dnes se ovšem budeme zabývat typem bit, což jsou vektory, ve kterých je každý prvek uložen v jediném bitu. Těmto vektorům budeme zkráceně říkat binární vektory (binary vector).
2. Role binárních vektorů
Binární vektory se začaly využívat například v oblasti RAG databází. Ovšem je důležité si ujasnit, proč tomu tak je. Do RAG (resp. do klasické RAG bez podpory hybridního vyhledávání) se neukládají přímo texty, ale jejich vektorizované podoby. Vstupní text je rozdělen na menší části (ty se mohou překrývat atd.) a každá část je embedded modelem (což může být natrénovaná neuronová síť atd.) převedena do formy vektoru s pevným počtem numerických hodnot. Hodnoty prvků těchto vektorů nějakým způsobem charakterizují původní větu (například desátý prvek může znamenat, že se ve větě mluví o počasí atd.). Tyto vektory mají relativně velký počet prvků (= velký počet dimenzí), který začíná na hodnotě 256, ovšem typické embedded modely pracují s vektory délky 768, 1024 nebo i 2048 prvků.
Ovšem představme si, že máme k dispozici RAG s 50 miliony vektorů (což není nijak závratné číslo, může se například jednat o celou firemní dokumentaci). V případě, že tyto vektory mají 1024 dimenzí a každý prvek je typu float32, je nutné prohledávat 200GB dat. Pokud by se typ prvků nějakým způsobem snížil až na jediný bit, bude objem RAG databáze mnohem menší: 6,4 GB a nikoli 200 GB. Samozřejmě může být vyhledávání méně kvalitní, to je však nutné si ověřit vhodnou evaluací výsledků.
To ovšem není vše. I samotný výpočet podobnosti vektorů může být mnohem rychlejší, když se například použije Hammingova vzdálenost. Teoreticky je možné tuto vzdálenost vypočítat pouhými dvěma instrukcemi pro každých 64 bitů (a výpočet lze dále vektorizovat).
3. Jaké typy vektorů se používají v běžných modelech?
Embedded modely typicky produkují vektory s pevně nastaveným počtem prvků (dimenzí) i s předem známým typem prvků. To si ostatně můžeme relativně snadno ověřit. Zjistíme, jaké vektory jsou vytvářeny modelem paraphrase-MiniLM-L6-v2, s nímž jsme se již na stránkách Roota setkali. Postup bude jednoduchý – necháme si modelem vektorizovat nějakou větu a vypíšeme informace o výsledném vektoru:
from sentence_transformers import SentenceTransformer
def embeddings_info(embeddings):
print(f"Embeddings shape: {embeddings.shape}")
print(f"Embeddings type: {type(embeddings)}")
print(f"Embeddings dtype: {embeddings.dtype}")
print(f"Size in bytes: {embeddings.nbytes}")
print()
print(embeddings)
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
print(model)
sentence = "The rain in Spain falls mainly on the plain"
embeddings = model.encode(sentence)
embeddings_info(embeddings)
Tento skript nejdříve vypíše informace o modelu – je zde mj. vypsán i počet dimenzí 384:
SentenceTransformer(
(0): Transformer({'max_seq_length': 128, 'do_lower_case': False, 'architecture': 'BertModel'})
(1): Pooling({'word_embedding_dimension': 384, 'pooling_mode_cls_token': False, 'pooling_mode_mean_tokens': True, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
)
Následně se vypíše informace o výsledném vektoru. Ten má (logicky) 384 prvků. Ty jsou typu float32 a tudíž má výsledný vektor velikost 384×4=1536 bajtů:
Embeddings shape: (384,) Embeddings type: <class 'numpy.ndarray'> Embeddings dtype: float32 Size in bytes: 1536
Hodnoty prvků vektoru již tak zajímavé nejsou:
[ 3.47846448e-01 1.65168166e-01 8.08912635e-01 7.29866028e-01 1.33826625e+00 6.67686611e-02 -2.15223745e-01 -9.88872275e-02 -1.41452923e-01 -5.20125091e-01 -8.75896867e-03 -9.80804861e-02 -2.63392180e-01 4.02818382e-01 3.99409264e-01 -2.09711939e-01 -1.25677630e-01 -3.75879079e-01 3.30041766e-01 -2.89256871e-01 ... ... ... 4.38241959e-01 9.15510207e-02 2.27444828e-01 3.36013436e-01 9.32963118e-02 7.67658055e-01 1.58524215e-01 -4.34361637e-01 3.16234380e-01 4.19851571e-01 5.04068255e-01 3.12186956e-01 -6.38126731e-01 3.04055482e-01 -3.13980915e-02 1.73595082e-02]
Druhým modelem, který si vyzkoušíme, je model nazvaný mixedbread-ai/mxbai-embed-large-v1:
from sentence_transformers import SentenceTransformer
def embeddings_info(embeddings):
print(f"Embeddings shape: {embeddings.shape}")
print(f"Embeddings type: {type(embeddings)}")
print(f"Embeddings dtype: {embeddings.dtype}")
print(f"Size in bytes: {embeddings.nbytes}")
print()
print(embeddings)
model = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
print(model)
sentence = "The rain in Spain falls mainly on the plain"
embeddings = model.encode(sentence)
embeddings_info(embeddings)
Nyní jsou výsledky odlišné. Počet dimenzí je 1024 (mnohem více) a typ prvků je float16. To znamená, že vektor v paměti zabere 1024×2=2048 bajtů:
SentenceTransformer(
(0): Transformer({'max_seq_length': 512, 'do_lower_case': False, 'architecture': 'BertModel'})
(1): Pooling({'word_embedding_dimension': 1024, 'pooling_mode_cls_token': True, 'pooling_mode_mean_tokens': False, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
)
Embeddings shape: (1024,)
Embeddings type: <class 'numpy.ndarray'>
Embeddings dtype: float16
Size in bytes: 2048
[-1.535 0.489 0.8823 ... -0.2404 -0.4429 0.0883]
4. Kvantování a embedding modely s binárními vektory
Z předchozí dvojice demonstračních příkladů je zřejmé, že vektorizace vět (nebo delších textů) s využitím embedded modelů je sice možná a funguje velmi dobře (už mnoho let – byla používána již před nástupem velkých jazykových modelů), ovšem typ prvků použitých ve vektorech do značné míry závisí na konstrukci embedded modelu. Jakým způsobem se ovšem má postupovat v případě, že jsme (například) s embedded modelem spokojeni, ale budeme vyžadovat, aby výsledkem vektorizace byly binární vektory? Balíček SentenceTransformers umožňuje provádět takzvané kvantování, což v tomto konkrétním případě (potřebujeme binární vektory) znamená, že se sekvence hodnot typu float16/half float nebo float32 převede na „pouhé“ binární hodnoty 0 a 1. Tím pochopitelně přijdeme o přesnost vyhledávání podobných vektorů, ale v praxi se ukazuje, že může být výhodnější použít delší binární vektory (například s 2048 dimenzemi) oproti kratším vektorům s prvky typu float (například jen s 384 dimenzemi).
5. Ukázka kvantování
Podívejme se nyní na praktické využití kvantování v průběhu vektorizace. Opět použijeme model nazvaný mixedbread-ai/mxbai-embed-large-v1, který produkuje vektory s prvky typu float16. Ovšem při vektorizaci si vyžádáme kvantování na formát „binary“ popř. „ubinary“:
binary_embeddings = model.encode(sentence, precision="binary") embeddings_info(binary_embeddings) ubinary_embeddings = model.encode(sentence, precision="ubinary") embeddings_info(ubinary_embeddings)
Celý skript bude vypadat následovně:
from sentence_transformers import SentenceTransformer
def embeddings_info(embeddings):
print(f"Embeddings shape: {embeddings.shape}")
print(f"Embeddings type: {type(embeddings)}")
print(f"Embeddings dtype: {embeddings.dtype}")
print(f"Size in bytes: {embeddings.nbytes}")
print()
print(embeddings)
print()
print()
model = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
print(model)
sentence = "The rain in Spain falls mainly on the plain"
embeddings = model.encode(sentence)
embeddings_info(embeddings)
binary_embeddings = model.encode(sentence, precision="binary")
embeddings_info(binary_embeddings)
ubinary_embeddings = model.encode(sentence, precision="ubinary")
embeddings_info(ubinary_embeddings)
Po spuštění skriptu se opět nejdříve vypíše informace o modelu (tu již ovšem známe):
SentenceTransformer(
(0): Transformer({'max_seq_length': 512, 'do_lower_case': False, 'architecture': 'BertModel'})
(1): Pooling({'word_embedding_dimension': 1024, 'pooling_mode_cls_token': True, 'pooling_mode_mean_tokens': False, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
)
Výsledek první vektorizace bez kvantování (opět známe):
Embeddings shape: (1024,) Embeddings type: <class 'numpy.ndarray'> Embeddings dtype: float16 Size in bytes: 2048 [-1.535 0.489 0.8823 ... -0.2404 -0.4429 0.0883]
Výsledek vektorizace s kvantováním „binary“:
Embeddings shape: (128,) Embeddings type: <class 'numpy.ndarray'> Embeddings dtype: int8 Size in bytes: 128 [ -30 120 -112 109 -86 8 89 -118 73 53 81 -33 93 -29 -36 -31 -89 77 -32 -127 85 -86 -71 -57 28 -13 -30 4 110 124 -25 -15 71 80 -117 -113 116 116 -60 91 -98 79 -53 -27 30 89 36 107 34 13 87 -113 -39 -114 -40 -50 -120 -92 -43 32 37 -96 -114 6 -79 -115 -61 -42 19 74 -45 102 -111 121 0 -82 123 34 60 -91 -70 -69 -107 48 -66 64 47 -116 40 -23 12 -37 -2 -60 7 121 105 -94 56 -124 63 27 99 -50 80 -95 125 -29 50 -25 -124 -13 -52 -4 -104 -73 -10 -21 49 103 -50 -74 116 69 21 -101 77 -103]
Výsledek vektorizace s kvantováním „ubinary“:
Embeddings shape: (128,) Embeddings type: <class 'numpy.ndarray'> Embeddings dtype: uint8 Size in bytes: 128 [ 98 248 16 237 42 136 217 10 201 181 209 95 221 99 92 97 39 205 96 1 213 42 57 71 156 115 98 132 238 252 103 113 199 208 11 15 244 244 68 219 30 207 75 101 158 217 164 235 162 141 215 15 89 14 88 78 8 36 85 160 165 32 14 134 49 13 67 86 147 202 83 230 17 249 128 46 251 162 188 37 58 59 21 176 62 192 175 12 168 105 140 91 126 68 135 249 233 34 184 4 191 155 227 78 208 33 253 99 178 103 4 115 76 124 24 55 118 107 177 231 78 54 244 197 149 27 205 25]
6. Jak interpretovat výsledek kvantování do binárního vektoru?
V obou předchozích demonstračních příkladech je výsledkem „vektorizace“ textu vektor reprezentovaný typem n-rozměrné pole (nd-array), přičemž toto pole obsahuje 128 prvků typu signed byte nebo unsigned byte (podle toho, jakou metodu kvantizace jsme zvolili). Je tomu tak z toho důvodu, že knihovna Numpy a její datový typ nd-array sice podporuje prvky typu numpy.bool a dokonce nabízí funkci numpy.packbits, ovšem výhodnější může být uložení vždy osmi prvků binárního vektoru do hodnoty typu byte (se znaménkem nebo bez znaménka, to je již do značné míry implementační detail). To tedy znamená, že vektor se 128 prvky typu byte reprezentuje 128×8=1024 bitový vektor, což je přesně ten výsledek, který jsme vyžadovali.
7. Výsledný binární vektor
Pro zajímavost si v úvodní části článku ještě ukážeme skript, který po svém spuštění převede zadanou větu do podoby binárního vektoru a následně je celý tento vektor (v binární podobě) vypsán na terminál. Připomeňme si, že vektorizací do binárního vektoru vlastně získáme jednorozměrné pole hodnot typu byte, takže nám postačuje každý bajt převést na sekvenci osmi bitů a pro hezčí výsledek ještě přidáme logiku pro odřádkování provedené vždy po výpisu osmi bajtů (tj. 8×8=64 binárních symbolů):
for i, byte in enumerate(ubinary_embeddings):
print(format(byte, "08b"), end="")
if i % 8 == 7:
print()
Celý zdrojový kód tohoto demonstračního příkladu vypadá následovně:
from sentence_transformers import SentenceTransformer
def embeddings_info(embeddings):
print(f"Embeddings shape: {embeddings.shape}")
print(f"Embeddings type: {type(embeddings)}")
print(f"Embeddings dtype: {embeddings.dtype}")
print(f"Size in bytes: {embeddings.nbytes}")
print()
model = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
print(model)
sentence = "The rain in Spain falls mainly on the plain"
ubinary_embeddings = model.encode(sentence, precision="ubinary")
embeddings_info(ubinary_embeddings)
for i, byte in enumerate(ubinary_embeddings):
print(format(byte, "08b"), end="")
if i % 8 == 7:
print()
Po spuštění skriptu se nejdříve vypíšou informace o modelu i o provedené vektorizaci:
SentenceTransformer(
(0): Transformer({'max_seq_length': 512, 'do_lower_case': False, 'architecture': 'BertModel'})
(1): Pooling({'word_embedding_dimension': 1024, 'pooling_mode_cls_token': True, 'pooling_mode_mean_tokens': False, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
)
Embeddings shape: (128,)
Embeddings type: <class 'numpy.ndarray'>
Embeddings dtype: uint8
Size in bytes: 128
A následně se vypíše všech 1024 bitů. Vektorizovaná věta „The rain in Spain falls mainly on the plain“ ve formě binárního vektoru vypadá takto:
0110001011111000000100001110110100101010100010001101100100001010 1100100110110101110100010101111111011101011000110101110001100001 0010011111001101011000000000000111010101001010100011100101000111 1001110001110011011000101000010011101110111111000110011101110001 1100011111010000000010110000111111110100111101000100010011011011 0001111011001111010010110110010110011110110110011010010011101011 1010001010001101110101110000111101011001000011100101100001001110 0000100000100100010101011010000010100101001000000000111010000110 0011000100001101010000110101011010010011110010100101001111100110 0001000111111001100000000010111011111011101000101011110000100101 0011101000111011000101011011000000111110110000001010111100001100 1010100001101001100011000101101101111110010001001000011111111001 1110100100100010101110000000010010111111100110111110001101001110 1101000000100001111111010110001110110010011001110000010001110011 0100110001111100000110000011011101110110011010111011000111100111 0100111000110110111101001100010110010101000110111100110100011001
8. Podpora pro práci s binárními vektory v databázi PostgreSQL
Prozatím jsme pro výpočty podobnosti (resp. vzdálenosti) vektorů používali klasickou Eukleidovskou metriku, tedy skutečnou vzdálenost bodů v prostoru (v tomto případě vzdálenost koncových bodů vektorů v prostoru). Tato metrika se taktéž označuje L2, ovšem rozšíření pgvector podporuje i další metriky, které jsou vypsány v tabulce:
| Operátor | Stručný popis |
|---|---|
| <-> | Eukleidovská vzdálenost |
| <#> | skalární součin (jedna z nejdůležitějších matematických operací vůbec; vrací se hodnota s otočeným znaménkem!) |
| <=> | 1 – kosinus úhlu mezi vektory (rozsah –1 až 1 za předpokladu, že jsou vektory normalizovány!) |
| <+> | Manhattanská metrika (vzdálenost při pohybu pouze po mřížce) |
| <~> | Hammingova vzdálenost (používá se například u vektorizovaného textu) |
| <%> | Jaccardův index |
Při práci s binárními vektory se typicky používají poslední dvě metriky: Hammingova vzdálenost a Jaccardův index. Hammingova vzdálenost se vypočte relativně snadno, protože se jedná o počet odlišných odpovídajících si bitů dvou vstupních binárních vektorů. Pro výpočet se může použít operace XOR (velmi rychlá) následovaná operací typu VCNT (zjištění počtu jedničkových bitů). Tento výpočet se může provádět s celými slovy, tj. například po 64 bitech, což je mnohem rychlejší, než výpočet skalárního součinu nebo Eukleidovské vzdálenosti.
9. Vytvoření tabulky obsahující sloupec s binárním vektorem
Po přihlášení k běžící databázi (například přes konzoli psql) nejdříve pro jistotu zadáme příkaz pro povolení rozšíření realizované v pgvectoru:
test=> CREATE EXTENSION vector;
Nyní je již možné vytvářet tabulky, které mají ve svých sloupcích uloženy binární vektory. Specifikace takového sloupce musí obsahovat i délku vektoru v bitech. Příkladem může být tabulka se sloupcem embedding, která bude obsahovat binární čtyřbitové vektory:
test=> CREATE TABLE b1 (id bigserial PRIMARY KEY, embedding bit(4) NOT NULL); CREATE TABLE
Přesvědčíme se, zda byla tabulka skutečně přidána do schématu:
test=> \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+--------
public | b1 | table | tester
(1 row)
V dalším kroku si vypíšeme strukturu této tabulky, informace o indexech, integritní omezení atd.:
test=> \d b1
Table "public.b1"
Column | Type | Collation | Nullable | Default
-----------+--------+-----------+----------+--------------------------------
id | bigint | | not null | nextval('b1_id_seq'::regclass)
embedding | bit(4) | | not null |
Indexes:
"b1_pkey" PRIMARY KEY, btree (id)
10. Vytvoření tabulky, naplnění daty a běžné dotazy nad tabulkou
Pro vložení binárního vektoru se používá následující zápis, ve kterém jsou bity zapsány v řetězci:
test=> INSERT INTO b1 (embedding) VALUES ('0000'), ('1111'), ('1100'), ('0001');
Nyní by tabulka měla obsahovat čtyři řádky, o čemž se snadno přesvědčíme klasickým dotazem SELECT:
test=> SELECT * FROM b1; id | embedding ----+----------- 5 | 0000 6 | 1111 7 | 1100 8 | 0001 (4 rows)
11. Dotazy založené na výpočtu vzdálenosti vektorů
Nyní se dostáváme k velmi důležité vlastnosti rozšíření pgvector. V rámci tohoto rozšíření je totiž umožněno v podmínkách v příkazech SELECT, DELETE a UPDATE použít nové operátory. Jeden z těchto operátorů známe; zapisuje se znaky ↔ a používá se pro výpočet vzdálenosti dvou vektorů na základě standardní L2 metriky.
V případě binárních vektorů se používají další dva operátory s výpočtem Hammingovy vzdálenosti a popř. Jaccardova indexu (viz osmou kapitolu). Pokusme se tedy zjistit nejbližší vektory k vektoru 1110, přičemž pro výpočet bude použita Hammingova vzdálenost:
test=> SELECT * FROM b1 ORDER BY embedding <~> '1110' LIMIT 5; id | embedding ----+----------- 6 | 1111 7 | 1100 5 | 0000 8 | 0001 (4 rows)
A skutečně: první vektor se liší o jediný bit, druhý taktéž o jeden bit, další o tři bity, poslední o všechny čtyři bity.
Samozřejmě můžeme použít i jiný vektor:
test=> SELECT * FROM b1 ORDER BY embedding <~> '1000' LIMIT 5; id | embedding ----+----------- 5 | 0000 7 | 1100 8 | 0001 6 | 1111 (4 rows)
Vypočtenou vzdálenost (v bitech) si můžeme nechat vypsat:
test=> SELECT id, embedding, embedding <~> '0000' as distance FROM b1 order by distance; id | embedding | distance ----+-----------+---------- 5 | 0000 | 0 8 | 0001 | 1 7 | 1100 | 2 6 | 1111 | 4 (4 rows)
Dtto pro odlišný vektor:
test=> SELECT id, embedding, embedding <~> '1110' as distance FROM b1 order by distance; id | embedding | distance ----+-----------+---------- 6 | 1111 | 1 7 | 1100 | 1 5 | 0000 | 3 8 | 0001 | 4 (4 rows)
atd.:
test=> SELECT id, embedding, embedding <~> '0011' as distance FROM b1 order by distance; id | embedding | distance ----+-----------+---------- 8 | 0001 | 1 5 | 0000 | 2 6 | 1111 | 2 7 | 1100 | 4 (4 rows)
12. Podpora binárních vektorů a rozšíření pgvector v Pythonu
Již v úvodním článku o rozšíření pgvector jsme si ukázali, jakým způsobem je toto rozšíření použitelné v aplikacích naprogramovaných v Pythonu. Použili jsme přitom kombinaci nabízenou balíčkem psycopg2 s balíčkem pgvector. Naprosto stejné balíčky je možné využít i při práci s binárními vektory; dokonce ani nebude zapotřebí pro konstrukci vektorů používat knihovnu Numpy (i když se jedná o tranzitivní závislost rozšíření pgvector). V databázových dotazech, které budeme v Pythonu volat, je možné využít všechny nové operátory, tj. operátor pro L2 vzdálenost, Hammingovu vzdálenost i Jaccardův index.
Pro připomenutí si ukažme, jak se balíček pgvector instaluje a jaké má závislosti:
$ pip install --user pgvector Collecting pgvector Downloading pgvector-0.3.6-py3-none-any.whl.metadata (13 kB) Requirement already satisfied: numpy in /home/ptisnovs/.local/lib/python3.12/site-packages (from pgvector) (2.2.0) Downloading pgvector-0.3.6-py3-none-any.whl (24 kB) Installing collected packages: pgvector Successfully installed pgvector-0.3.6
13. Programové vytvoření tabulky se sloupcem s binárními vektory
V rámci předchozích testů byla ručně vytvořená tabulka nazvaná b1. Před provedením dalších operací ji vymažeme o ověříme si, že k jejímu smazání skutečně došlo:
$ psql -h localhost -p 54321 -U tester
Password for user tester:
psql (16.9, server 18.2 (Debian 18.2-1.pgdg12+1))
WARNING: psql major version 16, server major version 18.
Some psql features might not work.
Type "help" for help.
test=> drop table b1;
DROP TABLE
test=> \dt
Did not find any relations.
V dalším demonstračním příkladu naprogramovaném v Pythonu programově vytvoříme tabulku nazvanou (opět) b1. Tato tabulka bude obsahovat sloupec nazvaný embedding se čtyřprvkovými binárními vektory. Pro komunikaci s Postgresem se používá známá knihovna psycopg2:
import psycopg2
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
print(connection)
CREATE_TABLE_STATEMENT = """
CREATE TABLE IF NOT EXISTS b1 (
id bigserial PRIMARY KEY,
embedding bit (4) NOT NULL
);
"""
LIST_TABLES_QUERY = """
SELECT table_schema,table_name
FROM information_schema.tables
WHERE table_schema='public'
ORDER BY table_schema,table_name;
"""
with connection.cursor() as cursor:
print(CREATE_TABLE_STATEMENT)
cursor.execute(CREATE_TABLE_STATEMENT)
connection.commit()
print(LIST_TABLES_QUERY)
cursor.execute(LIST_TABLES_QUERY)
tables = cursor.fetchall()
for table in tables:
print(table)
Po spuštění tohoto demonstračního příkladu se nejdříve vypíše informace o připojení, dále se vypíšou oba prováděné příkazy a nakonec seznam tabulek (měl by obsahovat minimálně tabulku b1 ve schématu public:
$ uv run create_table_b1.py
<connection object at 0x7faafa93d6c0; dsn: 'user=tester password=xxx dbname=test host=localhost port=54321', closed: 0>
CREATE TABLE IF NOT EXISTS b1 (
id bigserial PRIMARY KEY,
embedding bit (4) NOT NULL
);
SELECT table_schema,table_name
FROM information_schema.tables
WHERE table_schema='public'
ORDER BY table_schema,table_name;
('public', 'b1')
14. Zápis binárních vektorů do tabulky
Nyní si ukažme, jakým způsobem se v Pythonu provádí zápis vektorů do tabulky b1. Binární vektory jsou v tomto případě jednoduše reprezentovány formou řetězců obsahujících pouze znaky „0“ a „1“. Pro zápis se používá běžná syntaxe příkazu INSERT (není zde použita žádná „vektorová specialita“):
import psycopg2
import numpy as np
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
vectors = [
"0000",
"1111",
"1100",
"0011",
]
with connection.cursor() as cursor:
for vector in vectors:
cursor.execute("INSERT INTO b1 (embedding) VALUES (%s)", (vector, ))
connection.commit()
Jen pro úplnost – spuštění tohoto skriptu můžeme provést například přes pdm nebo uv tak, jako tomu bylo i v demonstračních příkladech uvedených v předchozích článcích:
$ uv run insert_into_b1.py
15. Vyhledávání vektorů na základě jejich vzdálenosti
Nejdůležitější operací, kterou rozšíření pgvector nabízí, je pochopitelně vyhledávání vektorů na základě jejich vzdálenosti resp. podobnosti. V dalším demonstračním příkladu je ukázáno vyhledávání založené na Hammingově vzdálenosti. Konkrétně se budeme snažit najít maximálně pět vektorů, které jsou co nejvíce podobné binárnímu vektoru 0100. Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
with connection.cursor() as cursor:
cursor.execute("SELECT embedding FROM b1 ORDER BY embedding <~> %s::bit(4) LIMIT 5", ("0100", ))
records = cursor.fetchall()
for record in records:
print(record[0])
Výsledek by měl vypadat přesně takto (pořadí vektorů je pevně dané – od nejpodobnějšího k nejméně podobnému):
0000 1100 1111 0011
Ve skutečnosti není bezpodmínečně nutné, aby se v dotazu použilo přetypování ::bit(4). Rozšíření pgvector může typy operandů operátoru pro Hammingovu vzdálenost odvodit dynamicky, takže se demonstrační příklad (resp. dotaz, který je v něm použitý) zkrátí na:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
with connection.cursor() as cursor:
cursor.execute("SELECT embedding FROM b1 ORDER BY embedding <~> %s LIMIT 5", ("0100", ))
records = cursor.fetchall()
for record in records:
print(record[0])
Výsledek by měl být totožný s příkladem předchozím:
0000 1100 1111 0011
16. Využití odlišné metriky: Jaccardův index
Při vyhledávání vektorů na základě jejich podobnosti pochopitelně nemusíme počítat pouze Hammingovu vzdálenost. Namísto ní můžeme podobnost vektorů zjišťovat s využitím Jaccardova indexu, který se zapisuje formou <%>. Tento příklad uvádím především proto, aby bylo patrné, jak se do řetězce předávaného do databáze zapisuje znak procenta – při zápisu totiž musí dojít k jeho zdvojení:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
with connection.cursor() as cursor:
cursor.execute("SELECT embedding FROM b1 ORDER BY embedding <%%> %s LIMIT 5", ("0100", ))
records = cursor.fetchall()
for record in records:
print(record[0])
Pochopitelně si opět ukážeme, jaké vektory se vrátí a v jakém pořadí (bude odlišné):
1100 1111 0000 0011
17. Zjištění vzdálenosti vektorů přečtených z databáze
A konečně se podívejme na příklad, ve kterém se zjistí podobnosti (vzdálenosti) vektorů k vektoru specifikovanému a posléze se tyto vzdálenosti vypíšou. Podobný postup použijeme i v závěrečném příkladu, takže jen ve stručnosti:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
with connection.cursor() as cursor:
cursor.execute("SELECT embedding, embedding <~> '0000' AS distance FROM b1 ORDER BY distance")
records = cursor.fetchall()
for record in records:
print(record[0], record[1])
Po spuštění se vždy vypíše hodnota nalezeného binárního vektoru a vedle ní i vypočtená vzdálenost (v bitech):
0000 0.0 1100 2.0 0011 2.0 1111 4.0
18. Vyhledávání vět s podobným významem
Konečně se dostáváme k tomu, abychom propojili možnosti embedding modelů s rozšířením pgvector. Ukážeme si poslední dva demonstrační příklady. V příkladu prvním se vytvoří tabulka nazvaná b2. Tato tabulka bude obsahovat sloupec embedding s binárními vektory s 1024 prvky. Do této tabulky je přidáno sedm vektorizovaných vět, tj. vektorů, které byly vytvořeny embedding modelem:
import psycopg2
from sentence_transformers import SentenceTransformer
from pgvector.psycopg2 import register_vector
model = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
CREATE_TABLE_STATEMENT = """
CREATE TABLE IF NOT EXISTS b2 (
id bigserial PRIMARY KEY,
sentence TEXT NOT NULL,
embedding bit (1024) NOT NULL
);
"""
LIST_TABLES_QUERY = """
SELECT table_schema,table_name
FROM information_schema.tables
WHERE table_schema='public'
ORDER BY table_schema,table_name;
"""
with connection.cursor() as cursor:
cursor.execute(CREATE_TABLE_STATEMENT)
connection.commit()
cursor.execute(LIST_TABLES_QUERY)
tables = cursor.fetchall()
for table in tables:
print(table)
sentences = [
"The rain in Spain falls mainly on the plain",
"The tesselated polygon is a special type of polygon",
"The quick brown fox jumps over the lazy dog",
"To be or not to be, that is the question",
"It is a truth universally acknowledged...",
"How old are you?",
"The goat ran down the hill"
]
embeddings = model.encode(sentences, precision="ubinary")
print(f"Embeddings shape: {embeddings.shape}")
for sentence, vector in zip(sentences, embeddings):
print(type(vector), vector.shape)
ascii_vector = ""
for i, byte in enumerate(vector):
ascii_vector += format(byte, "08b")
print(ascii_vector)
with connection.cursor() as cursor:
cursor.execute("INSERT INTO b2 (sentence, embedding) VALUES (%s, %s)", (sentence, ascii_vector))
connection.commit()
V konzoli psql si ověříme, že tabulka existuje a jaký je její obsah:
test=> \d b2
Table "public.b2"
Column | Type | Collation | Nullable | Default
-----------+-----------+-----------+----------+--------------------------------
id | bigint | | not null | nextval('b2_id_seq'::regclass)
sentence | text | | not null |
embedding | bit(1024) | | not null |
Indexes:
"b2_pkey" PRIMARY KEY, btree (id)
test=> SELECT count(*) FROM b2;
count
-------
7
(1 row)
Obsah tabulky b2, ovšem bez vektorů:
test=> select id, sentence from b2; id | sentence ----+----------------------------------------------------- 1 | The rain in Spain falls mainly on the plain 2 | The tesselated polygon is a special type of polygon 3 | The quick brown fox jumps over the lazy dog 4 | To be or not to be, that is the question 5 | It is a truth universally acknowledged... 6 | How old are you? 7 | The goat ran down the hill (7 rows)
Celý obsah tabulky b2:
id | sentence | embedding ----+-----------------------------------------------------+---------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------- 1 | The rain in Spain falls mainly on the plain | 0110001011111000000100001110110100101010100010001101100100001010110010011011010111010001010111111101110101100011010111000110000100100111110011010110000000000001110101010010101000111001010001111001110001110011011000101000010011101110111111000110011101110001110001111101000000001011000011111111010011110100010001001101101100011110110011110100101101100101100111101101100110100100111010111010001010001101110101110000111101011001000011100101100001001110000010000010010001010101101000001010010100100000000011101000011000110001000011010100001101010110100100111100101001010011111001100001000111111001100000000010111011111011101000101011110000100101001110100011101100010101101100000011111011000000101011110000110010101000011010011000110001011011011111100100010010000111111110011110100100100010101110000000010010111111100110111110001101001110110100000010000111111101011000111011001001100111000001000111001101001100011111000001100000110111011101100110101110110001111001110100111000110110111101001100010110010101000110111100110100011001 2 | The tesselated polygon is a special type of polygon | 0110010011101101000101000110111000001010101000101010000000111100101010000001010101000011101111110111111101000010100100000100100100101110001011110010010101100001101101010010001110110011000110111101000100100011011000110101111101011111111110011111111101010000011011110110000001001010010011010100001000100011011111001001101001011110000111111011100101100111101100001001000110110100101000111110000100001111111101010000111000010011100011000001101001001111111010100010000101010100101011011011000110111010110111001101000011111001000101010000000111000110100100111000011101001010100011100001100011111000101101000110100011011011111101100011011111110101100001011101101000010100110000100000011011001001100010100100111010101100100000011100101001011111001011110000010010001111001110010110000000101110101101110010111011110101011000101010100101101011110000110100001011011111111110000110111111110101100001100000101101001111111111000001010010101111011111110001110110100011101100000110110010010010010000000101111010111001001100110010101101101110 3 | The quick brown fox jumps over the lazy dog | 0110000011111011000110011110111110000110101000010010100000111110000001110101001111001010011101000000111101010000111101110111001101011001011011010000010111000101110101011111000100111101101011110011111001011011001100111110110110011001011000001111111100111111111000011101000000000010110111011100011110110100011001010101000010110100110110110111101110000100001101011110001000111011111010100111100010101000100100010101111000111010101001110001100000101111100010000010101001111110111001110010001011011001011101010000010010111100010101100000001101000000001111011100101111010000111111100001001110010000101011100111100011010101010011110010001011100010001011000111100100011100010000111110101010100000010110000010110100110011010000101010101111000100010010010010111000100110100000001100111010000110011100100101111010000110101001101001001001101000001100110110000000001001110000000011001001100111100100001001101101011110100010111001101000001001011101010111011100100011111101110010111001101110000011001111111110011001010110110000111111011111 4 | To be or not to be, that is the question | 1001001110101101010001011001111010000010001000000010001111010010101111010011001111000111001111111000111100100010100101001101100100011100110011110011011111011011010111111011011110110111000000011101100001010011010101101101111110010001010011011111001011001100110001101011000100011010010101011110001000001111010011010101111101100110100001111001100111001001001101001111001110100000111000110101000010001110111000000101111111100001100111100001101011000101001010000110111000011111001110010010000110011010010101001000010101101101100011100000001101100100011110010110100101000000101011101001100010110001101001000111110011111011001101100001110111110101101010111110100100110000111100000110111110010000100011000110110000010100000101111111110110100011001001010010010010111101110110000011110111111001000011011111111111011101101000001100001010101100011101100101010101011111011000101000001000101011011101101011000110000101100011011010100010001001010100101111000110110011101100010101110011000010100101001010000000010100000000010001011100111100 5 | It is a truth universally acknowledged... | 0111101011101000010000011111110110101000111000001010011101111010111010110001100111110111111111111101110101101010101101011111000100101110111011010101011100000111110001000110001100110011000000010111001001100011011110100101111110110001111111000010011000111101110001010001000110100110011110010010001101101000010011111101111101100110100111101011100001000111111101101111000010110100100001111000000001001110011100011000001000000011101111110101101011000101011010010010011101101110011001001001001110000000100010000110011011100001101101110001001110000110010111000100111111100001111011000000110010111011100010101001100000011011001101100100010101110011101001011010001100011100101000000110101101100101010011110010100010111100000110100010101101001110101011010000010010111110100110011110110010001001001101010101000011100010110110001010011111000110000100111010010010111110011010010010100000000110000101101010001111010101111111000001101011100001010011110111111110000001100000011100110111110010100101001111111101001100110000111001011000110100 6 | How old are you? | 1001001111110101010111010011110100011000111100100110101111011110111011010011111100010111101101110010110100000010010101010111100100001111011011011101011101000110000101011111101110110011101100011101110001111011001101110101111110000111011011011010010001111101110010101001001000011000000101101101101001101111001011011101111100010110000001111010100101001101101101101110101110101000101010111101000000001010011101011001111001010000001011100100100011100001001010010110011001001101000110110101001101000010010101001001000100110101101101000000000110010100001100111100111101100001100101100100100110110000001000000111110011111000001101100100010011110100101010110011111100110000110000010011011100100010111011000110111000110000011000011111101101101010001011000010000100101101101110010101100110011010111111110001111010110000111100001010001101101100000000111101010010110111011001000001001000101011011001100001100111011101001010101011100000101011011111010001010111011001101100010110100010000110100101000111010000111000000101100001111000110001 7 | The goat ran down the hill | 1111011111110101100111001110111110001100000100000100100010011110010000010001110111110111010111010101001101110010100011001010000100000100001001010001000000100001010101000010000111111111001101010000000011011111101101000111110100110011101000011010111011100111101000111001001010001010100111010010010011000000000011011101100010110110011000111001011111010101101001011110001100111110100010011011001010101111100001011001010100010010100011101111010011101001101000000010100001111101101011010101000011111011011011010010011001010101100111110011000001100100011101110100011001011010011111100101000111111001101000000011100011010011101001100001100111010000100010011101100110001000011100000011111010000011111111011000110010110010100010001110110111000111110110000100001110100101110110111110101010110001001110110001011100011111000110111100011011011010110010110010000100011111001110001111001001001110100100001011001111111100100010011000111010000100011100110011110110000011111101110010110011100110110010001111110110110000000101101001011011011001 (7 rows)
Nejdůležitější je ovšem příklad poslední, protože v něm budeme zjišťovat podobnost vět. Na základě vstupu (věta) zjistíme, které věty v databázi jsou jí sémanticky nejbližší. Opět se zde využije kombinace embedding modelu (vektorizuje vstupní větu) s pgvector (vyhledávání na základě Hammingovy vzdálenosti):
import psycopg2
from sentence_transformers import SentenceTransformer
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="localhost", port=54321, user="tester", password="123qwe", dbname="test"
)
print(connection)
register_vector(connection)
model = SentenceTransformer("mixedbread-ai/mxbai-embed-large-v1")
print(model)
def find_similar_sentences(connection, query_sentence, k):
print(f"Query: {query_sentence}")
print(f"Most {k} similar sentences:")
vector = model.encode(query_sentence, precision="ubinary")
with connection.cursor() as cursor:
query = """
SELECT sentence
FROM b2
ORDER BY embedding <~> %s::bit(1024)
LIMIT %s
"""
ascii_vector = ""
for i, byte in enumerate(vector):
ascii_vector += format(byte, "08b")
cursor.execute(query, (ascii_vector, k))
records = cursor.fetchall()
for i, record in enumerate(records):
print(f" #{i}: {record[0]}")
print("-"*40)
find_similar_sentences(connection, "The quick brown fox jumps over the lazy dog", 3)
find_similar_sentences(connection, "quick brown fox jumps over lazy dog", 3)
find_similar_sentences(connection, "The quick brown fox jumps over the angry dog", 3)
find_similar_sentences(connection, "The quick brown cat jumps over the lazy dog", 3)
find_similar_sentences(connection, "What is your age?", 3)
find_similar_sentences(connection, "Shakespeare", 3)
find_similar_sentences(connection, "animal", 3)
find_similar_sentences(connection, "geometry", 3)
find_similar_sentences(connection, "weather", 3)
Výsledky získané pro devět vstupních vět vypadají následovně:
<connection object at 0x7f96a46447c0; dsn: 'user=tester password=xxx dbname=test host=localhost port=54321', closed: 0>
SentenceTransformer(
(0): Transformer({'max_seq_length': 512, 'do_lower_case': False, 'architecture': 'BertModel'})
(1): Pooling({'word_embedding_dimension': 1024, 'pooling_mode_cls_token': True, 'pooling_mode_mean_tokens': False, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
)
Query: The quick brown fox jumps over the lazy dog
Most 3 similar sentences:
#0: The quick brown fox jumps over the lazy dog
#1: The goat ran down the hill
#2: How old are you?
----------------------------------------
Query: quick brown fox jumps over lazy dog
Most 3 similar sentences:
#0: The quick brown fox jumps over the lazy dog
#1: The goat ran down the hill
#2: How old are you?
----------------------------------------
Query: The quick brown fox jumps over the angry dog
Most 3 similar sentences:
#0: The quick brown fox jumps over the lazy dog
#1: The goat ran down the hill
#2: It is a truth universally acknowledged...
----------------------------------------
Query: The quick brown cat jumps over the lazy dog
Most 3 similar sentences:
#0: The quick brown fox jumps over the lazy dog
#1: The goat ran down the hill
#2: How old are you?
----------------------------------------
Query: What is your age?
Most 3 similar sentences:
#0: How old are you?
#1: To be or not to be, that is the question
#2: It is a truth universally acknowledged...
----------------------------------------
Query: Shakespeare
Most 3 similar sentences:
#0: How old are you?
#1: To be or not to be, that is the question
#2: It is a truth universally acknowledged...
----------------------------------------
Query: animal
Most 3 similar sentences:
#0: The goat ran down the hill
#1: The quick brown fox jumps over the lazy dog
#2: How old are you?
----------------------------------------
Query: geometry
Most 3 similar sentences:
#0: The tesselated polygon is a special type of polygon
#1: How old are you?
#2: It is a truth universally acknowledged...
----------------------------------------
Query: weather
Most 3 similar sentences:
#0: The rain in Spain falls mainly on the plain
#1: How old are you?
#2: To be or not to be, that is the question
----------------------------------------
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python s nainstalovanými balíčky psycopg2 a pgvector byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V tabulce zobrazené níže jsou odkazy na jednotlivé příklady:
20. Odkazy na Internetu
- pgvector: vektorová databáze postavená na Postgresu
https://www.root.cz/clanky/pgvector-vektorova-databaze-postavena-na-postgresu/ - Rozšíření PostgreSQL jménem pgvector, embedding a sémantické vyhledávání (1. část)
https://www.root.cz/clanky/rozsireni-postgresql-jmenem-pgvector-embedding-a-semanticke-vyhledavani-1-cast/ - FAISS: knihovna pro rychlé a efektivní vyhledávání podobných vektorů
https://www.root.cz/clanky/faiss-knihovna-pro-rychle-a-efektivni-vyhledavani-podobnych-vektoru/ - FAISS: knihovna pro rychlé a efektivní vyhledávání podobných vektorů (2. část)
https://www.root.cz/clanky/faiss-knihovna-pro-rychle-a-efektivni-vyhledavani-podobnych-vektoru-2-cast/ - Knihovna FAISS a embedding: základ jazykových modelů
https://www.root.cz/clanky/knihovna-faiss-a-embedding-zaklad-jazykovych-modelu/ - Knihovna FAISS a embedding: základ jazykových modelů (2. část)
https://www.root.cz/clanky/knihovna-faiss-a-embedding-zaklad-jazykovych-modelu-2-cast/ - FAISS (Facebook AI Similarity Search)
https://en.wikipedia.org/wiki/FAISS - FAISS documentation
https://faiss.ai/ - Introduction to Facebook AI Similarity Search (Faiss)
https://www.pinecone.io/learn/series/faiss/faiss-tutorial/ - Faiss: Efficient Similarity Search and Clustering of Dense Vectors
https://medium.com/@pankaj_pandey/faiss-efficient-similarity-search-and-clustering-of-dense-vectors-dace1df1e235 - Cosine Distance vs Dot Product vs Euclidean in vector similarity search
https://medium.com/data-science-collective/cosine-distance-vs-dot-product-vs-euclidean-in-vector-similarity-search-227a6db32edb - F16C
https://en.wikipedia.org/wiki/F16C - FP16 (AVX-512)
https://en.wikipedia.org/wiki/AVX-512#FP16 - Top 8 Vector Databases in 2025: Features, Use Cases, and Comparisons
https://synapsefabric.com/top-8-vector-databases-in-2025-features-use-cases-and-comparisons/ - Is FAISS a Vector Database? Complete Guide
https://mljourney.com/is-faiss-a-vector-database-complete-guide/ - Vector database
https://en.wikipedia.org/wiki/Vector_database - Similarity search
https://en.wikipedia.org/wiki/Similarity_search - Nearest neighbor search
https://en.wikipedia.org/wiki/Nearest_neighbor_search#Approximation_methods - Decoding Similarity Search with FAISS: A Practical Approach
https://www.luminis.eu/blog/decoding-similarity-search-with-faiss-a-practical-approach/ - MetricType and distances
https://github.com/facebookresearch/faiss/wiki/MetricType-and-distances - RAG – Retrieval-augmented generation
https://en.wikipedia.org/wiki/Retrieval-augmented_generation - pgvector na GitHubu
https://github.com/pgvector/pgvector - Why we replaced Pinecone with PGVector
https://www.confident-ai.com/blog/why-we-replaced-pinecone-with-pgvector - PostgreSQL as VectorDB – Beginner Tutorial
https://www.youtube.com/watch?v=Ff3tJ4pJEa4 - What is a Vector Database? (neobsahuje odpověď na otázku v titulku :-)
https://www.youtube.com/watch?v=t9IDoenf-lo - PGVector: Turn PostgreSQL Into A Vector Database
https://www.youtube.com/watch?v=j1QcPSLj7u0 - Milvus
https://milvus.io/ - Vector Databases simply explained! (Embeddings & Indexes)
https://www.youtube.com/watch?v=dN0lsF2cvm4 - Vector databases are so hot right now. WTF are they?
https://www.youtube.com/watch?v=klTvEwg3oJ4 - Step-by-Step Guide to Installing “pgvector” and Loading Data in PostgreSQL
https://medium.com/@besttechreads/step-by-step-guide-to-installing-pgvector-and-loading-data-in-postgresql-f2cffb5dec43 - Best 17 Vector Databases for 2025
https://lakefs.io/blog/12-vector-databases-2023/ - Top 15 Vector Databases that You Must Try in 2025
https://www.geeksforgeeks.org/top-vector-databases/ - Picking a vector database: a comparison and guide for 2023
https://benchmark.vectorview.ai/vectordbs.html - Top 9 Vector Databases as of Feburary 2025
https://www.shakudo.io/blog/top-9-vector-databases - What is a vector database?
https://www.ibm.com/think/topics/vector-database - SQL injection
https://en.wikipedia.org/wiki/SQL_injection - Cosine similarity
https://en.wikipedia.org/wiki/Cosine_similarity - Euclidean distance
https://en.wikipedia.org/wiki/Euclidean_distance - Dot product
https://en.wikipedia.org/wiki/Dot_product - Hammingova vzdálenost
https://cs.wikipedia.org/wiki/Hammingova_vzd%C3%A1lenost - Jaccard index
https://en.wikipedia.org/wiki/Jaccard_index - Manhattanská metrika
https://cs.wikipedia.org/wiki/Manhattansk%C3%A1_metrika - FAISS (Facebook AI Similarity Search)
https://en.wikipedia.org/wiki/FAISS - FAISS documentation
https://faiss.ai/ - Introduction to Facebook AI Similarity Search (Faiss)
https://www.pinecone.io/learn/series/faiss/faiss-tutorial/ - Faiss: Efficient Similarity Search and Clustering of Dense Vectors
https://medium.com/@pankaj_pandey/faiss-efficient-similarity-search-and-clustering-of-dense-vectors-dace1df1e235 - Cosine Distance vs Dot Product vs Euclidean in vector similarity search
https://medium.com/data-science-collective/cosine-distance-vs-dot-product-vs-euclidean-in-vector-similarity-search-227a6db32edb - F16C
https://en.wikipedia.org/wiki/F16C - FP16 (AVX-512)
https://en.wikipedia.org/wiki/AVX-512#FP16 - Top 8 Vector Databases in 2025: Features, Use Cases, and Comparisons
https://synapsefabric.com/top-8-vector-databases-in-2025-features-use-cases-and-comparisons/ - Is FAISS a Vector Database? Complete Guide
https://mljourney.com/is-faiss-a-vector-database-complete-guide/ - FAISS and sentence-transformers in 5 Minutes
https://www.stephendiehl.com/posts/faiss/ - Sentence Transformer: Quickstart
https://sbert.net/docs/quickstart.html#sentence-transformer - Sentence Transformers: Embeddings, Retrieval, and Reranking
https://pypi.org/project/sentence-transformers/ - uv
https://docs.astral.sh/uv/ - A Gentle Introduction to Retrieval Augmented Generation (RAG)
https://wandb.ai/cosmo3769/RAG/reports/A-Gentle-Introduction-to-Retrieval-Augmented-Generation-RAG---Vmlldzo1MjM4Mjk1 - The Beginner’s Guide to Text Embeddings
https://www.deepset.ai/blog/the-beginners-guide-to-text-embeddings - What are Word Embeddings?
https://www.youtube.com/watch?v=wgfSDrqYMJ4 - How to choose an embedding model
https://www.youtube.com/watch?v=djp4205tHGU - What is a Vector Database? Powering Semantic Search & AI Applications
https://www.youtube.com/watch?v=gl1r1XV0SLw - How do Sentence Transformers differ from traditional word embedding models like Word2Vec or GloVe?
https://zilliz.com/ai-faq/how-do-sentence-transformers-differ-from-traditional-word-embedding-models-like-word2vec-or-glove - BERT (language model)
https://en.wikipedia.org/wiki/BERT_(language_model) - Levenštejnova vzdálenost
https://cs.wikipedia.org/wiki/Leven%C5%A1tejnova_vzd%C3%A1lenost - pgvector Tutorial: Integrate Vector Search into PostgreSQL
https://www.datacamp.com/tutorial/pgvector-tutorial - pgvectorbench
https://github.com/pgvectorBench/pgvectorBench - pgvector 0.4.0 performance
https://supabase.com/blog/pgvector-performance - IVFFlat (Inverted File Flat) Index
https://skyzh.github.io/write-you-a-vector-db/cpp-05-ivfflat.html - HNSW (Hierarchical Navigable Small Worlds) Index
https://skyzh.github.io/write-you-a-vector-db/cpp-06–02-hnsw.html - Vector Indexing: Your Guide to Understanding and Implementation
https://www.teradata.com/insights/ai-and-machine-learning/what-is-vector-index - Vector Indexing Explained: Everything You Need to Know
https://www.datastax.com/guides/what-is-a-vector-index - Voroného diagram
https://cs.wikipedia.org/wiki/Voron%C3%A9ho_diagram - Matplotlib Home Page
http://matplotlib.org/ - matplotlib (Wikipedia)
https://en.wikipedia.org/wiki/Matplotlib - The Case Against pgvector
https://alex-jacobs.com/posts/the-case-against-pgvector/ - Embedding Quantization
https://github.com/huggingface/sentence-transformers/blob/main/examples/sentence_transformer/applications/embedding-quantization/README.md - Binary and Scalar Embedding Quantization for Significantly Faster & Cheaper Retrieval
https://huggingface.co/blog/embedding-quantization - Embedding Quantization
https://www.sbert.net/examples/sentence_transformer/applications/embedding-quantization/README.html - Understanding Quantization in Embedding Models
https://www.linkedin.com/pulse/understanding-quantization-embedding-models-rob-murphy-v5gue/ - Optimization of embeddings storage for RAG systems using quantization and dimensionality reduction techniques
https://arxiv.org/html/2505.00105v1
