Усі уроки · 44
Шлях 3 · Дорожня карта AI-інженера·Урок 13 з 33

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

Чому семантичному пошуку потрібна векторна база даних, як індекси ANN на кшталт HNSW швидко знаходять найближчих сусідів, яку базу вибрати — і робочий приклад пошуку за схожістю.

РівеньСереднійЧас~15 хв

Спершу: що таке база даних?

База даних — це програма, уся робота якої — пам’ятати дані й відповідати на запитання про них. Ви один раз передаєте їй мільйон записів, а потім питаєте: «які записи підходять під це?» — і вона відповідає за мілісекунди, не перечитуючи все з нуля.

Два слова ви почуєте в розмові про будь-яку базу даних:

  • Запит — запитання, яке ви їй ставите. «Усі замовлення за березень», «користувач із такою поштою», «п’ять об’єктів, найсхожіших на цей».
  • Індекс — допоміжна структура, яку база заздалегідь будує, щоб відповідати, не перевіряючи кожен запис. Як предметний покажчик у кінці книжки: ви одразу відкриваєте потрібну сторінку, а не читаєте всі 400.

Звичайна база даних чудово справляється з точними запитаннями: це значення, ця дата, це ім’я користувача. Для запиту «знайди п’ять документів, близьких за змістом до цього абзацу» вона марна: у ньому немає нічого точного.

Векторна база даних створена саме для такого запитання: її записи — вектори, а запит звучить так: «що розташоване найближче до цього?»

Проблема: найближчі сусіди за великих обсягів

Ембеддинги розв’язують питання «що означає цей текст», а координати визначає модель, яку ви вибрали для їх побудови (Моделі ембеддингів). Наступна проблема — швидкість. Припустімо, у вас мільйон документів з ембеддингами й один вектор запиту. Які 5 документів найближчі до нього?

Простими словами: «найближчі» означає «найбільш схожі за змістом». У цьому весь фокус із уроку про ембеддинги: близькі вектори — це схожі тексти. Лишається тільки швидко їх знайти.

Очевидне рішення — порахувати відстань до кожного вектора й відсортувати — називається повним перебором (brute-force search), і для кількох тисяч векторів він годиться. На мільйоні він уже гальмує, а на сотні мільйонів безнадійний.

Саме це розв’язує векторна база даних: вона зберігає вектори й за мілісекунди відповідає на запит «дай K найближчих сусідів цього вектора» навіть за величезних обсягів. Робить вона це за допомогою індексу — структури даних, яка віддає трохи точності в обмін на велику швидкість.

Як працюють індекси (версія в один абзац)

Основний підхід — наближений пошук найближчих сусідів (Approximate Nearest Neighbor, ANN). На практиці вам трапляться два сімейства індексів:

  • HNSW (Hierarchical Navigable Small World) будує багатошаровий граф, де кожен вектор з’єднано з кількома сусідніми. Запит починається з верхнього шару — там стрибки через увесь простір — і шар за шаром наближається до цілі: так ви спершу їдете шосе, потім міськими вулицями, а наприкінці шукаєте точну адресу. Швидко й точно, але потребує багато пам’яті.
  • IVF (Inverted File) спершу розкладає всі вектори по кошиках: стандартний алгоритм k-means (k-середніх) ділить їх на задану кількість «районів». Потім запит переглядає лише кілька найближчих до нього кошиків замість усієї колекції (так у базах даних називають «один іменований набір записів»). Пам’яті потрібно менше, точність трохи нижча, налаштовувати легко.

Реалізовувати це не потрібно

Індекси ANN — результат десятиліть досліджень, і їх уже реалізовано за вас. Ваше завдання — знати, що вони існують, розуміти компроміс «точність в обмін на швидкість» і вибирати розумні параметри. Ось і все.

Пайплайн

Ось повний життєвий цикл системи векторного пошуку: кожен крок — окремий компонент, який ви побудуєте:

Step through the pipeline
→→→→→
1. Завантаження документів

Завантажте свої тексти — звернення до підтримки, документацію продукту, статті.

out› фрагменти
1/6

Яку вибрати: просто візьміть одну

