Бинарные метапризнаки — это дополнительные характеристики страницы или запроса, которые вы добавляете к эмбеддингу (вектору) вручную, чтобы помочь модели кластеризации учитывать то, что эмбеддинг сам не видит.
Бинарные метапризнаки — это признаки «второго порядка» (признаки признаков или результаты сравнения объектов), которые принимают строго два значения (0 или 1, да/нет, истина/ложь) и описывают не содержание само по себе, а факт наличия или отсутствия у данных некоторого формального свойства.
Простыми словами: это не «температура текста 36.6», а «температура выше нормы? (да/нет)». Это ручной способ «научить» кластеризацию учитывать интенцию, структуру или любой другой атрибут, который не выучивается автоматически из текста (либо выучивается плохо). В SEO это особенно ценно для разделения страниц с одинаковой тематикой, но разной целью.
Они называются бинарными, потому что принимают значения 0 или 1 (есть признак / нет признака). Иногда их делают вещественными от 0 до 1 (например, нормализованная длина текста), но суть та же. Пример:
есть ли слова «купить», «цена»? → 1 или 0
есть ли слова «инструкция», «как сделать»? → 1 или 0
есть ли «скачать», «бесплатно»? → 1 или 0
длина текста, поделённая на 5000 → от 0 до 1
Тематическая карта, составленная по проекту, должна содержать словарь, содержащий бинарные метапризнаки.
Применение в SEO (Поисковая оптимизация)
В SEO бинарные метапризнаки используются для построения «чёрно-белой» логики ранжирования и технического аудита. Это быстрые фильтры для сортировки миллиардов страниц.
Примеры метапризнаков (вопросы, на которые отвечает алгоритм):
Есть ли ключевое слово в Title? (0 — нет, 1 — есть)
Содержит ли URL модификатор (page=2, ?sort=)? (Фильтр дублей)
Превышает ли плотность ключей порог 5%? (Спам-сигнал)
Совпадает ли H1 с Title?
Является ли страница «вечнозеленой» (не зависит от времени)?
Зачем это нужно SEO-специалисту:
Машинное обучение в ранжировании (Яндекс, Google): деревья решений (например, RankNet) обожают бинарные признаки. Им проще отрезать: «Если страница без H1 (1) И дубль (1) — то в минус».
Кластеризация семантики: определить, относится ли запрос к коммерческому типу (да/нет), чтобы создать нужный тип страницы.
Автоматизация аудита: чек-лист «все хорошо / все плохо» проще считать роботам.
Применение в NLP (обработка естественного языка)
В NLP бинарные метапризнаки — это фундамент классической инженерии признаков (до эпохи больших языковых моделей). Они кодируют лингвистические «законы» или правила контекста.
Примеры:
is_capitalized? (Начинается ли токен с заглавной буквы?) — помогает выделить имена собственные (NER).
has_negation? (Есть ли в предложении «не», «ни», «без»?) — критично для тональности (сентимент-анализа).
is_stop_word? (Является ли слово стоп-словом?).
prev_tag_is_NOUN? (Был ли предыдущий токен существительным?) — для POS-разметки.
are_words_reversed? (Нарушен ли прямой порядок слов?).
Зачем это нужно:
Снятие неоднозначности (WSD): признак «есть ли слово “крыло” рядом со словом “самолет”? (да/нет)» отличает птицу от авиации.
Работа без GPU: до появления BERT/GPT бинарные признаки в сочетании с логистической регрессией давали быстрые и объяснимые модели (например, спам-фильтры почты).
Главная проблема (и почему это сейчас не так популярно)
Потеря оттенков серого. Бинарный признак «Текст уникален? (0 или 1)» неэффективен: он не различает 99% и 40% уникальности.
Проклятие размерности. Вместо одного вещественного признака (например, «TF-IDF = 0.73») вам нужно создавать десятки бинарных условий («>0.1», «>0.2», …, «>0.8»).
Как бинарные метапризнаки могут использоваться для кластеризации в SEO
Их эмбеддинги (даже от ada-002) будут очень близки — оба про сноуборды. Косинусное расстояние может быть 0.85. HDBSCAN с большей вероятностью положит их в один кластер. Но для SEO это разные интенты: информационный и коммерческий. Их нужно кластеризовать отдельно, потому что:
Для информационной страницы вы будете продвигать её по запросам «как выбрать сноуборд», «советы по выбору».
Для коммерческой — по «купить сноуборд», «сноуборд цена».
Если они в одном кластере, вы не сможете корректно сгруппировать запросы или страницы под единую семантическую тему.
Бинарный признак «коммерческая интенция» заставит векторы разойтись: у страницы А этот признак = 0, у Б = 1. Даже если эмбеддинги близки, суммарный вектор (эмбеддинг + [0]) vs (эмбеддинг + [1]) будет иметь евклидово расстояние не менее 1.0, что для HDBSCAN достаточно для разделения.
Пример из реальной SEO-задачи
Допустим, вы кластеризуете 1000 страниц интернет-магазина электроники. Без метапризнаков в один кластер могут попасть:
«Обзор iPhone 15» (информационный)
«Купить iPhone 15» (коммерческий)
«Ремонт iPhone 15» (сервисный, транзакционный)
Добавляем три бинарных признака:
is_review (наличие слов «обзор», «тест», «мнение»)
Теперь векторы страниц разъезжаются по трём разным областям пространства. Кластеризация становится семантической + интенционной, что гораздо полезнее для SEO.
Как их создавать без программирования
Если вы не пишете код, а работаете в готовых инструментах (типа Screaming Frog + OpenAI + какой-то GUI для кластеризации), то «бинарные метапризнаки» можно добавить как отдельные колонки в CSV перед загрузкой в кластеризатор. Например:
Адрес страницы
Текст
is_commercial
is_informational
/product
…
1
0
/blog
…
0
1
А затем в инструменте кластеризации указать, что эти колонки нужно добавить к эмбеддингам (как дополнительные измерения).
Почему они «бинарные», а не просто теги
Потому что мы численно добавляем их к вектору эмбеддинга. Эмбеддинг — это массив чисел, например, длины 768. Добавляя ещё 3 бинарных числа, мы получаем вектор длины 771. Кластеризация работает уже в этом расширенном пространстве, где «купить» и «обзор» разнесены искусственно, но управляемо.
Без бинарности (например, если бы мы просто поставили текстовую метку «коммерческая»), алгоритм кластеризации не смог бы её использовать — он работает только с числами.
Как определять такие признаки с помощью LLM
Ниже – расширение стандартного SEO-пайплайна: автоматическое определение бинарных метапризнаков с помощью LLM (GPT-4o-mini, дешёво и быстро). Модель будет анализировать основной текст страницы (или первые ~2000 символов) и выдавать структурированный ответ: коммерческая, информационная, навигационная, транзакционная, а также наличие признаков «обзор», «сравнение», «список» и т.д.
Можно использовать либо OpenAI API, либо локальную модель (через Ollama, LM Studio или LiteLLM).
1. Установка дополнительных библиотек
pip install openai tenacity
# если хотите локальную модель через Ollama:
pip install ollama
2. Код для автоматического определения признаков
Добавьте этот блок. Он создаст колонку intent_features (вектор признаков), которую вы уже используете.
import openai
from tenacity import retry, stop_after_attempt, wait_exponential
import json
import ollama # опционально
# ========== НАСТРОЙКИ LLM ==========
USE_OPENAI = True # False если используете Ollama
OPENAI_MODEL = "gpt-4o-mini" # или "gpt-3.5-turbo" для экономии
OPENAI_API_KEY = "your-key-here" # или поставьте переменную окружения
# Если используете Ollama (локально)
OLLAMA_MODEL = "llama3.1:8b" # или "mistral", "phi3"
# Промпт для LLM
INTENT_PROMPT_TEMPLATE = """Ты — SEO-эксперт. Проанализируй основной текст страницы и определи следующие бинарные признаки (0 или 1). Верни только JSON.
Признаки:
- is_commercial (есть слова "купить", "цена", "стоимость", "заказать", "доставка")
- is_informational (инструкции, руководства, как сделать, советы, почему, что такое)
- is_navigational (поиск конкретного сайта/бренда, "скачать", "вход", "личный кабинет")
- is_review (обзор, тест, мнение, впечатления, плюсы и минусы)
- is_comparison (сравнение, vs, или, лучше чем)
- is_list (список, топ, рейтинг, лучшие, подборка)
- has_faq (вопрос-ответ, часто задаваемые вопросы, Q&A)
Текст страницы (первые 2500 символов):
{text}
Ответь только JSON: {{"is_commercial": 0, "is_informational": 1, ...}}
Не добавляй пояснений."""
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def get_intent_features_openai(text: str) -> dict:
"""Вызов OpenAI API для получения признаков."""
# Обрезаем текст до разумной длины (токены не бесплатны)
truncated = text[:2500] if len(text) > 2500 else text
prompt = INTENT_PROMPT_TEMPLATE.format(text=truncated)
response = openai.chat.completions.create(
model=OPENAI_MODEL,
messages=[{"role": "user", "content": prompt}],
temperature=0.0,
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
# Приводим все значения к float (0.0 или 1.0)
return {k: float(v) for k, v in result.items()}
def get_intent_features_ollama(text: str) -> dict:
"""Локальная модель через Ollama."""
truncated = text[:2500]
prompt = INTENT_PROMPT_TEMPLATE.format(text=truncated)
response = ollama.chat(model=OLLAMA_MODEL, messages=[{"role": "user", "content": prompt}])
# Пытаемся извлечь JSON из ответа (иногда модель добавляет текст)
content = response['message']['content']
try:
# Ищем первую фигурную скобку
start = content.find('{')
end = content.rfind('}') + 1
json_str = content[start:end]
result = json.loads(json_str)
except:
# fallback: все нули
result = {"is_commercial": 0, "is_informational": 0, "is_navigational": 0,
"is_review": 0, "is_comparison": 0, "is_list": 0, "has_faq": 0}
return {k: float(v) for k, v in result.items()}
# ========== ПРИМЕНЕНИЕ КО ВСЕМ СТРАНИЦАМ ==========
print("Определение метапризнаков через LLM...")
intent_features_list = []
for idx, text in enumerate(tqdm(raw_texts, desc="LLM интенции")):
if not text or len(text.strip()) < 50:
# слишком короткий текст — считаем нейтральным
intent_features_list.append([0.0]*7)
continue
try:
if USE_OPENAI:
feats_dict = get_intent_features_openai(text)
else:
feats_dict = get_intent_features_ollama(text)
# Преобразуем словарь в список в фиксированном порядке
ordered_keys = ["is_commercial", "is_informational", "is_navigational",
"is_review", "is_comparison", "is_list", "has_faq"]
features = [feats_dict.get(k, 0.0) for k in ordered_keys]
except Exception as e:
print(f"Ошибка на странице {idx}: {e}")
features = [0.0]*7
intent_features_list.append(features)
intent_features = np.array(intent_features_list)
print(f"Метапризнаки LLM: {intent_features.shape}")
Практические замечания
Стоимость (при использовании OpenAI)
GPT-4o-mini: ~$0.15 за 1M токенов (вход) и $0.6 за 1M токенов (выход). На 1000 страниц (каждая ~2000 символов ≈ 500 токенов) стоимость составит около $0.08–0.10.
Скорость
OpenAI: ~0.5–1 секунда на страницу. 1000 страниц = 15–20 минут. Можно ускорить с помощью asyncio и пакетной обработки.
Ollama (локально на GPU): быстрее, но нужно настроить модель.
Как улучшить промпт под русский язык
Если ваш контент на русском, замените английские ключевые слова в промпте на русские. Пример:
Вы можете запустить локальную модель через Ollama (установить отдельно). Для большинства SEO-задач достаточно модели llama3.1:8b или mistral:7b. Они дадут приемлемое качество бесплатно.
Проверка качества
После того как получите кластеры, сравните распределение LLM-признаков внутри кластеров:
# После кластеризации
for cluster_id in set(cluster_labels):
if cluster_id == -1: continue
mask = cluster_labels == cluster_id
mean_intents = intent_features[mask].mean(axis=0)
print(f"Кластер {cluster_id}: коммерч={mean_intents[0]:.2f}, информ={mean_intents[1]:.2f}, обзор={mean_intents[3]:.2f}")
Если внутри кластера смешаны страницы с коммерческими и информационными признаками (например, 0.5 и 0.5), значит, кластеризация всё ещё не идеальна — увеличьте вес этих признаков (например, продублируйте их или умножьте на коэффициент 2 перед конкатенацией).
Результат
Полностью автоматическое извлечение 7 бинарных метапризнаков с помощью LLM.
Гибкость (OpenAI или локальная модель).
Простую интеграцию в существующий пайплайн.
Бинарные метапризнаки — это логические фильтры для текста и страниц. В SEO они помогают отсеивать мусор и строить правила ранжирования. В NLP — кодировать лингвистические закономерности. Сегодня их вытесняют нейросети, но для объяснимого AI (почему алгоритм принял решение) они остаются незаменимыми.