Roman Kryvolapov Engineering Blog

Векторни бази данни

Машинното обучение и особено невронните мрежи позволяват да се преобразуват „неструктурирани данни“ във вектори с фиксирана дължина (най-често float32), които запазват семантиката на първоначалния обект. Например два сходни текста ще имат близки вектори (малко евклидово разстояние или висока косинусова близост).

Векторни бази данни (vector databases):
това са специализирани хранилища, предназначени за ефективно търсене и съпоставяне на вектори (обикновено – ембединги), които представят обекти като текст, изображения, аудио или видео в числов вид.

Какво обикновено се съхранява във векторната база

КомпонентКакво еПример
IDУникален идентификатор на записа„doc-001“ или 123
ВекторЧислов списък, който представя обекта[0.12, -0.56, 0.44, …, -0.03] (обикновено float32)
ДокументПървоначалният текст или файл (по желание)„Как си?“
МетаданниДопълнителни полета за филтриране, тагове, контекст{"language": "ru", "user": "petya", "tags": ["faq"]}

Популярни векторни бази

ИмеРазработчикИндексиранеМетрикиПлюсовеМинуси
MilvusZillizIVF_FLAT, IVF_SQ8, IVF_PQ, HNSW, ANNOY, FlatL2, IP, Cosine, Jaccard, HammingМащабируемост (милиарди точки)
Много индекси
gRPC/REST
Изисква Docker или Standalone
По-сложен за внедряване
QdrantQdrant (на Rust)HNSW (модифициран), FlatCosine, Dot, Euclidean (L2)Бърз, с двигател на Rust
Лесна инсталация
Филтриране по метаданни
Засега по-малко индекси
Няма вградена клъстеризация
WeaviateSemi.technologiesHNSW + Text (Hybrid Search)Cosine, Dot, EuclideanХибридно търсене (BM25 + векторно)
GraphQL API
Автоматично инжектиране на данни
Изисква повече памет
GraphQL не винаги е удобен
ChromaChroma orgFlat (точен), HNSW (в roadmap/частично)CosineМного проста инсталация
Идеален за RAG и локално стартиране
Само Flat (засега)
Няма филтриране по метаданни (частично има)
FAISSFacebook/MetaFlat, IVF, PQ, OPQ, HNSW, LSHL2, Dot, Cosine (чрез нормализация)Много гъвкав
Поддръжка на GPU
Най-добра производителност на CPU/GPU
Това е библиотека, не сървър
Нужни са ръчна настройка и кодиране
OpenSearchAmazonHNSW, Faiss backend, ANN native pluginL2, Dot, CosineХибридно търсене (BM25 + ANN)
Интеграция с текст
Съвместим с Elasticsearch
Сложна настройка на ANN
Високи изисквания към паметта

Преобразуване на данните във вектори (Embedding)

Например текстът „Как си?“ може да бъде преобразуван във вектор от 384 стойности

[0.12, -0.56, 0.44, ..., -0.03]

За това се използват специализирани embedding модели, като например:

ИмеКакво кодира и как работиПредимства
all-MiniLM-L6-v2Лек и бърз модел на базата на Transformer
Кодира фрази, въпроси, абзаци
Компактен (~80MB)
Поддръжка в sentence-transformers
Работи „от кутията“
text-embedding-ada-002
(OpenAI)
Комерсиален модел от OpenAI
Изисква API ключ
Кодира всякакви текстове
Високо качество на ембедингите
Поддръжка на различни езици
Отлично подходящ за RAG
bge-small-enСъвременен модел от BAAI
Поддържа шаблони: „query:...“, „passage:...“
Висока точност
Поддръжка на многоезичност (в M3E)
Отличен за Qdrant, LangChain
e5-base / e5-largeУниверсални модели от FlagAI
Подходящи за търсене, клъстеризация, QA
Най-добри показатели в MTEB
Поддръжка на многоезични задачи
Работят без fine-tune
Instructor-XLКодира текста с оглед на задачата
Използва инструкции от вида: „Represent the ... for ...“
Повишена точност
Подходящ за task-aware embedding
Отличен за RAG/FAQ
mpnet-base-v2От Microsoft
Контекстно чувствителен модел
Добър за сходни фрази
Добър баланс между точност и скорост
Подходящ за paraphrase и general search
LaBSEОт Google
Многоезичен модел
Най-добре работи с кратки изречения
Поддръжка на 100+ езика
Отличен избор за междуезиково търсене