Усі варіанти добрі — і тому вибрати складніше, ніж мало б бути:

  • Chroma — відкритий код, орієнтована на Python, налаштувань не потребує. Варіант за замовчуванням для навчання й прототипів.
  • FAISS — бібліотека, а не база даних. Чиста швидкість, перевірена на практиці в Meta, сервера немає, і готового способу зберігати дані між запусками теж немає. Чудово підходить, коли ембеддинги живуть усередині вашого застосунку.
  • LanceDB — вбудована база: дані зберігаються в локальних файлах, окремий сервер не потрібен. Проста й сучасна.
  • Supabase pgvector — вектори просто в Postgres. Якщо ваші дані вже лежать у Postgres, а масштаб помірний, це шлях найменшого опору.
  • Qdrant — відкритий код, висока швидкість, розвинена фільтрація. Популярний варіант для розміщення на власних серверах.
  • Weaviate — повнофункціональна база з відкритим кодом і вбудованим гібридним пошуком: він поєднує старий добрий пошук за ключовими словами та векторний, тож одним запитом ви знаходите і точні артикули товарів, і наближені за змістом збіги.
  • Pinecone — повністю керований сервіс: обслуговувати нічого не треба. Підходить, якщо простіше заплатити, ніж самому тримати інфраструктуру.

Коротка відповідь

Почніть із Chroma (або з pgvector, якщо ви вже користуєтеся Postgres). Вона спокійно справляється з мільйонами векторів — більше, ніж побачить більшість побічних проєктів. Переїзд потім — це скрипт перерахунку ембеддингів і пів дня роботи. Питання «яку векторну базу вибрати» варте хвилин роздумів, а не тижнів.

Реалізація: індексація та пошук

Суть вміщається приблизно у 20 рядків. Перший файл — математика, що лежить в основі: косинусна схожість, повний перебір. Другий файл — те саме зі справжньою векторною базою (Chroma): ANN-індекс замінює цикл.

Косинусна схожість в одному реченні: уявіть дві стрілки, що виходять з однієї точки, — одна для вашого запитання, інша для документа. Оцінка показує, наскільки вони вказують в одному напрямку: 1 означає «те саме», 0 — «жодного зв’язку». Довжина не має значення, і це саме те, що потрібно: запитання з двох слів і довгий абзац усе одно можуть бути про те саме, а отже, можуть опинитися сусідами.

vector-search
1import numpy as np
2 
3docs = {
4 #98c379">"forgot password": [0.9, 0.1, 0.0],
5 #98c379">"account locked": [0.85, 0.15, 0.0],
6 #98c379">"refund policy": [0.0, 0.9, 0.2],
7 #98c379">"double charge": [0.05, 0.85, 0.1],
8 #98c379">"track my package": [0.1, 0.0, 0.9],
9}
10query = np.array([0.88, 0.12, 0.02])
11 
12for name, vec in docs.items():
13 a = np.array(vec)
14 score = np.dot(a, query) / (np.linalg.norm(a) * np.linalg.norm(query))
15 print(name, round(score, 3))

Запустіть brute_force.py — і два документи про акаунт отримають найвищі оцінки, точно так, як на карті змістів з уроку про ембеддинги. with_chroma.py улаштований як код для робочої системи: ви віддаєте базі сирі тексти (ембеддинги вона будує сама), а запит за мілісекунди повертає два найближчі звернення — важку роботу виконує індекс. Замініть ці три документи своїми — і маєте семантичний пошук; передайте результати LLM замість того, щоб виводити їх на екран, — і маєте RAG.

Справжні ембеддинги, іграшковий приклад

У цьому прикладі використано «іграшкові» тривимірні вектори, щоб математика була на виду. Справжні ембеддинги мають від 384 до 3072 вимірів, але код лишається тим самим: numpy і векторним базам розмір не важливий.

Два практичні зауваження для реальної експлуатації:

  • Завжди зберігайте вихідний текст поруч із кожним вектором: він знадобиться для відповіді, а сам по собі вектор нічого не говорить.
  • Переіндексовуйте під час зміни моделі. Вектори різних моделей непорівнянні, тому після зміни моделі доведеться заново порахувати ембеддинги й перебудувати індекс.
Перевірте себе

Чому б просто не пройти циклом по всіх векторах і не порахувати відстань?

Що показує косинусна схожість?

Ви змінили модель ембеддингів. Що ламається?

Що потрібно зберігати поруч із кожним вектором?