Команда AI for Devs подготовила перевод статьи о том, как поисковые системы превращают обычный текст в токены и почему этот процесс важнее, чем кажется. Разбираем каждый этап: нормализацию, токенизацию, стоп-слова, стемминг и то, как всё это влияет на качество поиска.
Когда вы вводите предложение в строку поиска, легко представить, что поисковая система видит то же самое, что и вы. На самом деле поисковые системы (или поисковые базы данных) не хранят текстовые блоки, и они не хранят предложения. Они даже не хранят слова так, как мы их себе представляем. Они разбирают введённый текст (как индексированный, так и поисковый), очищают его, а затем собирают заново в нечто немного более абстрактное и гораздо более полезное: токены. Именно этими токенами вы и пользуетесь при поиске, и именно они хранятся в ваших инвертированных индексах для поиска.
Давайте замедлим процесс и внимательно рассмотрим этот пайплайн, остановившись на каждом этапе, чтобы увидеть, как язык разлагается и восстанавливается, а также как это влияет на результаты.
Для теста мы используем вариацию фразы "The quick brown fox jumps over the lazy dog". В ней есть всё, что делает токенизацию интересной: заглавные буквы, знаки препинания, акценты и слова, которые меняются по ходу работы пайплайна. К концу процесса фраза будет выглядеть иначе, но будет идеально подготовлена для поиска.
Фильтрация текста: приведение регистра и свёртка символов
Прежде чем разбирать текст на части, нужно отфильтровать всё, что не представляет ценности. Обычно это означает проверку всех символов строки: приведение букв к нижнему регистру и, если это ожидаемо, свёртку диакритики (как в résumé, façade или Noël) до базовых букв.
Этот шаг гарантирует, что символы нормализованы и единообразны ещё до начала токенизации. Café превращается в cafe, résumé — в resume, что позволяет поиску находить результаты независимо от акцентов. Приведение к нижнему регистру обеспечивает совпадение database и Database, хотя и добавляет тонкости: например, Olive (имя) начнёт совпадать с olive (закуска). Большинство систем принимает этот компромисс: ложные срабатывания лучше, чем пропущенные результаты. Поиск по коду здесь исключение, поскольку ему часто нужно сохранять символы и учитывать регистр, например в camelCase или PascalCase.
Посмотрим, как трансформируется исходная строка. Мы заменяем заглавную T на строчную, а é сворачиваем в e. Ничего неожиданного. Все эти поля интерактивны, так что можно подставить свои предложения и посмотреть, что получится.
Разделение текста на искомые части с помощью токенизации
На этапе токенизации отфильтрованный текст разбивается на индексируемые единицы. Здесь мы переходим от работы с предложением как с одним целым к обработке набора отдельных, пригодных для поиска элементов, называемых токенами.
Самый распространённый подход для английского текста — токенизация по пробелам и знакам препинания: разделяем по пробелам и символам, и получаем токены. Но даже этот базовый шаг имеет свои особенности: табы, переводы строк или такие слова, как full-text, могут обрабатываться по-разному. У каждой системы есть свои тонкости: например, дефолтный токенизатор Lucene превращает it’s в [it's], а Tantivy разбивает на [it, s]¹.
В целом выделяют три класса токенизаторов:
Токенизаторы, ориентированные на слова. Разбивают текст на отдельные слова по границам слов. Это включает как простые токенизаторы по пробелам, так и более продвинутые, учитывающие особенности разных языков и поддерживающие неанглийские наборы символов². Подход подходит для большинства поисковых систем, где нужно сопоставлять целые слова.
Токенизаторы частичных слов. Делят слова на более мелкие фрагменты. Полезны для сопоставления частей слов или работы со сложными терминами. N-gram-токенизаторы создают перекрывающиеся последовательности символов, а edge n-gram-токенизаторы выделяют только префиксы или суффиксы. Подход особенно эффективен для автодополнения и нечеткого поиска, но может порождать шум в результатах.
Токенизаторы структурированного текста. Предназначены для специальных форматов данных: URL, email-адресов, путей к файлам или структурированных данных. Они сохраняют значимые разделители и корректно обрабатывают доменно-специфические паттерны, которые обычные токенизаторы бы исказили. Незаменимы, когда в контенте присутствует не художественный текст, требующий особой обработки.
В нашем примере мы используем простой токенизатор, но ниже можно переключиться на триграммный токенизатор (n-gram длиной 3), чтобы почувствовать, насколько отличается результат (и не забудьте, что текст в верхнем поле можно изменять).
Отбрасывание слов-пустышек с помощью стоп-слов
Некоторые слова несут минимум смысла. Они встречаются повсюду и размывают содержание: «the», «and», «of», «are». Это стоп-слова. Поисковые системы часто полностью исключают их из обработки³, предполагая, что оставшиеся токены будут нести больше сигнала.
Однако такой подход не лишён риска. В названии The Who слово «the» имеет значение. Поэтому списки стоп-слов обычно настраиваются⁴ и не являются универсальными. В системах, поддерживающих BM25, их часто вовсе не используют, потому что ранжирование по формуле автоматически снижает вес очень частотных терминов. Но в системах без поддержки BM25 (например, Postgres tsvector) стоп-слова критически важны.
Обратите внимание, как удаление стоп-слов сразу делает список токенов более сфокусированным. Их стало не десять, а восемь, и оставшиеся токены несут больше семантического содержания.
Сведение слов к корню с помощью стемминга
Jump, jumps, jumped, jumping. Человек мгновенно видит связь между этими формами. Компьютер — нет, если только мы не дадим ему механизм для этого⁵.
Таким механизмом является стемминг. Стеммер — это набор правил, который обрезает слова до общего корня. Иногда это происходит изящно, а иногда довольно грубо. Основой большинства современных алгоритмов стемминга для английского служит алгоритм Мартина Портера 1980 года, который определил подход к удалению суффиксов с соблюдением структуры слова. Сегодня многие стеммеры основаны на варианте Snowball.
Результаты могут выглядеть странно. Database превращается в databas, lazy — в lazi. Но это нормально: стеммеры не стремятся к эстетике, их задача — единообразие. Если все формы слова lazy сводятся к lazi, поисковая система может рассматривать их как один термин⁶. Существует также лематизация, которая использует лингвистические знания для приведения слова к словарной форме, но она сложнее и дороже вычислительно по сравнению с принципом «достаточно хорошо», на котором основан стемминг⁷.
Вот финальное преобразование: наши токены сведены к основным основам. Jumped превратилось в jump, lazy — в lazi, а database — в databas. Эти формы могут не выглядеть как настоящие слова, но выполняют важную задачу: они единообразны. Если пользователь ищет jumping, jumped или jumps, все они будут сведены к jump и сопоставятся с содержимым индекса. В это�� и заключается сила стемминга: он сглаживает различия во множестве способов, которыми люди выражают одну и ту же концепцию.
Финальные токены
Наше предложение прошло полный пайплайн. То, что начиналось как «The full-text database jumped over the lazy café dog», было преобразовано на каждом этапе: очищено от пунктуации и регистра, разделено на отдельные слова, очищено от стоп-слов и, наконец, сведено к корням.
В результате получился чистый набор из восьми токенов:
Это же преобразование применяется ко всем данным, которые мы храним в инвертированном индексе, а также к нашим запросам. Если кто-то ищет «databases are jumping», запрос тоже проходит токенизацию: приведение регистра, разделение, удаление стоп-слов и стемминг. В итоге остаются databas и jump, которые идеально совпадут с содержимым индекса.
Почему токенизация важна
Токенизация редко становится поводом для гордости. На конференциях никто не хвастается своим фильтром стоп-слов. Но именно она является тихим двигателем поиска. Без неё dogs не совпадало бы с dog, а jumping не находило бы jump.
Каждая поисковая система серьёзно инвестирует в этот этап, потому что всё остальное (скоринг, ранжирование, релевантность) зависит от корректности токенов. Это не самый зрелищный, но крайне точный компонент, и когда он реализован хорошо, весь поиск работает лучше.
Русскоязычное сообщество про AI в разработке

Друзья! Эту статью подготовила команда ТГК «AI for Devs» — канала, где мы рассказываем про AI-ассистентов, плагины для IDE, делимся практическими кейсами и свежими новостями из мира ИИ. Подписывайтесь, чтобы быть в курсе и ничего не упустить!