Типични размери на вектори (дължини на ембедингите) при различните модели:

МоделДължина на вектора
all-MiniLM-L6-v2384
text-embedding-ada-002 (OpenAI)1536
bge-small-en384
bge-base-en768
bge-large-en1024
e5-small-v2384
e5-base-v2768
e5-large-v21024
mpnet-base-v2768
LaBSE768
Instructor-XL768 или 1024

Индексиране на векторите

Векторите, съответстващи на обектите, се индексират, за да може после бързо да се търсят по входящ вектор най-близките до него от запазените в базата.
Начини за индексиране на вектори:

Видове индекси:

ИмеКак работиПредимстваНедостатъци
FlatПрегледва ръчно всички векториНай-точно търсене
Проста реализация
Идеален за дебъгване и малки набори
Много бавен при голям обем
Изисква много изчисления
Не се мащабира
HNSWТърсене по мрежа от сходни вектори (като се започва от „центровете“)Много бърз
Висока точност
Подходящ за големи бази
Изисква много памет
Дълго построяване на индекса
Труден за параметризиране
IVFРазделя векторите на групи (клъстери) и търси само в тяхПо-бърз от Flat
Гъвкава настройка (nprobe)
Добре се мащабира
Може да пропусне сходни вектори
Изисква предварително обучение
PQЗаменя части от вектора с къси кодовеСилно икономисва памет
Бързо търсене по таблица
Идеален за големи набори
Загуба на точност
Изисква обучение (codebook)
Не е за задачи с висока точност
OPQПодобрена версия на PQ – първо „коригира“ вектораПо-висока точност от PQ
Добре работи във FAISS, Milvus
Комбинира се с IVF
По-сложен за обучение
Все още е приблизителен метод
AnnoyСтрои много случайни дървета и търси по тяхЛесен за използване
Малко зависи от ресурсите
Подходящ за CPU и мобилни устройства
По-малко точен от HNSW
Дълго построяване на индекса
Не може да се обновява след построяването

Когато потребителят въведе заявка, тя се преобразува във вектор и базата търси най-близките съседи (KNN) по избраната метрика.

Видове метрики:

Име на метрикатаКак работиПредимстваНедостатъци
Cosine SimilarityСравняваме ъгъла между векторите. Колкото по-близо е ъгълът до 0°, толкова по-голямо е сходството.Отчита само посоката
Добре работи с текстове и ембединги
Не зависи от дължината на вектора
Не отчита мащаба (дължината)
Не е подходяща, ако дължината на вектора е важна
Euclidean (L2)Измерваме „линейното“ разстояние между точките. По-близо – значи по-сходно.Проста и интуитивно разбираема
Подходяща за координати, изображения
Не нормира векторите (мащабът влияе)
Не винаги е добра за текстове
Inner Product (Dot Product)Умножаваме съответните координати и събираме произведенията. Колкото по-голяма е сумата, толкова по-високо е сходството.Изчислява се много бързо
Добре работи с ненормализирани вектори
Чувствителна е към дължината на вектора
Стойностите може да са трудни за интерпретиране
Manhattan (L1)Сума от модулите на разликите по всяка координата – като по клетките на мрежа.Устойчива на извънредни стойности
По-добре работи с разредени вектори
Използва се по-рядко
По-зле работи с плътни вектори
Hamming DistanceБроим в колко бита се различават два бинарни вектора.Много бърза за бинарни данни
Подходяща за fingerprint и хешове
Работи само с бинарни вектори
Не е приложима за float
Jaccard SimilarityОтношение на сечението към обединението на множества или бинарни вектори.Идеална за тагове и бинарни признаци
Разбираема метрика
Само за бинарни вектори
Не работи с float вектори
TanimotoОбобщена метрика на Jaccard, приложима и за float вектори.Подходяща за химични структури, fingerprint
Работи и с бинарни, и с реални
Рядко се използва
Ограничена поддръжка в библиотеките

