14. Векторные базы данных
HNSW, метрики расстояния и интеграция с Go
Векторные базы данных: Поиск смысла в многомерном пространстве
Традиционные базы данных, такие как 6. PostgreSQL или 1. Архитектура MySQL, оптимизированы для работы со структурированными данными: числами, строками и датами. Они отлично справляются с запросом WHERE price < 100, но пасуют перед задачей «найди мне товары, похожие на этот по стилю».
С расцветом Large Language Models (LLM) и нейросетей возникла необходимость хранить и быстро искать эмбеддинги (embeddings) — высокоразмерные векторы чисел, которые представляют собой сжатый «смысл» объекта (текста, картинки или аудио).
1. Что такое векторный поиск?
Векторная база данных хранит не сами объекты, а их представления в виде векторов в многомерном пространстве (обычно от 384 до 1536+ измерений).
flowchart LR
Text["Текст / Картинка / Аудио"] --> Model["Нейросеть (Embedding Model)"]
Model --> Vector["Вектор чисел: [0.12, -0.45, ..., 0.89] (1536 dim)"]
Vector --> VDB["Векторная СУБД (Индексация HNSW)"]
classDef default fill:#1e293b,stroke:#334155,color:#f8fafc;
classDef emb fill:#312e81,stroke:#6366f1,color:#ffffff;
class Vector,VDB emb;
Основная задача векторного поиска — найти K ближайших соседей (K-Nearest Neighbors, kNN) для целевого вектора. Близость определяется не совпадением символов, а математическим расстоянием между точками.
Основные метрики расстояния:
- L2 (Euclidean Distance): Обычное геометрическое расстояние. Чем меньше, тем лучше. Чувствительно к величине векторов.
- Cosine Similarity: Измеряет косинус угла между векторами. Фокусируется на направлении, а не на длине. Идеально для текстов.
- Inner Product (Dot Product): Скалярное произведение. Часто используется, если векторы нормализованы.
2. Mechanical Sympathy: Почему нельзя просто использовать циклы?
Если у вас 10 миллионов векторов по 1536 измерений, то простой перебор (Flat Search) потребует $10^7 \times 1536$ операций умножения и сложения для каждого поискового запроса.
3. Алгоритмы индексации: Как работает магия
Чтобы поиск был быстрым, используются алгоритмы Approximate Nearest Neighbors (ANN). Мы жертвуем 1% точности ради ускорения в 1000 раз.
IVFFlat (Inverted File Index)
Пространство делится на кластеры (ячейки Вороного). При поиске мы сначала находим ближайшие центроиды кластеров, а затем ищем только внутри этих кластеров.
HNSW (Hierarchical Navigable Small World)
На текущий момент это стандарт индустрии. Он строит граф, где узлы — это векторы.
- Иерархия: На верхних слоях графа узлов мало, и «прыжки» между ними длинные (как магистрали между городами).
- Спуск: Мы находим ближайшую точку на верхнем слое, спускаемся на слой ниже и уточняем поиск (как улицы внутри города).
- Сложность: Поиск в HNSW имеет логарифмическую сложность $O(\log N)$.
flowchart TD
Start["Запрос: Вектор V"] --> Layer2["Слой 2 (Магистрали между кластерами)"]
Layer2 -->|"Найти ближайший узел"| NodeA["Узел A"]
NodeA --> Layer1["Слой 1 (Улицы внутри района)"]
Layer1 -->|"Уточнение позиции"| NodeB["Узел B"]
NodeB --> Layer0["Слой 0 (Дома / Все векторы)"]
Layer0 -->|"Точный расчет kNN"| Result["Ближайшие соседи"]
classDef default fill:#1e293b,stroke:#334155,color:#f8fafc;
classDef step fill:#312e81,stroke:#6366f1,color:#ffffff;
class Layer2,Layer1,Layer0 step;
4. Архитектура векторных СУБД
Существует два подхода к реализации:
- Vector-native (Qdrant, Milvus, Weaviate, Pinecone): Написаны с нуля (часто на Rust или Go) для максимальной производительности векторных операций.
- Vector-plugins (pgvector для Postgres, Elasticsearch): Добавляют поддержку векторов в знакомые системы. Удобно, но часто медленнее и хуже масштабируется при миллиардах векторов.
5. Практика в Go: Работа с pgvector
Go отлично подходит для работы с векторными БД благодаря своей производительности и типизации. Рассмотрим пример использования pgvector через sqlx.
package main
import (
"context"
"log"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"github.com/pgvector/pgvector-go"
)
type Document struct {
ID int `db:"id"`
Content string `db:"content"`
Embedding pgvector.Vector `db:"embedding"` // Вектор в Go
}
func GetSimilarDocs(db *sqlx.DB, queryEmbedding []float32) ([]Document, error) {
// Используем оператор <-> для L2 расстояния или <=> для Cosine
query := `SELECT id, content FROM items
ORDER BY embedding <=> $1
LIMIT 5`
var docs []Document
// Конвертируем []float32 в формат pgvector
err := db.Select(&docs, query, pgvector.NewVector(queryEmbedding))
if err != nil {
return nil, err
}
return docs, nil
}
Итог
- Векторные БД решают задачу семантического поиска через поиск ближайших соседей (kNN).
- HNSW — самый эффективный алгоритм для быстрого поиска по графу.
- Mechanical Sympathy: Векторный поиск критически зависит от SIMD-инструкций и скорости работы с памятью.
- Сжатие (Квантование): Необходимый инструмент при работе с Big Data, чтобы не разориться на оперативной памяти.
Мы разобрали, как хранить и искать смыслы. Теперь пора применить это на практике и понять, как интегрировать векторный поиск в реальные AI-продукты: 15. Поиск по embedding и AI use cases