pgvector, embedding a sémantické vyhledávání: binární vektory

Dnes
Doba čtení: 33 minut

Sdílet

Autor: Depositphotos
Poměrně často se ve světě embedding modelů a sémantického vyhledávání setkáme s binárními vektory namísto vektorů obsahujících prvky typu float či half float. Ukážeme si, jak se s binárními vektory manipuluje přes pgvector.

Obsah

1. pgvector, embedding a sémantické vyhledávání (3. část: binární vektory)

2. Role binárních vektorů

3. Jaké typy vektorů se používají v běžných modelech?

4. Kvantování a embedding modely s binárními vektory

5. Ukázka kvantování

6. Jak interpretovat výsledek kvantování do binárního vektoru?

7. Výsledný binární vektor

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

20. Odkazy na Internetu

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.

Poznámka: pokud bychom vektorizovali více textů současně, bylo by výsledkem dvourozměrné pole (matice). Počet řádků by odpovídal počtu vektorizovaných textů a počet sloupců by byl stále roven 128mi.

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):

Školení Hacking

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
----------------------------------------
Poznámka: na rozdíl od plnohodnotných vektorů  32bitovými prvky je ve výsledcích jediná chyba: věta „Shakespeare“ by měla být nejpodobnější větě „To be or not to be, that is the question“. Ovšem vektorizace s razantní kvantizací vedla k tomu, že model vlastně obsahuje pouze asi 3% (!!!) původních informací.

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:

# Demonstrační příklad Stručný popis příkladu Cesta
1 create_extension.py registrace rozšíření vector pro uživatele připojeného k databázi https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_extension.py
2 insert_into_v2.py zápis 2D vektorů do tabulky v2 https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/in­sert_into_v2.py
3 insert_normalized.py zápis normalizovaných 2D vektorů do tabulky normalized https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/in­sert_normalized.py
       
4 read_vectors1.py přečtení vektorů bez jejich konverze na skutečné vektory https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/re­ad_vectors1.py
5 read_vectors2.py důkaz, že přečtené vektory jsou vráceny jako řetězce https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/re­ad_vectors2.py
6 read_vectors3.py přečtení vektorů z databáze ve formě N-dimenzionálních polí https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/re­ad_vectors3.py
       
7 select_by_distance1.py výběr vektorů na základě jejich odlišnosti od zadaného vektoru https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance1.py
8 select_by_distance2.py dtto, ale s lepším naformátováním SQL dotazu https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance2.py
9 select_by_distance3.py výběr vektorů s nejmenší odlišností (vzdáleností) https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance3.py
10 select_by_distance4.py výběr vektorů se zadanou nejmenší odlišností (vzdáleností) https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance4.py
       
11 various_distances.py použití různých metrik na nenormalizované vektory https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/va­rious_distances.py
12 various_distances_normalized.py použití různých metrik na normalizované vektory https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/va­rious_distances_normalized­.py
       
13 create_table_v2.py vytvoření tabulky se sloupcem, který bude obsahovat dvouprvkové vektory https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_table_v2.py
14 create_table_v384.py vytvoření tabulky se sloupcem, který bude obsahovat vektory s 384 prvky https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_table_v384.py
16 create_embeddings_one_sentence.py vektorizace jediné věty na základě zvoleného modelu https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_embeddings_one_senten­ce.py
17 create_embeddings.py vektorizace většího počtu vět na základě zvoleného modelu https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_embeddings.py
18 sentence_into_v384.py vložení vektoru reprezentujícího jedinou větu do tabulky se sloupcem typu vektor https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tence_into_v384.py
19 sentences_into_v384.py vložení většího množství vět do tabulky se sloupcem typu vektor https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tences_into_v384.py
20 sentence_similarity1.py sémantické vyhledávání přes rozšíření pgvector https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tence_similarity1.py
       
21 create_table_v384B.py vytvoření tabulky se sloupcem typu vektor a sloupcem s původním textem https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_table_v384B.py
22 sentences_into_v384b.py vložení vektorizovaného i původního textu do tabulky v384b https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tences_into_v384b.py
23 sentence_similarity2.py sémantické vyhledávání přes rozšíření pgvector https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tence_similarity2.py
       
24 index-benchmark-1.py základ pro všechny další benchmarky https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/index-benchmark-1.py
25 index-benchmark-2.py benchmark pro změření rychlosti zápisu vektorů do tabulky https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/index-benchmark-2.py
26 index-benchmark-3.py vizualizace benchmarku pro změření rychlosti zápisu vektorů do tabulky https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/index-benchmark-3.py
27 index-benchmark-4.py změření rychlosti zápisů vektorů do tabulky v závislosti na počtu dimenzí https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/index-benchmark-4.py
28 index-benchmark-5.py změření rychlosti vyhledávání vektorů na základě jejich podobnosti https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/index-benchmark-5.py
29 index-benchmark-6.py změření rychlosti vyhledávání s využitím různých metrik podporovaných rozšířením pgvector https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/index-benchmark-6.py
       
30 create_table_b1.py vytvoření tabulky obsahující sloupec s binárními vektory https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/cre­ate_table_b1.py
31 insert_into_b1.py vložení několika binárních vektorů do tabulky https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/in­sert_into_b1.py
32 select_by_distance_b1_1.py výběr binárních vektorů na základě jejich odlišnosti od zadaného vektoru https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance_b1_1.py
33 select_by_distance_b1_2.py výběr bez provedení typové konverze https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance_b1_2.py
34 select_by_distance_b1_3.py výběr na základě Jaccardova indexu https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance_b1_3.py
35 select_by_distance_b1_4.py výběr s výpisem nalezené vzdálenosti https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/se­lect_by_distance_b1_4.py
       
36 binary_embeddings1.py výpis základních informací o vektorizaci modelem paraphrase-MiniLM-L6-v2 https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/bi­nary_embeddings1.py
37 binary_embeddings2.py výpis základních informací o vektorizaci modelem mixedbread-ai/mxbai-embed-large-v1 https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/bi­nary_embeddings2.py
38 binary_embeddings3.py vektorizace s využitím binární kvantizace https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/bi­nary_embeddings3.py
39 binary_embeddings4.py výpis výsledného binárního vektoru https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/bi­nary_embeddings4.py
       
40 sentences_into_b2.py vložení vektoru reprezentujícího jedinou větu do tabulky se sloupcem typu vektor https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tences_into_b2.py
41 sentence_similarity_b2.py sémantické vyhledávání přes rozšíření pgvector https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/sen­tence_similarity_b2.py
       
42 pyproject.toml soubor s definicí Pythonovského projektu použitého ve všech demonstračních příkladech https://github.com/tisnik/most-popular-python-libs/blob/master/pgvector/py­project.toml