Пример за работа с векторна база

В примера с CRUD операции ще използваме Python, базата Milvus, embedding модел e5‑base

Python
from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection
from pymilvus.model.dense import SentenceTransformerEmbeddingFunction
# 1. Свързване към Milvus (по подразбиране localhost:19530)
connections.connect("default", host="localhost", port="19530")
# 2. Инициализиране на функцията за ембединг с модела e5-base-v2
# Този модел изисква:
# - Префикс "passage: " за документите
# - Префикс "query: " за заявките за търсене
ef = SentenceTransformerEmbeddingFunction("intfloat/e5-base-v2")
# 3. Дефиниране на схемата на колекцията:
# - "id" — целочислен идентификатор (първичен ключ)
# - "text" — първоначалният текст на документа (низ)
# - "emb" — ембединг вектор с размерност 768
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=False),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=512),
FieldSchema(name="emb", dtype=DataType.FLOAT_VECTOR, dim=768)
]
schema = CollectionSchema(fields, description="Колекция с ембединги от e5-base-v2")
# 4. Създаване на колекцията в Milvus със зададената схема
collection = Collection("e5_collection", schema)
# 5. Подготовка и вмъкване на документи
# Важно: преди да се подадат на модела, трябва да се добави префиксът "passage: "
raw_docs = ["Hello world", "Milvus vector database", "Semantic search with e5 model"]
docs = [f"passage: {d}" for d in raw_docs] # добавяме префикса
ids = [1, 2, 3]
# Изчисляваме ембедингите на документите с помощта на e5‑base
embs = ef.encode_documents(docs)
# Вмъкваме в колекцията:
# - идентификаторите
# - първоначалните (чисти) текстове без префикси
# - ембедингите
collection.insert([ids, raw_docs, embs])
# 6. Създаване на индекс по полето "emb" за ускоряване на търсенето
collection.create_index(
field_name="emb",
index_params={
# тип на индекса
"index_type": "IVF_FLAT",
# параметър за разделяне на клъстери
"params": {"nlist": 128},
# метрика за разстоянието (евклидово разстояние)
"metric_type": "L2"
}
)
# 7. Зареждане на колекцията в оперативната памет
# Без това търсенето няма да работи
collection.load()
# 8. Заявка за търсене
# По същия начин – използваме префикса "query: " пред текста на заявката
query_docs = ["query: vector database"]
q_emb = ef.encode_queries(query_docs)
# Изпълняваме семантично търсене по ембедингите
results = collection.search(
# ембединг на заявката за търсене
data=q_emb,
# поле, по което се извършва търсенето
anns_field="emb",
# параметри на търсенето
param={"metric_type": "L2", "params": {"nprobe": 10}},
# брой най-близки съседи
limit=2,
# допълнителни полета, които трябва да се върнат
output_fields=["text"]
)
# 9. Извеждаме резултатите от търсенето
for i, hits in enumerate(results):
print(f"Резултати за заявката: '{query_docs[i]}'")
if not hits:
print("Нищо не е намерено")
continue
for rank, hit in enumerate(hits, start=1):
print(f" {rank}:")
print(f" ID: {hit.id}")
print(f" Текст: {hit.entity.get('text')}")
print(f" Разстояние: {hit.distance:.4f}")
# Резултати за заявката: 'query: vector database'
# 1:
# ID: 2
# Текст: Milvus vector database
# Разстояние: 2.8374
# 2:
# ID: 3
# Текст: Semantic search with e5 model
# Разстояние: 5.4931
# 10. Изтриване на документ по ID
# В случая се изтрива документът с id = 1
collection.delete(expr="id in [1]")
# 11. Изтриване на цялата колекция (ако вече не е нужна)
collection.drop()

Copyright: Roman Kryvolapov