Значення як координати: від ембеддингів до баз даних векторів та RAG
Ембеддинги, косинусна схожість, векторні бази даних та цикл RAG пояснюються за допомогою чотирьох коротких речень про собак, котів та Париж, щоб стимулювати кліки у семантичному пошуку.
Джерела: Meaning as Coordinates: Semantic Search, Vector Databases, and RAG
Поле пошуку, яке не повертає жодних результатів для запиту „хороша тварина для активної людини“, хоча база знань сповнена порад щодо собак, чітко демонструє, де не вистачає точного збігу ключових слів. Його виправлення означає розглядати значення як позицію у просторі. Саме ця ідея лежить в основі семантичного пошуку, векторних баз даних та технологій генерації з підтримкою пошуку, і один невеликий приклад достатній, щоб зрозуміти все це.
Приклад, який використовується протягом усього тексту
Уявіть собі мініатюрну базу знань, що складається з чотирьох речень:
1. Dogs are loyal and need daily walks and exercise
2. Cats are independent pets that groom themselves
3. Golden retrievers are friendly family dogs
4. The capital of France is Paris
Користувач запитує:
"What's a good pet for an active person who exercises a lot?"
У запиті ніколи не міститься слово „собака“ чи „гуляння“. Звичайний пошук за ключовими словами не дозволить знайти щось корисне. Проте машина все одно може надати правильну відповідь, адже вона порівнює розташування текстів у просторі значень, а не ті слова, які вони мають спільні.
Схожі значення, сусідні точки
Вектор — це просто рядок чисел, який кодує значення тексту. Оскільки схожі значення створюють схожі вектори, пошук пов’язаних текстів перетворюється на пошук сусідніх точок. Це перетворює мовну проблему на геометричну, що є основою сучасних пошукових систем, систем рекомендацій та технології RAG — методу, який надає мовній моделі зовнішню бібліотеку для пошуку інформації, щоб вона перестала вигадувати речі.
Коли значення є набором координат, робота з значенням означає роботу з відстанню та напрямком, у чому комп’ютери демонструють виняткову здатність.
Ембеддинги для цілих уривків
Вектори на рівні слів недостатні для пошуку в документах. Для речення, абзацу чи сторінки потрібен окремий вектор. Моделі типу Transformer можуть створювати такий вектор: достатньо ввести фрагмент тексту, і вони повертають один вектор, який зазвичай називається ембеддингом та узагальнює його значення.
"Dogs need daily walks" ──► [ 0.91, 0.08, -0.30, ... ] (e.g. 384 numbers)
"The capital is Paris" ──► [-0.05, 0.62, 0.44, ... ]
Ембеддинг речення стискає уривок у список чисел фіксованої довжини. Два тексти на одну тему дають схожі списки, а неспоріднені тексти — дуже різні. Окремі числа нічого не означають для людини. Важливо те, як вони взаємопов’язані, і ніхто не читає їх безпосередньо. Їх лише порівнюють.
Образ, який потрібно мати на увазі
Уявіть кожен текст як точку у просторі. Тексти з схожим змістом знаходяться близько один до одного, а неспоріднені — далеко один від одного.
(a simplified 2-D "meaning space")
dog walks • • golden retriever ← the "dogs" neighborhood
\ /
(pets)
cat grooming • • Paris ← far away, different topic
На цій спрощеній карті два речення про собак знаходяться поруч, речення про кота розташоване біля них, оскільки воно також стосується домашньої тварини, а речення про Париж знаходиться в зовсім іншому регіоні. Справжні простори вбудовування мають сотні вимірів, але інтуїція залишається тією ж: пов’язані значення означають близькі точки. Усе, що йде далі, — це способи знаходження найближчих точок.
Пошук за ключовими словами проти семантичного пошуку
Пошук за ключовими словами знаходить слова. Пошук за словом „вправи“ проігнорує документ, у якому йдеться про щоденні прогулянки, оскільки немає спільних слів. Цей метод є швидким, але буквальним, і він не враховує значення.
Семантичний пошук знаходить значення. Запит перетворюється на вектор, і повертаються документи, вектори яких знаходяться найближче до нього, незалежно від слів, які вони містять. Ось рейтинг, отриманий для прикладу, з використанням фактичних значень схожості:
Query: "a good pet for an active person who exercises"
0.999 ############################# Golden retrievers are friendly family dogs
0.995 ############################# Dogs are loyal and need daily walks and exercise
0.398 ########### Cats are independent pets that groom themselves
0.059 # The capital of France is Paris
У цьому запиті немає спільних ключових слів із реченнями про собак, проте обидва мають найвищий бал приблизно 1,0. Речення про кота знаходиться посередині, оскільки кіт також є домашнім улюбленцем, а речення про Париж має значення майже нульове. Пошук за ключовими словами не дав би жодних результатів. Саме тому семантичний пошук здається інтелектуальним: він розпізнає, що активна людина, яка займається спортом, та собака, якій потрібні щоденні прогулянки, — це майже одне й те саме. Різниця між пошуком за ключовими словами та семантичним пошуком — це різниця між відсутністю результатів та відповіддю, яку насправді потрібно було знайти.
Вимірювання близькості за допомогою косинусової схожості
Звичайним інструментом для вимірювання близькості двох векторів є косинусова схожість. Він враховує кут між двома стрілками та ігнорує їхню довжину.
- Стрілки, спрямовані в одному напрямку, дають схожість близько 1, що означає дуже схожий зміст.
- Стрілки під прямим кутом дають схожість близько 0, що означає відсутність зв’язку.
- Стрілки, що вказують у протилежних напрямках, свідчать про схожість близьку до -1, що означає протилежне значення.
Невеликий кут між запитом та документом про собаку виглядає так:
^ query
│ /
│/ small angle → cosine ≈ 1 (similar)
└──────► dog doc
І широкий кут на документ з Парижа:
^ query
│
└──────► Paris doc big angle → cosine ≈ 0 (unrelated)
Використовується кут замість довжини, щоб довгі та короткі тексти можна було порівнювати справедливо. Математичні обчислення вміщуються в одну рядок: потрібно помножити вектори та нормалізувати результат.
import numpy as np
def cosine(a, b):
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
Ця невелика функція лежить в основі кожного балу у наведеному вище рейтингу. Бали коливаються від -1 до 1, і значення близьке до 1 означає, що два тексти мають однакове значення.
Векторні бази даних для пошуку у великих масштабах
У прикладі є чотири документи. Реальні системи містять мільйони чи мільярди документів – наприклад, кожен заявок на підтримку, сторінку продукту чи абзац корпоративної документації. Порівнювати запит із мільярдом векторів по одному під час кожного пошуку є надто повільним.
Для цього існують векторні бази даних. Це сховища, створені для ефективного виконання однієї задачі – прийняття вектора запиту та швидкого повернення найближчих збережених векторів. Вони використовують метод приблизного пошуку найближчого сусіда – сімейство розумних індексів, які знаходять майже найближчі відповіді, уникаючи більшості порівнянь. Невелика точність обмінюється на значне прискорення.
documents ─► embed ─► [ vector database ]
│ stores millions of vectors
│ + a smart index (ANN)
query ─► embed ─────────► │
▼
top-k nearest matches (in milliseconds)
Документи проходять через модель ембеддингу лише один раз та зберігаються разом із індексом. Коли надходить запит, він також піддається ембеддингу, і база даних миттєво повертає k найкраще відповідних елементів, незалежно від кількості векторів, які вона містить. До продуктів у цій галузі належать FAISS, Pinecone, Weaviate, Chroma та Milvus, а також pgvector — розширення, яке дозволяє використовувати пошук векторів у звичайному PostgreSQL. Деталі можуть відрізнятися, але всі вони зберігають ембеддинги та швидко знаходять сусідні елементи. Коротко кажучи, база даних векторів — це пошукова система для змісту.
У чому помиляються моделі простої мови
Великі моделі мови справляють враження, але самі по собі мають три серйозні слабкості:
- Галюцинації. Коли вони не впевнені, вони формулюють відповіді, які здаються переконливими та правильними, але насправді є хибними.
- Межа знань. Модель знає лише те, що було в даних для навчання, зафіксованих у певну попередню дату.
- Відсутність знань про ваші дані. Внутрішні документи, приватні нотатки та зустріч минулого тижня залишаються невідомими для моделі.
You: "What's our refund policy?"
Plain LLM: "Typically, refunds are offered within 30 days..." ← a guess, not YOUR policy
Коли запитують про політику повернення грошей, проста модель відповідає з пам’яті, роблячи правдоподібні припущення, а не наводячи фактичну політику компанії. Це ризиковано щодо будь-якої фактичної чи конфіденційної інформації. Необхідно надавати саме правильні факти у момент відповіді моделі, без необхідності її перенавчання, і RAG саме це й робить.
Retrieval-augmented generation
RAG поєднує все вищезазначене в один цикл. Замість того, щоб просити модель відповідати з пам’яті, спочатку за допомогою семантичного пошуку знаходять відповідні документи, а потім прослять модель дати відповідь на їхній основі. Назва є описом: retrieval — це пошук відповідного тексту, augmented — додавання його до запиту, а generation — це можливість для моделі написати відповідь.
1. Question ─► embed ─► search the vector DB ─► top relevant chunks
│
2. Build a prompt: "Using ONLY the context below, answer the question.
Context: [retrieved chunks]
Question: [user's question]"
│
3. LLM generates an answer grounded in the retrieved text ─► with sources
Виконання прикладу:
Question: "What's a good pet for an active person?"
→ retrieves docs 1 & 3 (the dog sentences, from Figure 3)
→ prompt: "Using this context: [Dogs need daily walks and exercise;
Golden retrievers are friendly family dogs] — answer the question."
→ LLM: "A dog — especially a golden retriever — is a great fit, since
they need daily walks and exercise. (Source: docs 1, 3)"
Тепер відповідь має підстави. Вона ґрунтується на реальних документах, якими керує власник, і може вказати джерело факту. Значно менше вигаданих відповідей потрапляє всередину. RAG — це семантичний пошук у поєднанні з мовною моделлю: спочатку знаходяться правильні факти, а потім модель сформулює відповідь.
Практичний процес та його основна проблема
RAG складається з двох фаз. Налаштування виконується один раз, а запити — щоразу, коли хтось ставить запитання.
── SETUP (once) ── ── QUERY (every question) ──
documents user question
│ split into chunks │ embed
│ embed each chunk │ search vector DB
│ store in vector DB │ get top-k chunks
▼ │ build prompt with chunks
[ vector database ] ◄───── search ──────┤ send to LLM
▼
grounded answer
Документи діляться на фрагменти, вбудовуються та зберігаються один раз. Кожне запитання також вбудовується, порівнюється зі збереженими фрагментами, і найкращі з них надсилаються до моделі для формування кінцевої відповіді.
Проблема полягає у самому процесі розділення документів на фрагменти перед їх вбудовуванням. Занадто великі фрагменти об’єднують кілька тем у один вектор, що призводить до нечітких результатів пошуку. Занадто малі фрагменти втрачають контекст, через що відповідь може бути неповною. Вибір розміру фрагментів є одним із найважливіших практичних рішень у функціонуючій системі RAG, і це нагадування про те, що проста підготовка даних впливає на все подальше.
Те саме у коді
Частина системи RAG, яка відповідає за пошук, — це базовий семантичний пошук, який може бути реалізований лише кількома рядками коду на NumPy. Він отримує вектори деяких документів та вектор запиту, а потім сортує документи за косинусовою схожістю:
import numpy as np
def cosine(a, b):
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))def search(query_vec, doc_vecs, docs, k=2):
scores = [(cosine(query_vec, v), d) for v, d in zip(doc_vecs, docs)]
return sorted(scores, reverse=True)[:k] # top-k most similar
Якщо надати чотири вектори тварин та Парижа, а також запит про активну особу, система спочатку повертає два документи про собак, що відповідає раніше показаному рейтингу. Це і є семантичний пошук у кількох рядках коду.
У реальному проекті вектори не створюються вручну. Використовується модель ембеддингу та сховище векторів, і код виглядає приблизно так:
# Illustrative real-world usage (libraries download a model, so not run here)
from sentence_transformers import SentenceTransformer
import chromadb
model = SentenceTransformer("all-MiniLM-L6-v2") # turns text into vectors
db = chromadb.Client().create_collection("pets")db.add(documents=my_docs, embeddings=model.encode(my_docs).tolist(),
ids=[str(i) for i in range(len(my_docs))])q = "a good pet for an active person"
hits = db.query(query_embeddings=model.encode([q]).tolist(), n_results=2)
# hits now holds the top-2 most semantically similar documents
Бібліотеки виконують більшу частину роботи, але в основі все одно використовують пошук за косинусною схожістю. Семантичний пошук — це кілька рядків математичних формул для векторів, а справжні інструменти оптимізують цю ідею за рахунок швидкості та масштабування.
Весь процес
Текст спочатку складається з символів та токенів, потім перетворюється на ембеддинги, далі отримує контекст завдяки механізму уваги в трансформері, і нарешті стає пошуковою картою знань, яка робить мовні моделі точними та корисними.
raw text
→ characters / tokens / embeddings [article 1: From Characters to Meaning]
→ attention turns them into context-aware
vectors inside a transformer [article 2: Attention, Explained Simply]
→ those vectors become coordinates we can
store, search, and retrieve [article 3: this one]
→ semantic search (find by meaning)
→ vector databases (search at scale)
→ RAG (ground an LLM in real data)
Основною ідеєю, яка пронизує все це, є ідея про те, що значення — це точка у просторі. Спочатку це стосується слів, потім речень у контексті, а нарешті — цілих бібліотек документів, які можна шукати та аналізувати.
Ще одне відмінність, яку варто пам’ятати. RAG надає моделі доступ до знань без зміни самої моделі, наче даючи їй нотатки для читання під час формування відповіді. Тонку налаштування — це інший підхід, при якому внутрішні ваги моделі коригуються шляхом навчання на прикладах. RAG забезпечує знання для читання, тоді як тонка налаштування змінює саму модель. Вміння вибирати один із цих підходів є самостійною практичною навичкою.
Пов’язана література
- Практичні нотатки: Межі векторного пошуку в RAG — косинусна схожість та інше — Покрокове керівництво з використання Практичних нотаток: Межі векторного пошуку в RAG — косинусна схожість та інше: шаблони коду, перевірки та інші елементи для команд, які впроваджують цю модель.
- Практичні нотатки: Векторні бази даних для продакшн-середовищ RAG: індексування, гібридний пошук — Покрокове керівництво з використання Практичних нотаток: Векторні бази даних для продакшн-середовищ RAG: індексування, гібридний пошук: шаблони коду, перевірки та інші елементи для команд, які впроваджують цю модель.