Obsah
1. Rozšíření Postgresu pgvector, embedding a sémantické vyhledávání (1. část)
2. Proč se omezujeme pouze na sémantické vyhledávání jednotlivých vět?
3. Konfigurace a spuštění Postgresu
4. Projektový soubor využitelný pro všechny demonstrační příklady
5. Programové vytvoření tabulky, která obsahuje sloupec s vektory
6. Zápis vektorů do tabulky v2
7. Výběr pěti vektorů nejpodobnějších zadanému vstupu
8. Příprava tabulky pro uložení výsledku embeddingu: vektorů s 384 dimenzemi
9. Výsledky embeddingu kompatibilní s rozšířením pgvector
10. Vložení vektoru reprezentujícího jedinou větu do tabulky se sloupcem typu vektor
11. Embedding většího množství vět
12. Naplnění tabulky v384 výsledky vektorizace většího množství vět
13. Sémantické vyhledávání s využitím rozšíření pgvector
14. Výsledky sémantického vyhledávání
15. Databázová tabulka obsahující jak původní věty (texty), tak i jejich vektorizovanou podobu
16. Skript pro konstrukci embedding modelu původních vět s jejich uložením do databáze
17. Sémantické vyhledávání podruhé: nyní vracející původní věty
18. Výsledky vylepšeného sémantického vyhledávání
19. Repositář s demonstračními příklady
1. Rozšíření Postgresu pgvector, embedding a sémantické vyhledávání (1. část)
V dnešním článku si ukážeme, jakým způsobem je možné zkombinovat možnosti známé databáze PostgreSQL, jejího rozšíření pgvector a taktéž knihovny Sentence transformers (určené pro programovací jazyk Python), která dokáže převádět texty do podoby vektorů reprezentujících (samozřejmě, že pouze do určité míry) sémantiku těchto vět. Navážeme tak jak na úvodní článek o rozšíření pgvector [1], tak i na trojici článků o knihovně FAISS [2] [3] [4].
Postupně si v dnešním článku ukážeme několik kroků, které povedou k tomu, že na konci článku budeme mít v Postgresové tabulce uloženy věty (nebo jiné texty – kratší či delší) jak ve vektorizované podobě (sémantika), tak i v původní podobě, čehož bude možné později využít pro takzvané hybridní vyhledávání:
- Instalace rozšíření pgvector
- Povolení rozšíření pgvector v běžícím Postgresu
- Vytvoření tabulky se sloupcem typu „vektor“ (s pevným počtem prvků)
- Vektorizace vět s jejich uložením do této tabulky
- Sémantické vyhledávání v tabulce s využitím nových operátorů, které rozšiřují původní možnosti SQL Postgresu
- Vytvoření tabulky se sloupcem typu „vektor“ (s pevným počtem prvků) a sloupcem typu „text“
- Vektorizace vět s jejich uložením do této nové tabulky
- Sémantické vyhledávání v nové tabulce, vrátí se přímo nejpodobnější věty v jejich původní podobě
2. Proč se omezujeme pouze na sémantické vyhledávání jednotlivých vět?
Na tomto místě se může čtenář ptát, z jakého důvodu se vlastně omezujeme na převody (vektorizaci) jednotlivých vět; proč se například nejedná o jednotlivá slova, kratší slovní spojení, nebo naopak o celé odstavce nebo kapitoly? Modely, které přes knihovnu Sentence transformers používáme pro vektorizaci, převádí vstupní text (pokud problém značně zjednodušíme: bez ohledu na jeho délku) na vektor pevné délky, přičemž v jednotlivých prvcích tohoto vektoru je nějakým způsobem zakódována sémantika textu.
Pokud bychom pracovali s jednotlivými slovy nebo s krátkými slovními spojeními, může být vše po technické stránce v pořádku, ovšem otázkou je, k jakému účelu by taková databáze vektorů byla, protože by se pro daný vstup opět vracela pouze jednotlivá slova nebo slovní spojení (což může být pro některé účely dostačující, většinou – například při konstrukci RAG databáze – ovšem nikoli).
Problém může nastat u vektorizace delších textů, protože model tento text bez ohledu na jeho délku vždy transformuje do již zmíněného vektoru konstantní délky. Zde tedy musí dojít ke zobecnění textu a výsledky vyhledání podle kontextu nebudou přesné. U mnoha modelů navíc existuje limitní délka vstupních textů. Pokud se omezíme na jednotlivé věty, většinou na tuto délku nenarazíme (pokud se ovšem nejedná o schválně natahované věty – viz například známou knihu Obsluhoval jsem anglického krále).
3. Konfigurace a spuštění Postgresu
Nejdříve je nutné spustit a nakonfigurovat samotnou databázi PostgreSQL i její rozšíření pgvector. Celý postup již byl popsán v článku pgvector: vektorová databáze postavená na Postgresu, takže celý postup uvedu pouze ve stručnosti. Databáze PostgreSQL samozřejmě musí být nejdříve nainstalována, typicky s využitím balíčkovacího mechanismu vaší distribuce Linuxu.
Před instalací rozšíření pgvector zjistíme verzi Postgresu (a současně otestujeme, jestli je databáze nainstalována):
$ postgres --version nbsp; postgres (PostgreSQL) 16.x nebo 17.x.
V dalším kroku nainstalujeme rozšíření pgvector, typicky opět s využitím balíčkovacího mechanismu distribuce Linuxu. Číslo v názvu balíčku (zde konkrétně 17) musí odpovídat hlavnímu číslu verze samotného Postgresu:
$ sudo dnf install pgvector_17
Spustíme službu s Postgresem (pokud již neběží):
$ sudo service postgresql start Redirecting to /bin/systemctl start postgresql.service
Ověříme si, že služba skutečně běží:
$ service postgresql status
Výsledek může vypadat například takto:
Redirecting to /bin/systemctl status postgresql.service
● postgresql.service - PostgreSQL database server
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; preset: disabled)
Drop-In: /usr/lib/systemd/system/service.d
└─10-timeout-abort.conf
Active: active (running) since Thu 2025-08-07 12:51:07 CEST; 46s ago
Process: 2779867 ExecStartPre=/usr/libexec/postgresql-check-db-dir postgresql (code=exited, status=0/SUCCESS)
Main PID: 2779869 (postgres)
Tasks: 7 (limit: 37947)
Memory: 29.6M (peak: 31.9M)
CPU: 132ms
CGroup: /system.slice/postgresql.service
├─2779869 /usr/bin/postgres -D /var/lib/pgsql/data
├─2779871 "postgres: logger "
├─2779872 "postgres: checkpointer "
├─2779873 "postgres: background writer "
├─2779875 "postgres: walwriter "
├─2779876 "postgres: autovacuum launcher "
└─2779877 "postgres: logical replication launcher "
Aug 07 12:51:07 ptisnovs-thinkpadt14gen3.xyzzy.cz systemd[1]: Starting postgresql.service - PostgreSQL database server...
Aug 07 12:51:07 ptisnovs-thinkpadt14gen3.xyzzy.cz postgres[2779869]: 2025-08-07 12:51:07.555 CEST [2779869] LOG: redirecting log output to logging collector p>
Aug 07 12:51:07 ptisnovs-thinkpadt14gen3.xyzzy.cz postgres[2779869]: 2025-08-07 12:51:07.555 CEST [2779869] HINT: Future log output will appear in directory ">
Aug 07 12:51:07 ptisnovs-thinkpadt14gen3.xyzzy.cz systemd[1]: Started postgresql.service - PostgreSQL database server.
Pokud je vše v pořádku, nastavíme konfiguraci rozšíření pgvector, resp. si ověříme, jaká je jeho konfigurace:
$ cat `pg_config --sharedir`/extension/vector.control comment = 'vector data type and ivfflat and hnsw access methods' default_version = '0.6.2' module_pathname = '$libdir/vector' relocatable = true trusted = true
Posledním krokem, který je nutné udělat před založením „vektorové“ tabulky, je registrace rozšíření pro uživatele, který je přihlášen přes psql (ale i s využitím dalších klientů). Tato registrace probíhá příkazem CREATE (což může být poněkud matoucí):
test=> CREATE EXTENSION IF NOT EXISTS vector;
To, zda je rozšíření zaregistrováno, zjistíme takto (nezapomeňte na zápis příkazu včetně zpětného lomítka):
test=> \dx
List of installed extensions
Name | Version | Schema | Description
---------+---------+------------+------------------------------------------------------
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
vector | 0.6.2 | public | vector data type and ivfflat and hnsw access methods
(2 rows)
4. Projektový soubor využitelný pro všechny demonstrační příklady
Ve druhém kroku si, a to naprosto stejným způsobem, jako jsme to provedli u demonstračních příkladů využívajících knihovnu FAISS, připravíme projekt v Pythonu a následně do něj nainstalujeme všechny potřebné knihovny. Pro vytvoření projektu použijeme buď nástroj PDM – viz též PDM: moderní správce balíčků a virtuálních prostředí Pythonu nebo (což je v současnosti výhodnější) nástroj uv. Projekt bude vytvořen v novém (původně prázdném) adresáři a jeho projektový soubor pyproject.toml může vypadat následovně:
[project]
name = "sentence-transformer-and-pgvector"
version = "0.1.0"
description = "Sentence transformer and pgvector demos"
authors = [
{name = "Pavel Tisnovsky", email = "tisnik@centrum.cz"},
]
dependencies = [
]
requires-python = "==3.12.*"
readme = "README.md"
license = {text = "MIT"}
[tool.pdm]
distribution = false
Příkazem pdm add nebo uv add do projektu přidáme všechny potřebné balíčky:
$ uv add datasets sentence-transformers matplotlib psycopg2 pgvector
Výsledný projektový soubor by měl vypadat takto:
[project]
name = "sentence-transformer-and-pgvector"
version = "0.1.0"
description = "Sentence transformer and pgvector demos"
authors = [
{name = "Pavel Tisnovsky", email = "tisnik@centrum.cz"},
]
dependencies = [
"datasets>=4.0.0",
"matplotlib>=3.10.5",
"pgvector>=0.4.1",
"psycopg2>=2.9.10",
"sentence-transformers>=5.0.0",
]
requires-python = "==3.12.*"
readme = "README.md"
license = {text = "MIT"}
[tool.pdm]
distribution = false
5. Programové vytvoření tabulky, která obsahuje sloupec s vektory
Ukažme si nyní, jak je možné programově, konkrétně pouze s využitím Pythonu a modulu psycopg2, vytvořit v databázi PostgreSQL tabulku se sloupcem, do kterého bude možné zapisovat vektory s předem známým počtem dimenzí. Tabulku pojmenujeme v2, kde dvojka značí počet dimenzí a v pochopitelně znamená vector. Tato tabulka bude obsahovat pouze dva sloupce: sloupec s automaticky generovanými primárními klíči a již zmíněný sloupec určeny pro dvourozměrné vektory. Po vytvoření tabulky (pokud již neexistuje) se ještě vypíše seznam všech tabulek získaných z výchozího schématu public:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
print(connection)
CREATE_TABLE_STATEMENT = """
CREATE TABLE IF NOT EXISTS v2 (
id bigserial PRIMARY KEY,
embedding vector(2) 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)
Výsledek po spuštění tohoto skriptu by mohl vypadat následovně:
$ python create_table_v2.py
<connection object at 0x7fcbb3d2cb80; dsn: 'user=tester password=xxx dbname=test host='' port=5432', closed: 0>
CREATE TABLE IF NOT EXISTS v2 (
id bigserial PRIMARY KEY,
embedding vector(2) NOT NULL
);
SELECT table_schema,table_name
FROM information_schema.tables
WHERE table_schema='public'
ORDER BY table_schema,table_name;
('public', 'v2')
Tabulku si můžeme zobrazit i z psql:
$ psql -U tester -d test
Password for user tester:
psql (16.8)
Type "help" for help.
test=> \dt
List of relations
Schema | Name | Type | Owner
--------+-------+-------+--------
public | v2 | table | tester
(1 row)
test=> \d v2
Table "public.v2"
Column | Type | Collation | Nullable | Default
-----------+-----------+-----------+----------+--------------------------------
id | bigint | | not null | nextval('v2_id_seq'::regclass)
embedding | vector(2) | | not null |
Indexes:
"v2_pkey" PRIMARY KEY, btree (id)
6. Zápis vektorů do tabulky v2
Jeden z důvodů, proč vektorové databáze existují, je požadavek na nalezení podobných vektorů na základě zvolené metriky (těch se používá hned několik, jak si ostatně řekneme v dalším textu). Pro ilustraci, jakým způsobem se vlastně podobné vektory hledají, si vytvoříme tabulky, do které budou uloženy dvoudimenzionální vektory, tj. pouze dvojice numerických hodnot. Takové vektory lze snadno vizualizovat v rovině.
Do již vytvořené tabulky nazvané v2 vložíme patnáct záznamů. Při zápisu je každý zapisovaný vektor reprezentován objektem typu numpy.ndarray; konkrétně se jedná o jednorozměrné pole (tedy o skutečný vektor):
import psycopg2
import numpy as np
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
x = [-5, -4, -3, 3, 4, 5, 3, 3, 3, 4, 4, 4, 5, 5, 5]
y = [ 5, 3, 5, -5, -3, -5, 3, 4, 5, 3, 4, 5, 3, 4, 5]
with connection.cursor() as cursor:
for i in range(len(x)):
vector = np.array([x[i], y[i]])
print(type(vector), vector)
cursor.execute("INSERT INTO v2 (embedding) VALUES (%s)", (vector, ))
connection.commit()
Po skončení činnosti skriptu (a zejména po závěrečném commitu) si prohlédneme obsah tabulky v2. Zajímat nás bude zejména obsah sloupce embedding, protože id jsou generována:
test=> select * from v2; id | embedding ----+----------- 31 | [-5,5] 32 | [-4,3] 33 | [-3,5] 34 | [3,-5] 35 | [4,-3] 36 | [5,-5] 37 | [3,3] 38 | [3,4] 39 | [3,5] 40 | [4,3] 41 | [4,4] 42 | [4,5] 43 | [5,3] 44 | [5,4] 45 | [5,5] (15 rows)
7. Výběr pěti vektorů nejpodobnějších zadanému vstupu
V rámci rozšíření pgvector je 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ů se zapisuje těmito třemi znaky:
<->
Tento operátor dokáže porovnat dvojici vektorů na základě jejich vzdálenosti, přičemž se pro výpočet vzdálenosti používá klasická Eukleidovská metrika L2, které zjistí vzdálenosti koncových bodů vektorů (v našem případě ve dvourozměrném prostoru). V navazujících kapitolách si ukážeme některá možná použití tohoto operátoru i operátorů dalších, pomocí nichž se vektory porovnávají na základě odlišných metrik.
Výše zmíněný operátor ↔ můžeme použít v klauzuli ORDER BY, která zajistí uspořádání vektorů na základě jejich vzdálenosti od zadaného vektoru (nebo, chcete-li, na základě podobnosti vektorů z tabulky se zadaným vektorem). Podívejme se na jednoduchý příklad z předchozího článku:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
with connection.cursor() as cursor:
query = """
SELECT embedding
FROM v2
ORDER BY embedding <-> %s::vector
LIMIT 5
"""
cursor.execute(query, ([3, 3], ))
records = cursor.fetchall()
for record in records:
print(record[0])
Výsledkem výše uvedeného dotazu je pětice vektorů, které jsou nejpodobnější vektoru [3, 3]:
[3. 3.] [3. 4.] [4. 3.] [4. 4.] [3. 5.]
Výsledných pět vrácených vektorů si můžeme vizualizovat v 2D rovině:
│ y
│
│
│
│
. . │ . o .
. │ o * o
. │ . o .
│
│
─────────────────────────────────────[0,0]──────────────────────────────────────
│ x
│
│ .
│
│ . .
│
│
│
│
8. Příprava tabulky pro uložení výsledku embeddingu: vektorů s 384 dimenzemi
Nyní již vlastně máme k dispozici všechny potřebné znalosti nutné pro to, abychom využili databázi PostgreSQL s rozšířením pgvector pro vyhledávání textů na základě jejich sémantiky. Připomeňme si, že pro vektorizaci textů budeme používat modely, které produkují vektory s 384 dimenzemi. Připravíme si tedy tabulku nazvanou v384, jejíž jeden sloupec bude obsahovat vektory s právě 384 prvky. K vytvoření této tabulky použijeme skript:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
print(connection)
CREATE_TABLE_STATEMENT = """
CREATE TABLE IF NOT EXISTS v384 (
id bigserial PRIMARY KEY,
embedding vector(384) 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 dokončení skriptu si v konzoli psql vypíšeme všechny tabulky:
test=> \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+--------
public | v2 | table | tester
public | v384 | table | tester
(2 rows)
Pro zajímavost se ještě podíváme na podrobnější informace o tabulce v384:
test=> \d v384
Table "public.v384"
Column | Type | Collation | Nullable | Default
-----------+-------------+-----------+----------+----------------------------------
id | bigint | | not null | nextval('v384_id_seq'::regclass)
embedding | vector(384) | | not null |
Indexes:
"v384_pkey" PRIMARY KEY, btree (id)
9. Výsledky embeddingu kompatibilní s rozšířením pgvector
V článcích o knihovně FAISS (viz dvacátou kapitolu s odkazy) jsme si mj. ukázali, že modely nabízené knihovnou Sentence-transformers dokážou ze vstupního textu vytvořit vektory s pevným počtem prvků (typicky 256, 384, 512 nebo 768 prvků). Tyto vektory obsahující sémantiku vstupního textu jsou typu numpy.ndarray, což je – nikoli náhodou – naprosto stejný typ, jaký akceptuje rozšíření pgvector při ukládání vektorů do databáze.
Ostatně si toto tvrzení můžeme velmi snadno ověřit vektorizací jedné věty:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
print(model)
sentence = "The rain in Spain falls mainly on the plain"
embeddings = model.encode(sentence)
print(f"Embeddings shape: {embeddings.shape}")
vector = embeddings
print(type(vector), vector.shape)
Výsledky vypsané tímto skriptem by měly vypadat takto:
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})
)
Embeddings shape: (384,)
<class 'numpy.ndarray> (384,)
10. Vložení vektoru reprezentujícího jedinou větu do tabulky se sloupcem typu vektor
Vyzkoušejme si, jak vlastně bude vypadat skript, který po svém spuštění provede převod jediné věty do podoby vektoru obsahujícího informace o sémantice této věty. Následně bude obsah vypočteného vektoru uložen do tabulky v384 pro pozdější využití. Všechny kroky, které skript provádí, jsme si popsali a taktéž ukázali v předchozích kapitolách; nyní pouze spojíme dva koncepty do jediného programu:
import psycopg2
import numpy as np
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
print(model)
sentence = "The rain in Spain falls mainly on the plain"
embeddings = model.encode(sentence)
print(f"Embeddings shape: {embeddings.shape}")
vector = embeddings
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
with connection.cursor() as cursor:
cursor.execute("INSERT INTO v384 (embedding) VALUES (%s)", (vector, ))
connection.commit()
Skript po svém spuštění vypíše základní informace o modelu a taktéž tvar (shape) výsledného n-rozměrného pole:
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})
)
Embeddings shape: (384,)
V konzoli psql prozkoumáme obsah tabulky v384. Měla by obsahovat jeden záznam:
test=> select count(*) from v384;
count
-------
1
(1 row)
Tento záznam obsahuje vektor s 384 hodnotami typu float32:
test=> select * from v384;
1 | [0.3478463,0.1651683,0.80891263,0.72986645,1.3382668,0.06676822,-0.21522364,-0.09888732,-0.14145306,-0.52012527,-0.00875917,-0.098080724,-0.2633922,0.402 8185,0.3994093,-0.20971194,-0.12567759,-0.37587926,0.33004197,-0.28925693,0.37450206,0.26958337,-0.29575813,-0.16029944,-0.2659341,0.10839906,-0.18690287,0.065 46495,0.14089231,-0.00020084734,0.10801713,-0.12372175,0.34069282,0.15052913,-0.067606784,-0.63805515,0.46768042,-0.18611597,-0.062298153,0.022772968,-0.725291 8,0.4015648,-0.23507196,0.140735,0.28050125,-0.010708517,0.26216206,0.24861158,0.40079835,0.33300167,0.27957687,-0.014675101,-0.17867266,0.26503685,-0.12559552 ,-0.21344356,-0.23851794,-0.22829832,0.59133744,0.54733306,0.123978466,0.07607324,-0.60898393,0.1268554,0.20776464,0.26186323,0.005309312,0.521085,0.16896497,- 0.24492708,-0.2067925,0.062349096,-0.04011694,-0.14361992,-0.64163506,-0.3978676,-0.3735655,0.02888419,-0.4679453,0.3147229,0.13757025,0.0809049,-0.15652154,0. 25277814,-0.0958617,-0.49047896,0.43949178,0.13820027,0.40036076,-0.5461719,0.11023465,-0.13689366,-0.5629067,0.9595211,0.44364336,0.65416235,0.7172294,-0.3110 0145,0.26710993,0.05519587,-0.019064216,0.052905,0.37381804,0.46188784,-0.2905547,0.25999615,0.07007515,-0.7525602,0.21104497,-0.18804012,0.15200639,-0.4752462 5,0.11918416,-0.40177488,-0.06337302,0.36457422,0.10553891,-0.015762012,-0.6414942,-0.29428843,0.108376496,0.039354797,0.22881092,0.28008914,0.6463641,0.360770 73,-0.47816375,-0.1615384,-0.06412483,-0.96497214,-0.0532971,0.50177723,0.51708335,-0.13628031,-0.1958101,0.20123464,-0.40123847,0.2829893,-0.036858603,-0.6490 6794,-0.34043348,0.075107746,0.05149603,0.12959899,0.738389,-0.59792465,-0.4481514,-0.123061426,-0.41743317,-0.26054513,0.08430738,-0.043441627,0.3164697,0.006 5179546,-0.5136731,0.15219939,0.27004403,-0.11969745,0.041190986,0.028731616,0.18188754,-0.072593756,0.4525503,-0.023671113,0.15453857,0.645053,-0.40997618,0.1 5929341,0.62040216,-0.316236,-0.087189525,-0.47167242,0.02946538,-0.3533273,-0.09394767,0.45658416,-0.06863275,-0.23714565,-0.07764846,-0.08026797,0.62771624,0 .32355314,0.2669482,0.5486583,-0.3239251,-0.12113338,-0.044172402,-0.24990128,0.3756979,-0.07775865,0.0505999,-0.38591337,0.87621766,-0.20538129,-0.26248366,0. 77906,0.14090697,0.08597355,-0.3895502,0.4853258,-0.23761,0.20602818,0.33449268,-0.20086986,0.3318084,0.015541857,0.5247317,0.2244594,-0.0144861825,-0.28824165 ,-0.029353546,-0.5679604,-0.27332273,0.2440015,0.22435383,0.3931917,0.42644545,-0.107593566,-0.70814306,0.57140315,0.3234386,-0.8088993,0.25423485,-0.18235168, 0.09815521,-0.17610633,-0.20705642,0.21467887,-0.08414001,0.36776218,0.2151859,0.41030267,-0.21997021,0.1556591,-0.45738858,-0.375895,0.042969152,0.1592543,0.2 3224005,0.8167258,-0.31846842,0.040264476,0.35690776,0.401083,-0.32581365,-1.1366388,0.1314889,-0.19686675,-0.6279306,-0.0013781949,-0.3910677,0.15773045,-0.44 587424,0.051197443,-0.24650817,0.55354327,-0.7592172,-0.5219158,-0.64309293,0.34288883,-0.012695101,-0.0847227,-0.17499705,0.23765267,-0.53753257,-0.08776954,0 .41179326,-0.18145639,0.044225227,-0.28497654,0.12960373,-0.4727863,-0.23930912,-0.9899117,0.7786404,-0.15647528,-0.332171,0.67442405,0.8531017,-0.39849016,-0. 31563175,-0.23753914,-0.69903916,0.06594103,-0.100104384,0.4190483,-0.78987145,-0.31334054,-0.18987493,-0.35639513,-0.42602998,0.7332483,0.59794927,-0.06998656 ,-0.3528726,-0.50578934,-0.27034423,-0.16521432,-0.4324772,-0.3757722,-0.10690414,0.29796785,-0.5734284,-0.010504636,-0.0290119,-0.20384084,0.22323895,-0.09243 289,0.3657885,-0.35423923,-0.49107325,-0.23372331,0.16865791,0.24685413,-0.2899907,-0.3106801,-0.08246122,-0.11810763,0.082611725,0.0680243,-0.41308358,-0.1096 9818,-0.24324872,0.28386712,0.23900475,-0.61941475,0.5347494,0.25323942,-0.1517316,-0.6160019,-0.45625067,-0.23615155,0.10238915,0.034416072,-0.13215332,-0.111 7928,-0.028137358,0.34055436,-0.022248073,-0.10388828,0.41457003,0.60157514,-0.64496595,0.18863411,0.66177106,-0.24609046,0.058639564,0.026280224,-0.17290074,0 .10932419,0.006086925,0.34558168,-0.21488343,-0.96902615,0.20507286,-0.17184074,0.37967455,-0.3468344,-0.35829875,-0.0062513067,0.33113906,-0.0027895935,-0.000 4822151,-0.043646622,0.07335393,0.14621384,-0.29227698,0.046420466,0.43824196,0.09155101,0.2274449,0.33601317,0.09329631,0.7676583,0.15852436,-0.43436182,0.316 23447,0.4198514,0.504068,0.31218696,-0.63812673,0.30405572,-0.031397928,0.017359529] (1 row)
11. Embedding většího množství vět
Embedding („vektorizace“) jediné věty je vhodný pouze pro demonstrační účely. V praxi bude v tabulce uloženo mnohem větší množství textů – od několika tisíc do několika milionů (ostatně příště se pokusíme vektorizovat a uložit milion vět, abychom si ověřili výkonnost Postgresu s rozšířením pgvector). Pokusme se nyní do tabulky v384 uložit sedm vektorizovaných vět, které jsme v předchozích článcích používali pro otestování embedding modelů. Samotný postup vektorizace většího množství vět již dobře známe, takže opět jen pro úplnost:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
print(model)
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)
print(f"Embeddings shape: {embeddings.shape}")
for vector in embeddings:
print(type(vector), vector.shape)
Po spuštění tohoto skriptu se nejdříve zobrazí informace o modelu (již dobře známe):
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})
)
Posléze se vypíše tvar n-rozměrného pole s vektorizovaným textem. Z výpisu je patrné, že se jedná o matici se sedmi řádky a 384 sloupci:
Embeddings shape: (7, 384)
Následně si necháme vypsat typy a tvary všech řádků vypočtené matice. Jedná se (pochopitelně) o vektory s 384 prvky:
<class 'numpy.ndarray'> (384,) <class 'numpy.ndarray'> (384,) <class 'numpy.ndarray'> (384,) <class 'numpy.ndarray'> (384,) <class 'numpy.ndarray'> (384,) <class 'numpy.ndarray'> (384,) <class 'numpy.ndarray'> (384,)
12. Naplnění tabulky v384 výsledky vektorizace většího množství vět
Nyní konečně můžeme přistoupit k uložení výše uvedených sedmi vektorizovaných vět do tabulky nazvané v384. Nejdříve ovšem z této tabulky vymažeme původní větu. Mělo by se jednat o jediný záznam, pokud jste ovšem nespustili skript ze šesté kapitoly několikrát za sebou:
test=> delete from v384; DELETE 1
Následující skript provede dvojici operací, které již dobře známe: vektorizaci sedmi vět s jejich následným uložením do Postgresu:
import psycopg2
import numpy as np
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
print(model)
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)
print(f"Embeddings shape: {embeddings.shape}")
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
for vector in embeddings:
print(type(vector), vector.shape)
with connection.cursor() as cursor:
cursor.execute("INSERT INTO v384 (embedding) VALUES (%s)", (vector, ))
connection.commit()
Z výpisů bude patrné, jaké operace skript provádí:
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})
)
Embeddings shape: (7, 384)
<class 'numpy.ndarray'> (384,)
<class 'numpy.ndarray'> (384,)
<class 'numpy.ndarray'> (384,)
<class 'numpy.ndarray'> (384,)
<class 'numpy.ndarray'> (384,)
<class 'numpy.ndarray'> (384,)
<class 'numpy.ndarray'> (384,)
Přesvědčíme se, kolik záznamů je nyní v tabulce v384 uloženo:
test=> select count(*) from v384;
count
-------
7
(1 row)
Teoreticky si samozřejmě můžeme vypsat celý obsah této tabulky, ovšem jedná se jen o (na první pohled) nicneříkající číselné údaje, takže výpis v článku nebudu uvádět.
13. Sémantické vyhledávání s využitím rozšíření pgvector
Nyní již konečně můžeme přistoupit k hlavnímu tématu dnešního článku – k vlastnímu sémantickému vyhledávání. Vyhledávaný termín (slovo, sousloví, větu) nejdříve převedeme na vektor s využitím stejného modelu, jaký byl použit pro vektorizaci původních sedmi vět:
vector = model.encode(query_sentence)
Dále v tabulce v384 vyhledáme k nejbližších vektorů s využitím L2 metriky. Proměnné části SQL dotazu jsou podtrženy:
SELECT id, embedding FROM v384 ORDER BY embedding <-> vector LIMIT k
V programovacím jazyku Python, pokud použijeme knihovnu Psycopg2, může být vyhledání a výpis k sémanticky nejbližších vět realizováno tímto způsobem:
vector = model.encode(query_sentence)
with connection.cursor() as cursor:
query = """
SELECT id, embedding
FROM v384
ORDER BY embedding <-> %s::vector
LIMIT %s
"""
cursor.execute(query, (vector, k))
records = cursor.fetchall()
for record in records:
print(record[0])
Celý skript bude vypadat takto:
import psycopg2
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
import faiss
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
print(connection)
register_vector(connection)
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
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)
with connection.cursor() as cursor:
query = """
SELECT id, embedding
FROM v384
ORDER BY embedding <-> %s::vector
LIMIT %s
"""
cursor.execute(query, (vector, k))
records = cursor.fetchall()
for record in records:
print(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)
14. Výsledky sémantického vyhledávání
Podívejme se nyní na výsledky vyhledávání přesně tak, jak je vypíše skript uvedený v předchozí kapitole. Nejdříve se, jak je zvykem, zobrazí informace o navázaném připojení k databázi:
<connection object at 0x7fec54362d40; dsn: 'user=tester password=xxx dbname=test host='' port=5432', closed: 0>
Dále se zobrazí informace o použitém embedding modelu:
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})
)
Poté již následuje výpis obsahující vyhledávaný termín (slovo, větu) a ID (skutečně pouze ID) nejbližších záznamů získaných z databáze:
Query: The quick brown fox jumps over the lazy dog Most 3 similar sentences: 5 9 4 ---------------------------------------- Query: quick brown fox jumps over lazy dog Most 3 similar sentences: 5 9 4 ---------------------------------------- Query: The quick brown fox jumps over the angry dog Most 3 similar sentences: 5 9 6 ---------------------------------------- Query: The quick brown cat jumps over the lazy dog Most 3 similar sentences: 5 6 4 ---------------------------------------- Query: What is your age? Most 3 similar sentences: 8 6 4 ---------------------------------------- Query: Shakespeare Most 3 similar sentences: 6 4 3 ---------------------------------------- Query: animal Most 3 similar sentences: 9 5 6 ---------------------------------------- Query: geometry Most 3 similar sentences: 4 6 9 ---------------------------------------- Query: weather Most 3 similar sentences: 3 6 8
15. Databázová tabulka obsahující jak původní věty (texty), tak i jejich vektorizovanou podobu
Jak je z výsledků předchozího skriptu patrné, získali jsme pouze ID záznamů, ovšem nikoli původní věty. Kromě toho můžeme získat prvky vektoru, ovšem původní větu (text) nelze z vektoru obnovit. Samozřejmě je možné do tabulky zapsat například index původních vět a mapování mezi indexem a větou provést v Pythonu, ve kterém můžeme mít k dispozici seznam nebo n-tici s původními větami:
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"
]
Ovšem proč nevyužít toho, že máme k dispozici plnohodnotný PostgreSQL? Věty přece můžeme zapsat v jejich původní textové formě do stejné tabulky, konkrétně do sloupce typu text, varchar atd. Konstrukce takové tabulky je snadná, pouze tabulku rozšíříme o sloupec sentence. Následující skript vytvoří novou prázdnou tabulku nazvanou v384b:
import psycopg2
from pgvector.psycopg2 import register_vector
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
print(connection)
CREATE_TABLE_STATEMENT = """
CREATE TABLE IF NOT EXISTS v384b (
id bigserial PRIMARY KEY,
embedding vector(384) NOT NULL,
sentence TEXT 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 skriptu by se měl vypsat seznam všech tabulek. Tento seznam získáme i příkazem \dt v konzoli psql:
test=> \dt
List of relations
Schema | Name | Type | Owner
--------+-------+-------+--------
public | v2 | table | tester
public | v384 | table | tester
public | v384b | table | tester
(3 rows)
Schéma nové tabulky v384b bude vypadat takto:
test=> \d v384b
Table "public.v384b"
Column | Type | Collation | Nullable | Default
-----------+-------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('v384b_id_seq'::regclass)
embedding | vector(384) | | not null |
sentence | text | | not null |
Indexes:
"v384b_pkey" PRIMARY KEY, btree (id)
16. Skript pro konstrukci embedding modelu původních vět s jejich uložením do databáze
Do tabulky v384b se ukládá jak původní věta (formou textu), tak i její vektorizovaná podoba:
INSERT INTO v384b (embedding, sentence) VALUES (vektor, původní věta)
Skript, který byl uveden ve dvanácté kapitole, je možné nepatrně upravit tak, aby do tabulky ukládal i původní věty:
import psycopg2
import numpy as np
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
print(model)
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)
print(f"Embeddings shape: {embeddings.shape}")
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
register_vector(connection)
for sentence, vector in zip(sentences, embeddings):
print(type(vector), vector.shape)
with connection.cursor() as cursor:
cursor.execute("INSERT INTO v384b (embedding, sentence) VALUES (%s, %s)", (vector, sentence))
connection.commit()
O tom, že do tabulky byly mj. uloženy i původní věty, se přesvědčíme velmi snadno, a to opět z konzole psql:
test=> select sentence from v384b;
sentence
-----------------------------------------------------
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
(7 rows)
17. Sémantické vyhledávání podruhé: nyní vracející původní věty
Původně jsme z tabulky v384 načítali ID sémanticky nejbližších vět, takže prováděný dotaz vypadal následovně:
SELECT id, embedding FROM v384 ORDER BY embedding <-> %s::vector LIMIT nastavená hodnota k
Nyní ovšem máme v tabulce v384b uloženy i původní věty, takže dotaz můžeme položit takovým způsobem, že pro vzorek získáme přímo k sémanticky nejpodobnějších vět:
SELECT sentence FROM v384b ORDER BY embedding <-> query_vektor LIMIT nastavená hodnota k
A právě tento druhý dotaz je použit v dnešním posledním demonstračním příkladu, jehož zdrojový kód vypadá následovně:
import psycopg2
from pgvector.psycopg2 import register_vector
from sentence_transformers import SentenceTransformer
import faiss
connection = psycopg2.connect(
host="", port=5432, user="tester", password="123qwe", dbname="test"
)
print(connection)
register_vector(connection)
model = SentenceTransformer("paraphrase-MiniLM-L6-v2")
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)
with connection.cursor() as cursor:
query = """
SELECT sentence
FROM v384b
ORDER BY embedding <-> %s::vector
LIMIT %s
"""
cursor.execute(query, (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)
18. Výsledky vylepšeného sémantického vyhledávání
Výše uvedený skript by měl po svém spuštění (opět) vypsat informaci o navázaném připojení k databázi i o použitém modelu:
<connection object at 0x7efcd7ac6d40; dsn: 'user=tester password=xxx dbname=test host='' port=5432', closed: 0>
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 již vypíše zadaný vzorek a vždy trojice sémanticky nejbližších vět k tomuto vzorku. Povšimněte si, že ve všech případech je první sémanticky nejbližší věta skutečně vybrána zcela korektně (snad až na otázku „animal“, pro kterou existuje více odpovědí):
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: The tesselated polygon is a special type of polygon
----------------------------------------
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: The tesselated polygon is a special type of polygon
----------------------------------------
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: To be or not to be, that is the question
----------------------------------------
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: To be or not to be, that is the question
#2: The tesselated polygon is a special type of polygon
----------------------------------------
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: The tesselated polygon is a special type of polygon
----------------------------------------
Query: Shakespeare
Most 3 similar sentences:
#0: To be or not to be, that is the question
#1: The tesselated polygon is a special type of polygon
#2: The rain in Spain falls mainly on the plain
----------------------------------------
Query: animal
Most 3 similar sentences:
#0: The goat ran down the hill
#1: The quick brown fox jumps over the lazy dog
#2: To be or not to be, that is the question
----------------------------------------
Query: geometry
Most 3 similar sentences:
#0: The tesselated polygon is a special type of polygon
#1: To be or not to be, that is the question
#2: The goat ran down the hill
----------------------------------------
Query: weather
Most 3 similar sentences:
#0: The rain in Spain falls mainly on the plain
#1: To be or not to be, that is the question
#2: How old are you?
----------------------------------------
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/ - 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/ - Vector database
https://en.wikipedia.org/wiki/Vector_database - Nearest neighbor search
https://en.wikipedia.org/wiki/Nearest_neighbor_search#Approximation_methods - 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 - 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