⚖️ Старый vs Новый: слова vs смысл

← К оглавлению урока

⚡ Что меняем

  • Поиск по словам (TF-IDF) → поиск по смыслу (эмбеддинги)
  • «Голый» запрос → запрос с таймаутом и автоповтором
  • FAISS L2 → косинусная близость с нормализацией

Сравнение 1: поиск по словам vs по смыслу

🟡 Поверхностный (TF-IDF / ключевые слова)

# Сравнивает символы/слова, НЕ смысл
similarity("латте", "капучино")  # ≈ 0 — буквы разные
# «отзыв о кофе» не найдётся,
# если в нём нет слова «кофе»

Подходит для точных совпадений, опечаток, коротких строк. Смысла не понимает.

🟢 Семантический (эмбеддинги)

# Сравнивает векторы смысла
emb("латте")  ↔  emb("капучино")  # близки!
# «Мне нравится латте» найдёт
# отзывы про кофе без слова «кофе»

Понимает близость по смыслу: синонимы, перефразировки, разные языки.

Сравнение 2: запрос без защиты vs устойчивый

🟡 Хрупкий запрос

r = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=[prompt])
print(r.text)
# зависнет при перегрузке;
# упадёт на первой ошибке 429/500

🟢 Устойчивый запрос

@retry(stop=stop_after_attempt(3),
       wait=wait_exponential(min=2, max=10))
def ask(prompt):
    return client.models.generate_content(
        model="gemini-2.0-flash",
        contents=[prompt]).text
# таймаут на клиенте + 3 попытки

Сравнение 3: L2 vs косинус (best practice)

🟡 Как в лекции (L2)

index = faiss.IndexFlatL2(dim)
index.add(embeddings)
# евклидово расстояние;
# чувствительно к длине вектора

🟢 Косинусная близость

faiss.normalize_L2(embeddings)   # нормализуем
index = faiss.IndexFlatIP(dim)   # скалярное произв.
index.add(embeddings)
# = косинус: сравнивает «направление» смысла
⚠️ Для текстовых эмбеддингов косинусная близость обычно предпочтительнее евклидовой. Способ нормализации и тип индекса зависят от версии FAISS — сверяйтесь с документацией.