Rozšíření PostgreSQL jménem pgvector, embedding a sémantické vyhledávání (1. část)

12. 8. 2025
Doba čtení: 34 minut

Sdílet

Autor: Depositphotos
Ukážeme si kombinaci možností databáze PostgreSQL, jejího rozšíření pgvector a taktéž knihovny Sentence transformers, která dokáže převádět texty do podoby vektorů. Výsledkem bude systém pro sémantické vyhledávání.

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

20. Odkazy na Internetu

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

  1. Instalace rozšíření pgvector
  2. Povolení rozšíření pgvector v běžícím Postgresu
  3. Vytvoření tabulky se sloupcem typu „vektor“ (s pevným počtem prvků)
  4. Vektorizace vět s jejich uložením do této tabulky
  5. 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
  6. Vytvoření tabulky se sloupcem typu „vektor“ (s pevným počtem prvků) a sloupcem typu „text“
  7. Vektorizace vět s jejich uložením do této nové tabulky
  8. 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
Poznámka: pokud do konfiguračního souboru nepřidáte poslední řádek, nebude možné rozšíření pgvector aktivovat pro další uživatele databáze (což ovšem nutně nemusí vadit).

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)
Poznámka: existují i další způsoby zjištění, jestli se pgvector používá, ale \dx je nejjednodušší.

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)
Poznámka: ve skutečnosti pravděpodobně uvidíte, že id začínají od nuly. V mém konkrétním případě jsem skript několikrát zkoušel (a mezitím původní záznamy vymazal), takže se počitadlo id stačilo posunout.

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,)
Poznámka: navíc nás ještě budou později zajímat typy prvků vektoru (float32, float16, bfloat16) a taktéž to, jestli jsou vektory normalizovány či nikoli. K tomu se však ještě vrátíme.

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
Poznámka: z odpovědi zobrazené v konzoli psql je patrné, že se skutečně vymazal jen jeden záznam.

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
Poznámka: prozatím musíme používat L2 metriku, protože vektory nejsou normalizovány a tudíž by výsledky skalárního součinu (nebo výpočtu kosinu úhlu mezi vektory) nebyly dobře použitelné.

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:

# 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 pyproject.toml soubor s definicí Pythonovského projektu 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. 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/
  3. 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/
  4. Knihovna FAISS a embedding: základ jazykových modelů
    https://www.root.cz/clanky/knihovna-faiss-a-embedding-zaklad-jazykovych-modelu/
  5. Vector database
    https://en.wikipedia.org/wi­ki/Vector_database
  6. Nearest neighbor search
    https://en.wikipedia.org/wi­ki/Nearest_neighbor_search#Ap­proximation_methods
  7. RAG – Retrieval-augmented generation
    https://en.wikipedia.org/wi­ki/Retrieval-augmented_generation
  8. pgvector na GitHubu
    https://github.com/pgvector/pgvector
  9. Why we replaced Pinecone with PGVector
    https://www.confident-ai.com/blog/why-we-replaced-pinecone-with-pgvector
  10. PostgreSQL as VectorDB – Beginner Tutorial
    https://www.youtube.com/wat­ch?v=Ff3tJ4pJEa4
  11. What is a Vector Database? (neobsahuje odpověď na otázku v titulku :-)
    https://www.youtube.com/wat­ch?v=t9IDoenf-lo
  12. PGVector: Turn PostgreSQL Into A Vector Database
    https://www.youtube.com/wat­ch?v=j1QcPSLj7u0
  13. Milvus
    https://milvus.io/
  14. Vector Databases simply explained! (Embeddings & Indexes)
    https://www.youtube.com/wat­ch?v=dN0lsF2cvm4
  15. Vector databases are so hot right now. WTF are they?
    https://www.youtube.com/wat­ch?v=klTvEwg3oJ4
  16. 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
  17. Best 17 Vector Databases for 2025
    https://lakefs.io/blog/12-vector-databases-2023/
  18. Top 15 Vector Databases that You Must Try in 2025
    https://www.geeksforgeeks.org/top-vector-databases/
  19. Picking a vector database: a comparison and guide for 2023
    https://benchmark.vectorvi­ew.ai/vectordbs.html
  20. Top 9 Vector Databases as of Feburary 2025
    https://www.shakudo.io/blog/top-9-vector-databases
  21. What is a vector database?
    https://www.ibm.com/think/to­pics/vector-database
  22. SQL injection
    https://en.wikipedia.org/wi­ki/SQL_injection
  23. Cosine similarity
    https://en.wikipedia.org/wi­ki/Cosine_similarity
  24. Hammingova vzdálenost
    https://cs.wikipedia.org/wi­ki/Hammingova_vzd%C3%A1le­nost
  25. Jaccard index
    https://en.wikipedia.org/wi­ki/Jaccard_index
  26. Manhattanská metrika
    https://cs.wikipedia.org/wi­ki/Manhattansk%C3%A1_metri­ka
  27. FAISS (Facebook AI Similarity Search)
    https://en.wikipedia.org/wiki/FAISS
  28. FAISS documentation
    https://faiss.ai/
  29. Introduction to Facebook AI Similarity Search (Faiss)
    https://www.pinecone.io/le­arn/series/faiss/faiss-tutorial/
  30. 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
  31. 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
  32. F16C
    https://en.wikipedia.org/wiki/F16C
  33. FP16 (AVX-512)
    https://en.wikipedia.org/wiki/AVX-512#FP16
  34. 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/
  35. Is FAISS a Vector Database? Complete Guide
    https://mljourney.com/is-faiss-a-vector-database-complete-guide/
  36. FAISS and sentence-transformers in 5 Minutes
    https://www.stephendiehl.com/pos­ts/faiss/
  37. Sentence Transformer: Quickstart
    https://sbert.net/docs/qu­ickstart.html#sentence-transformer
  38. Sentence Transformers: Embeddings, Retrieval, and Reranking
    https://pypi.org/project/sentence-transformers/
  39. uv
    https://docs.astral.sh/uv/
  40. A Gentle Introduction to Retrieval Augmented Generation (RAG)
    https://wandb.ai/cosmo3769/RAG/re­ports/A-Gentle-Introduction-to-Retrieval-Augmented-Generation-RAG---Vmlldzo1MjM4Mjk1
  41. The Beginner’s Guide to Text Embeddings
    https://www.deepset.ai/blog/the-beginners-guide-to-text-embeddings
  42. What are Word Embeddings?
    https://www.youtube.com/wat­ch?v=wgfSDrqYMJ4
  43. How to choose an embedding model
    https://www.youtube.com/wat­ch?v=djp4205tHGU
  44. What is a Vector Database? Powering Semantic Search & AI Applications
    https://www.youtube.com/wat­ch?v=gl1r1XV0SLw
  45. 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
  46. BERT (language model)
    https://en.wikipedia.org/wi­ki/BERT_(language_model)
  47. Levenštejnova vzdálenost
    https://cs.wikipedia.org/wi­ki/Leven%C5%A1tejnova_vzd%C3%A1le­nost
  48. pgvector Tutorial: Integrate Vector Search into PostgreSQL
    https://www.datacamp.com/tu­torial/pgvector-tutorial
  49. pgvectorbench
    https://github.com/pgvector­Bench/pgvectorBench
  50. pgvector 0.4.0 performance
    https://supabase.com/blog/pgvector-performance
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

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