Онлайн час в Google Meet
Лого на 91. НЕГ „Проф. Константин Гълъбов“

12. клас · Избираем модул · Урок 25

Векторни представяния и семантично търсене

Текстът като вектор от числа, близост по смисъл чрез косинус, вектори от Gemini или от локален модел, съхранени в SQLite и претърсени с numpy — срещу LIKE.

LIKE намира думи, не смисъл

Ученик пита „къде мога да се науча да пиша код“. В базата има запис „Клуб по програмиране — всяка сряда пишем игри на C#“. WHERE opisanie LIKE '%код%' не намира нищо: нито една дума не съвпада. За човек двете са очевидно едно и също. Нужно е представяне на текста, при което близкото по смисъл е близко и като числа.

Векторно представяне (embedding)

Редица от няколкостотин числа, която представя дума, изречение или абзац така, че близките по смисъл текстове имат близки вектори.

програмиранероботикапиша кодволейболбаскетболспорт с топкахорпеяИстинското пространство има стотици измерения — тук са свити до две.
Текстовете се групират по смисъл, без някой да е задавал групите.

Откъде идва смисълът

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

Броят числа във вектора е неговата размерност: 384 при малък локален модел, 768 или 3072 при моделите на Google. Повече измерения — по-фини разлики, но повече място и по-бавно търсене. Никое отделно число няма човешко значение; смисълът е в целия вектор.

Косинусова близост

Два вектора са „близки“, когато сочат в една посока. Мярката е косинусът на ъгъла между тях:

cos(a, b) = (a · b) / (|a| · |b|)

a · b   — скаларно произведение: a₁b₁ + a₂b₂ + … + aₙbₙ
|a|     — дължина на вектора: √(a₁² + a₂² + … + aₙ²)
  • 1 — една посока: почти един и същ смисъл.
  • 0 — перпендикулярни: несвързани неща.
  • −1 — противоположни посоки (при текстове почти не се среща).

Защо ъгъл, а не разстояние: дълъг и кратък текст на една тема имат вектори с различна дължина, но с почти еднаква посока. Косинусът не се влияе от дължината. На Python са два реда с numpy:

import numpy as np

def kosinus(a, b):
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

print(kosinus([1, 2, 3], [2, 4, 6]))    # 1.0 — същата посока, различна дължина
print(kosinus([1, 0], [0, 1]))          # 0.0 — перпендикулярни

Ключови думи срещу смисъл

LIKE (ключови думи)Вектори (смисъл)
намиратекстове със същите буквитекстове със същия смисъл, дори без общи думи
силна странаточни съвпадения: име, номер, код на грешкавъпрос, зададен с други думи; синоними; друг език
слаба страна„кодя“ не намира „програмиране“не различава добре отрицание; иска модел за векторите
ценанула — вградено в SQLвектор за всеки запис и за всеки въпрос

Векторите в базата

Откъде идват

Същият клиент от урок 19, друг метод. Моделът gemini-embedding-001 приема списък от текстове и връща по един вектор за всеки:

def vektori(tekstove):
    otg = client.models.embed_content(
        model="gemini-embedding-001", contents=tekstove)
    return [e.values for e in otg.embeddings]      # списък от списъци с числа

V = vektori(["Клуб по програмиране", "Волейболен отбор"])
print(len(V), "вектора по", len(V[0]), "числа")

Без ключ или при изчерпан лимит: безплатен многоезичен модел, който се сваля в Colab и работи локално — paraphrase-multilingual-MiniLM-L12-v2 от библиотеката sentence-transformers. Разбира български и дава вектори от 384 числа. Останалият код е същият.

Къде се пазят

SQLite няма тип „вектор“. При няколко десетки или стотици записа стига колона TEXT, в която векторът е записан като JSON списък; при търсене всички се четат и се сравняват с numpy. При милиони записи това е бавно и се минава на векторна база — но това вече е извън училищния проект.

import json

cur.execute("UPDATE tekstove SET vektor = ? WHERE id = ?", (json.dumps(v), id_))
# … и обратно:
v = np.array(json.loads(red[2]))

Какво не могат векторите

  • Не различават отрицание добре: „обичам котки“ и „не обичам котки“ имат близки вектори.
  • Наследяват пристрастията от текстовете, върху които са обучени.
  • Не смятат: „клуб с над 20 участници“ е работа на WHERE, не на близостта (урок 24).
  • Винаги връщат „най-близкото“ — дори когато нищо не е близко. Прагът на близостта е ваша работа (урок 26).

Какво трябва да запомниш

  • Векторът представя текст като редица от числа; близък смисъл — близки вектори.
  • Близостта се мери с косинус на ъгъла: a·b / (|a||b|), от 1 (еднакви) до 0 (несвързани).
  • LIKE намира букви, векторите — смисъл; добрите системи ползват и двете.
  • Вектор през embed_content или локален модел; пази се в SQLite като JSON текст; търси се с numpy.
  • Векторите от различни модели не са съвместими — записвайте модела.