20. Odkazy na Internetu

  1. pgvector: vektorová databáze postavená na Postgresu
    https://www.root.cz/clanky/pgvector-vektorova-databaze-postavena-na-postgresu/
  2. 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/
  3. 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/
  4. 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/
  5. Knihovna FAISS a embedding: základ jazykových modelů
    https://www.root.cz/clanky/knihovna-faiss-a-embedding-zaklad-jazykovych-modelu/
  6. 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/
  7. FAISS (Facebook AI Similarity Search)
    https://en.wikipedia.org/wiki/FAISS
  8. FAISS documentation
    https://faiss.ai/
  9. Introduction to Facebook AI Similarity Search (Faiss)
    https://www.pinecone.io/le­arn/series/faiss/faiss-tutorial/
  10. Faiss: Efficient Similarity Search and Clustering of Dense Vectors
    https://medium.com/@pankaj_pan­dey/faiss-efficient-similarity-search-and-clustering-of-dense-vectors-dace1df1e235
  11. 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
  12. F16C
    https://en.wikipedia.org/wiki/F16C
  13. FP16 (AVX-512)
    https://en.wikipedia.org/wiki/AVX-512#FP16
  14. 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/
  15. Is FAISS a Vector Database? Complete Guide
    https://mljourney.com/is-faiss-a-vector-database-complete-guide/
  16. Vector database
    https://en.wikipedia.org/wi­ki/Vector_database
  17. Similarity search
    https://en.wikipedia.org/wi­ki/Similarity_search
  18. Nearest neighbor search
    https://en.wikipedia.org/wi­ki/Nearest_neighbor_search#Ap­proximation_methods
  19. Decoding Similarity Search with FAISS: A Practical Approach
    https://www.luminis.eu/blog/decoding-similarity-search-with-faiss-a-practical-approach/
  20. MetricType and distances
    https://github.com/facebo­okresearch/faiss/wiki/Metric­Type-and-distances
  21. RAG – Retrieval-augmented generation
    https://en.wikipedia.org/wi­ki/Retrieval-augmented_generation
  22. pgvector na GitHubu
    https://github.com/pgvector/pgvector
  23. Why we replaced Pinecone with PGVector
    https://www.confident-ai.com/blog/why-we-replaced-pinecone-with-pgvector
  24. PostgreSQL as VectorDB – Beginner Tutorial
    https://www.youtube.com/wat­ch?v=Ff3tJ4pJEa4
  25. What is a Vector Database? (neobsahuje odpověď na otázku v titulku :-)
    https://www.youtube.com/wat­ch?v=t9IDoenf-lo
  26. PGVector: Turn PostgreSQL Into A Vector Database
    https://www.youtube.com/wat­ch?v=j1QcPSLj7u0
  27. Milvus
    https://milvus.io/
  28. Vector Databases simply explained! (Embeddings & Indexes)
    https://www.youtube.com/wat­ch?v=dN0lsF2cvm4
  29. Vector databases are so hot right now. WTF are they?
    https://www.youtube.com/wat­ch?v=klTvEwg3oJ4
  30. 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
  31. Best 17 Vector Databases for 2025
    https://lakefs.io/blog/12-vector-databases-2023/
  32. Top 15 Vector Databases that You Must Try in 2025
    https://www.geeksforgeeks.org/top-vector-databases/
  33. Picking a vector database: a comparison and guide for 2023
    https://benchmark.vectorvi­ew.ai/vectordbs.html
  34. Top 9 Vector Databases as of Feburary 2025
    https://www.shakudo.io/blog/top-9-vector-databases
  35. What is a vector database?
    https://www.ibm.com/think/to­pics/vector-database
  36. SQL injection
    https://en.wikipedia.org/wi­ki/SQL_injection
  37. Cosine similarity
    https://en.wikipedia.org/wi­ki/Cosine_similarity
  38. Euclidean distance
    https://en.wikipedia.org/wi­ki/Euclidean_distance
  39. Dot product
    https://en.wikipedia.org/wi­ki/Dot_product
  40. Hammingova vzdálenost
    https://cs.wikipedia.org/wi­ki/Hammingova_vzd%C3%A1le­nost
  41. Jaccard index
    https://en.wikipedia.org/wi­ki/Jaccard_index
  42. Manhattanská metrika
    https://cs.wikipedia.org/wi­ki/Manhattansk%C3%A1_metri­ka
  43. FAISS (Facebook AI Similarity Search)
    https://en.wikipedia.org/wiki/FAISS
  44. FAISS documentation
    https://faiss.ai/
  45. Introduction to Facebook AI Similarity Search (Faiss)
    https://www.pinecone.io/le­arn/series/faiss/faiss-tutorial/
  46. Faiss: Efficient Similarity Search and Clustering of Dense Vectors
    https://medium.com/@pankaj_pan­dey/faiss-efficient-similarity-search-and-clustering-of-dense-vectors-dace1df1e235
  47. 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
  48. F16C
    https://en.wikipedia.org/wiki/F16C
  49. FP16 (AVX-512)
    https://en.wikipedia.org/wiki/AVX-512#FP16
  50. 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/
  51. Is FAISS a Vector Database? Complete Guide
    https://mljourney.com/is-faiss-a-vector-database-complete-guide/
  52. FAISS and sentence-transformers in 5 Minutes
    https://www.stephendiehl.com/pos­ts/faiss/
  53. Sentence Transformer: Quickstart
    https://sbert.net/docs/qu­ickstart.html#sentence-transformer
  54. Sentence Transformers: Embeddings, Retrieval, and Reranking
    https://pypi.org/project/sentence-transformers/
  55. uv
    https://docs.astral.sh/uv/
  56. A Gentle Introduction to Retrieval Augmented Generation (RAG)
    https://wandb.ai/cosmo3769/RAG/re­ports/A-Gentle-Introduction-to-Retrieval-Augmented-Generation-RAG---Vmlldzo1MjM4Mjk1
  57. The Beginner’s Guide to Text Embeddings
    https://www.deepset.ai/blog/the-beginners-guide-to-text-embeddings
  58. What are Word Embeddings?
    https://www.youtube.com/wat­ch?v=wgfSDrqYMJ4
  59. How to choose an embedding model
    https://www.youtube.com/wat­ch?v=djp4205tHGU
  60. What is a Vector Database? Powering Semantic Search & AI Applications
    https://www.youtube.com/wat­ch?v=gl1r1XV0SLw
  61. 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
  62. BERT (language model)
    https://en.wikipedia.org/wi­ki/BERT_(language_model)
  63. Levenštejnova vzdálenost
    https://cs.wikipedia.org/wi­ki/Leven%C5%A1tejnova_vzd%C3%A1le­nost
  64. pgvector Tutorial: Integrate Vector Search into PostgreSQL
    https://www.datacamp.com/tu­torial/pgvector-tutorial
  65. pgvectorbench
    https://github.com/pgvector­Bench/pgvectorBench
  66. pgvector 0.4.0 performance
    https://supabase.com/blog/pgvector-performance
  67. IVFFlat (Inverted File Flat) Index
    https://skyzh.github.io/write-you-a-vector-db/cpp-05-ivfflat.html
  68. HNSW (Hierarchical Navigable Small Worlds) Index
    https://skyzh.github.io/write-you-a-vector-db/cpp-06–02-hnsw.html
  69. Vector Indexing: Your Guide to Understanding and Implementation
    https://www.teradata.com/insights/ai-and-machine-learning/what-is-vector-index
  70. Vector Indexing Explained: Everything You Need to Know
    https://www.datastax.com/guides/what-is-a-vector-index
  71. Voroného diagram
    https://cs.wikipedia.org/wi­ki/Voron%C3%A9ho_diagram
  72. Matplotlib Home Page
    http://matplotlib.org/
  73. matplotlib (Wikipedia)
    https://en.wikipedia.org/wi­ki/Matplotlib
  74. The Case Against pgvector
    https://alex-jacobs.com/posts/the-case-against-pgvector/
  75. Embedding Quantization
    https://github.com/huggin­gface/sentence-transformers/blob/main/exam­ples/sentence_transformer/ap­plications/embedding-quantization/README.md
  76. Binary and Scalar Embedding Quantization for Significantly Faster & Cheaper Retrieval
    https://huggingface.co/blog/embedding-quantization
  77. Embedding Quantization
    https://www.sbert.net/exam­ples/sentence_transformer/ap­plications/embedding-quantization/README.html
  78. Understanding Quantization in Embedding Models
    https://www.linkedin.com/pul­se/understanding-quantization-embedding-models-rob-murphy-v5gue/
  79. Optimization of embeddings storage for RAG systems using quantization and dimensionality reduction techniques
    https://arxiv.org/html/2505.00105v1

Autor článku

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