Разбираемся, как работает семантический поиск: эмбеддинги, k-ближайшие соседи и примеры на Python. Узнайте, почему «шоколадное молоко» и «молочный шоколад» — это разные вещи.
Большую часть истории интернета поиск напоминал разговор с буквальным роботом. Вы вводите «удобная обувь для стоячей работы», а он выдаёт страницу, где есть слова «удобная», «обувь» и «работа» — даже если это блог про то, как удобно лежать весь день и вообще не носить обувь. Технически верно, по духу — бесполезно.
Но этот робот получил апгрейд. Технология называется семантический поиск. Её суть: системе важно, что вы имеете в виду, а не просто какие слова напечатали.
Ключевой поиск — это игра в совпадение слов. Он берёт ваш запрос, ищет документы, содержащие те же слова (плюс иногда синонимы), и на этом всё. У него нет ни малейшего понятия, что эти слова значат. Это библиотекарь, который найдёт каждую книгу со словом «яблоко» на обложке, но не сможет сказать, хотите вы фрукт или телефон.
Семантический поиск играет в другую игру. Он спрашивает: «Что этот человек на самом деле пытается найти?» — и затем ищет это, даже если ваши точные слова нигде в ответе не встречаются.
Моё любимое доказательство, что смысл важнее слов: «шоколадное молоко» vs «молочный шоколад». Те же два слова. Поменяйте порядок — и вы перешли от напитка к плитке. Ключевой движок видит одинаковые ингредиенты и пожимает плечами. Семантический поиск знает, что вы описали два совершенно разных продукта. Этот маленький переворот — вся революция в миниатюре.
Вот часть, которая звучит как магия, но на деле — просто умная математика. Компьютеры не понимают слова. Они понимают числа. Поэтому семантический поиск берёт каждое слово, предложение и документ и преобразует их в длинный список чисел, называемый эмбеддингом — по сути, координаты, которые говорят, где находится этот смысл.
Представьте гигантскую невидимую карту. Не карту мест, а карту смыслов. На этой карте «собака» и «щенок» находятся практически рядом. «Кошка» — в том же районе. «Квантовая механика» — на другом конце города, а «шоколадное молоко» припарковано на почтительном расстоянии от «молочного шоколада» (по крайней мере, я так предполагал; позже я проверил это и был смирен).
Вещи, которые означают похожее, оказываются близко; те, что различаются, расходятся. Вот и весь эмбеддинг: адрес вещи в пространстве смыслов. Самое красивое — машина учится этим адресам сама, читая огромное количество текста и замечая, какие слова постоянно встречаются в похожем окружении. Слова, которые тусуются в одинаковых контекстах, получают похожие адреса. Никто не размечает это вручную.
Когда всё живёт на этой карте, поиск становится удивительно простым. Вы вводите запрос. Движок помещает ваш запрос на ту же карту в виде координат. Затем он просто оглядывается и спрашивает: «Кто тут рядом?» Техническое название для «найти ближайших соседей» — алгоритм k-ближайших соседей, но вы можете мысленно записать это как «хватай то, что припарковано ближе всего к тебе». Результаты, которые находятся ближе всего к вашему запросу в пространстве смыслов, лучше всего соответствуют вашему намерению — и они выдаются первыми.
Вот почему семантический поиск может выдать идеальный ответ, даже если он не содержит ни одного слова из вашего вопроса. Вы искали «удобная обувь для стоячей работы», а он возвращает руководство по сестринским клогам и противоутомительным стелькам — почти никакого совпадения по словам, но прямо в яблочко по смыслу, потому что все эти вещи живут в одном углу карты.
Смысл не фиксирован, он меняется в зависимости от того, кто спрашивает и где. Возьмём слово «футбол». Наберите его в Лондоне — вы почти наверняка имеете в виду вид спорта с круглым мячом и драматичными падениями. В Далласе — шлемы, тачдауны и запрет касаться мяча руками. Одно слово, два разных вида спорта, и хороший семантический движок использует контекст: ваше местоположение, формулировку, даже то, что вы искали пять минут назад, чтобы понять, какой из них вы имели в виду.
Контекст — это также намерение. Вы пытаетесь что-то узнать, купить или найти дорогу? «Лучшая кофемашина», вероятно, хочет обзоры и кнопку покупки. «Как работает кофемашина» хочет объяснение, а не страницу оформления заказа. Улавливать эту разницу — половина работы.
Я не просто поверил на слово — я запустил реальные тесты. Использовал модель sentence-embedding all-MiniLM-L6-v2 и небольшой Python-скрипт, чтобы проверить утверждения выше на реальных оценках косинусного сходства.
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')
# Наши документы
docs = {
'chocolate_milk': 'Холодный напиток, приготовленный из молока и шоколадного сиропа.',
'milk_chocolate': 'Сладкая плитка, изготовленная из какао-масла, сахара и сухого молока.',
'comfy_shoes': 'Удобная обувь для стоячей работы: сестринские клоги с противоутомительными стельками.',
'puppy': 'Маленькая собака, которая любит играть и спать.',
'quantum': 'Квантовая механика: физика субатомных частиц.',
'football_uk': 'Футбол: командный вид спорта с круглым мячом, популярный в Европе.',
'football_us': 'Американский футбол: спорт с овальным мячом, тачдаунами и шлемами.'
}
# Кодируем документы и запросы
doc_embeddings = {k: model.encode(v) for k, v in docs.items()}
queries = ['chocolate milk', 'milk chocolate', 'comfy shoes for standing all day']
for q in queries:
q_emb = model.encode(q)
print(f'Query: "{q}"')
print('-' * 60)
for doc_name, doc_emb in sorted(doc_embeddings.items(), key=lambda x: util.cos_sim(q_emb, x[1]), reverse=True):
sim = util.cos_sim(q_emb, doc_emb).item()
overlap = len(set(q.lower().split()) & set(docs[doc_name].lower().split()))
print(f'{doc_name:20} overlap={overlap:2} similarity={sim:.3f}')
print()
Результат:
Query: "chocolate milk"
------------------------------------------------------------
doc overlap similarity
chocolate_milk 2 0.751
milk_chocolate 2 0.727
comfy_shoes 0 0.130
puppy 0 0.111
quantum 0 0.058
football_uk 0 0.058
football_us 0 -0.056
Query: "milk chocolate"
------------------------------------------------------------
doc overlap similarity
milk_chocolate 2 0.758
chocolate_milk 2 0.733
puppy 0 0.117
comfy_shoes 0 0.101
quantum 0 0.073
football_uk 0 0.034
football_us 0 -0.078
Query: "comfy shoes for standing all day"
------------------------------------------------------------
doc overlap similarity
comfy_shoes 2 0.583
football_uk 0 0.066
puppy 0 0.051
milk_chocolate 0 0.043
football_us 0 0.042
chocolate_milk 0 0.002
quantum 0 -0.078
Пример с удобной обувью сработал идеально. Семантическое сходство для документа про клоги составило 0.583 — намного выше следующего кандидата (0.066). Ключевой поиск поймал только два общих слова («all», «day»), полностью пропустив основную идею: комфорт и обувь. Смысл был очевиден, буквальные слова — нет.
Случай с шоколадным молоком тоже подтвердился — но едва-едва. Ключевой поиск дал одинаковое количество совпадений для обоих запросов (2 слова). Семантический поиск правильно различал их, но разница составила всего 0.02–0.03. Реально, но не драматично.
А вот что меня удивило: когда я убрал предложения и оставил только две короткие фразы — «chocolate milk» и «milk chocolate», без контекста — их сходство оказалось 0.980. Практически одна и та же точка на карте. Так что «почтительное расстояние» я преувеличил. Двух слов модели недостаточно; решающую роль играет предложение вокруг них. Контекст — не просто приятное дополнение; для коротких фраз это единственное, что не даёт «напитку» и «конфете» слиться в одну координату.
Для сравнения: «dog» и «puppy» дали 0.804 (действительно близкие соседи), а «dog» и «quantum mechanics» — 0.214 (действительно далеко). Так что метафора карты в целом работает. Проблема именно в коротких, неоднозначных парах с перестановкой слов — им нужен контекст, чтобы развести значения.
Помимо того, что это просто умно, семантический поиск делает жизнь менее раздражающей. Вам не нужно угадывать волшебные ключевые слова, за которыми прячется база данных. Вы можете спрашивать по-человечески — небрежно, разговорно, полуоформленно — и всё равно получать то, что хотели. Со временем такие системы также обучаются: следят, на какие результаты люди нажимают и остаются, и подстраиваются, чтобы работать лучше.
Это разница между поисковой строкой, которая осуждает ваше правописание, и той, которая действительно улавливает суть. И после многих лет споров с буквальными роботами «улавливает суть» кажется маленьким чудом.
sentence-transformers и поиграйте с эмбеддингами.chromadb или faiss позволяют легко добавить семантический поиск в любое приложение на Python.Так что в следующий раз, когда вы введёте что-то расплывчатое в строку поиска и получите именно то, что вертелось у вас в голове — это не удача. Это карта смыслов, горсть координат и робот, который наконец научился читать между строк.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →