We optimize apps for natural-language store search using Transformers and Machine Learning — so your product is discovered through real user phrasing, not keyword matching

Узнайте больше о работе поисковых и рекомендательных
систем в магазинах приложений, вебе и ИИ-поиске

Популярные термины глоссария
Глоссарий 13 минут чтения

Семантическая плотность

Релизнуто
Узнали

В работе Sagi, Kaufmann и Clark термин обозначает компактность контекстных векторов, построенных для отдельных употреблений одного слова. Чем ближе эти векторы друг к другу, тем однороднее контексты употребления терма в исследуемом срезе корпуса.

В этой статье под семантической плотностью понимается именно среднее попарное сходство контекстных представлений одного объекта: терма, запроса, документа, товара или другого элемента, для которого собрано несколько наблюдений.

Результат — числовая оценка компактности группы векторов. Она не измеряет релевантность документа запросу, качество текста, полноту темы или вероятность высокого ранга.

От нее нужно отличать:

  • частоту ключевого слова;
  • лексическую плотность;
  • косинусное сходство одной пары;
  • плотность k ближайших соседей;
  • hubness;
  • семантическую энтропию;
  • semantic density у Qiu и Miikkulainen для оценки доверия к ответу языковой модели.

Место в поисковой системе

Семантическая плотность обычно относится к офлайн-анализу корпуса или журналов, а не к основной функции извлечения документов.

Контур

  1. Система получает документы, запросы или пользовательские события.
  2. Из данных извлекаются наблюдения одного объекта: контексты употребления терма, документы с удовлетворительными кликами по запросу, представления товара в разных источниках.
  3. Кодировщик строит вектор для каждого наблюдения.
  4. Наблюдения группируются по объекту, языку, домену, времени или другому срезу.
  5. Для группы рассчитываются компактность, доверительный интервал и дополнительные характеристики распределения.
  6. Результат используется для диагностики корпуса, исследования семантического дрейфа либо как кандидат в признаки модели.
  7. Польза проверяется на оценках релевантности и поисковых метриках.

Вход

  • матрица XRn×dX\in\mathbb{R}^{n\times d}X∈Rn×d;
  • одна строка на наблюдение;
  • идентификаторы документов и объектов;
  • язык, домен, время;
  • версия кодировщика;
  • параметры выделения контекста.

Выход

  • среднее попарное сходство;
  • средний угол;
  • число наблюдений;
  • доверительный интервал;
  • квантили распределения сходств;
  • различие между срезами.

Возможные задачи

Семантический дрейф терма. Сравниваются контексты слова в двух временных срезах.

Неоднозначность запроса. Можно измерять компактность представлений документов, с которыми пользователи успешно взаимодействовали после одного запроса. Низкая компактность может соответствовать нескольким намерениям. Без поправки на позиционное смещение такой показатель описывает текущую политику ранжирования, а не чистое намерение пользователя.

Контроль обучающих данных. Резкое падение компактности класса может указывать на смешение доменов, ошибочное объединение меток или изменение источника данных.

Признак модели ранжирования. Показатель можно передать модели как числовой признак. Сам факт его вычисления не доказывает пользу. Требуется абляционный эксперимент.

Место в рекомендательной системе

В рекомендательных системах стандартизованной метрики с таким названием нет. Близкая операция — расчет компактности представлений:

  • объектов в истории пользователя;
  • кандидатов в одной выдаче;
  • объектов одного класса;
  • представлений одного товара из разных источников.

Контур

  1. Система собирает события и признаки объектов.
  2. Модель строит представления пользователей и объектов.
  3. Для нужной группы вычисляется локальная компактность.
  4. Значение используется как диагностический показатель либо признак диверсификации.
  5. Результат проверяется на Recall@K, NDCG@K, покрытии каталога, разнообразии и онлайн-метриках.

Высокая компактность истории может означать узкий интерес пользователя. Она также может быть следствием схлопывания представлений или смещения действующей рекомендательной политики. Эти случаи нельзя различить по одному среднему косинусу.

История и происхождение

Sagi, Kaufmann и Clark представили Semantic Density Analysis в 2009 году для исследования изменения значений слов. Для каждого употребления целевого слова они строили вектор контекста, после чего сравнивали средний угол между такими векторами. Ограниченное употребление слова давало компактную группу; полисемия и разнообразие контекстов увеличивали разброс. Авторы использовали LSA, окно в 15 слов с каждой стороны и 100-мерное пространство.

Задача работы — историческая семантика. Авторы не предлагали ранжировать документы по этому показателю.

В 2024 году Qiu и Miikkulainen независимо использовали название semantic density для оценки доверия к конкретному ответу языковой модели. Их показатель суммирует близость целевого ответа к другим вероятным ответам на тот же запрос. Близость определяется с учетом семантической эквивалентности, нейтральности и противоречия. Это другая задача и другая формула.

Semantic entropy также оценивает неопределенность генерации, но агрегирует вероятности по классам семантически эквивалентных ответов и характеризует запрос или распределение ответов, а не локальную плотность одного целевого ответа.

Математическая основа

Пусть объект имеет nn наблюдений. Каждое наблюдение представлено нормированным вектором:

xiRd,xi2=1.x_i\in\mathbb{R}^d,\qquad \|x_i\|_2=1.

Попарное косинусное сходство:

sij=xixj.s_{ij}=x_i^\top x_j.

Семантическая плотность группы:

SDcos(X)=2n(n1)i<jsij.SD_{\cos}(X)= \frac{2}{n(n-1)} \sum_{i<j}s_{ij}.

Диапазон показателя — от 1-1−1 до 111.

  • Значение, близкое к 111, означает почти совпадающие направления.
  • Значение около 000 означает слабую среднюю связь.
  • Отрицательное значение означает преобладание противоположных направлений. Для обычных текстовых представлений такое встречается редко, но математически допустимо.

Линейный расчет без всех пар

Полная матрица сходств требует O(n2)O(n^2)O(n2) памяти или времени. Для среднего косинуса она не нужна.

Пусть:S=i=1nxi.S=\sum_{i=1}^{n}x_i.

Тогда:S2=i=1nxi2+2i<jxixj=n+2i<jxixj.\|S\|^2 = \sum_{i=1}^{n}\|x_i\|^2+ 2\sum_{i<j}x_i^\top x_j = n+2\sum_{i<j}x_i^\top x_j.

Следовательно:SDcos(X)=S2nn(n1).SD_{\cos}(X)= \frac{\|S\|^2-n}{n(n-1)}.

Эквивалентная запись через центроид:SDcos(X)=nxˉ21n1,xˉ=1nixi.SD_{\cos}(X)= \frac{n\|\bar{x}\|^2-1}{n-1}, \qquad \bar{x}=\frac{1}{n}\sum_i x_i.

Вычислительная сложность:

  • время — O(nd)O(nd)O(nd);
  • дополнительная память — O(d)O(d)O(d);
  • расчет можно выполнять потоком без хранения всех попарных сходств.

Средний угол

В исходной работе использовался угол:

A(X)=2n(n1)i<jarccos(xixj).A(X)= \frac{2}{n(n-1)} \sum_{i<j} \arccos(x_i^\top x_j).

Чем меньше средний угол, тем компактнее группа. Средний угол нельзя получить из среднего косинуса простым применением arccos\arccosarccos, поскольку функция нелинейна:

E[arccos(s)]arccos(E[s]).\mathbb{E}[\arccos(s)]\neq \arccos(\mathbb{E}[s]).

Для точного среднего угла нужен перебор пар. На больших группах применяют выборку пар.

Поправка на различия документов

Контексты могут быть похожи из-за общего автора, жанра, периода или источника. Sagi и соавторы предлагали вычитать угол между векторами исходных документов из угла между контекстами:

aijadj=aijctxaijdoc.a^{adj}_{ij} = a^{ctx}_{ij}-a^{doc}_{ij}.

Такая поправка нужна при сравнении корпусов, у которых различается общая стилистическая или тематическая структура.

Это компактность, а не плотность вероятности

Среднее попарное сходство не является оценкой функции вероятностной плотности. У него нет нормированного интеграла, полосы ядра и вероятностной интерпретации. Название «плотность» в работе Sagi относится к тому, насколько тесно группа векторов упакована в пространстве.

Отдельная формализация для языковых моделей

У Qiu и Miikkulainen целевой объект — ответ yy^*y∗ модели на запрос xxx. Для того же запроса генерируются опорные ответы y1,,yMy_1,\ldots,y_My1​,…,yM​.

SDLLM(yx)=i=1Mp(yix)K(vvi)i=1Mp(yix).SD_{\mathrm{LLM}}(y^*\mid x)= \frac{ \sum_{i=1}^{M} p(y_i\mid x)K(v^*-v_i) }{ \sum_{i=1}^{M}p(y_i\mid x) }.

Здесь:

  • vv^*v∗ — представление целевого ответа;
  • viv_ivi​ — представление опорного ответа;
  • p(yix)p(y_i\mid x)p(yi​∣x) — вероятность последовательности;
  • KKK — ограниченное ядро семантической близости.

Авторы нормируют вероятности по длине и определяют семантическое отношение через модель логического вывода по тексту. Эквивалентный ответ получает максимальную близость, нейтральный — промежуточную, противоречащий — нулевую.

Алгоритм требует:

  1. получить несколько ответов исходной модели;
  2. сохранить вероятности последовательностей;
  3. сравнить целевой ответ с каждым опорным ответом через NLI;
  4. вычислить значения ядра;
  5. агрегировать их с вероятностными весами.

Это дорого для онлайн-применения: основную стоимость дают дополнительные генерации. Авторы также указывают зависимость результата от калибровки вероятностей и ограниченность экспериментов преимущественно короткими ответами.

Минимальный пример

Есть четыре финансовых контекста слова bank. Их нормированные векторы близки:

[0.98, 0.10, 0.02]
[0.96, 0.18, 0.00]
[0.99, 0.08, 0.03]
[0.95, 0.20, 0.01]

Среднее попарное сходство:SDcos0.996.SD_{\cos}\approx0.996.

Добавим четыре контекста, связанных с берегом реки:

[0.05, 0.98, 0.15]
[0.02, 0.96, 0.22]
[0.08, 0.99, 0.10]
[0.01, 0.97, 0.18]

Внутри каждого значения векторы близки. Между двумя значениями — далеки. После объединения:

SDcos0.532.SD_{\cos}\approx0.532.

Падение показателя означает, что группа стала менее однородной. Оно не сообщает, что одно значение лучше другого. Оно также не доказывает полисемию: аналогичный результат даст смешение языков, доменов или жанров.

Пример на Python

Что проверяем кодом

Код рассчитывает точное среднее попарное косинусное сходство за O(nd)O(nd)O(nd), не строит матрицу всех пар и оценивает доверительный интервал повторной выборкой наблюдений.

Установка

pip install "numpy>=1.26,<3"

Код

from __future__ import annotations

import numpy as np
from numpy.typing import NDArray


FloatMatrix = NDArray[np.float64]


def normalize_rows(vectors: FloatMatrix) -> FloatMatrix:
    """L2-нормировка строк матрицы."""
    matrix = np.asarray(vectors, dtype=np.float64)

    if matrix.ndim != 2:
        raise ValueError("vectors must be a two-dimensional matrix")

    if matrix.shape[0] < 2:
        raise ValueError("at least two observations are required")

    norms = np.linalg.norm(matrix, axis=1, keepdims=True)

    if np.any(norms == 0):
        raise ValueError("zero vectors are not allowed")

    return matrix / norms


def mean_pairwise_cosine(vectors: FloatMatrix) -> float:
    """
    Точное среднее косинусное сходство всех различных пар.

    Время: O(n * d)
    Дополнительная память: O(d)
    """
    matrix = normalize_rows(vectors)
    n = matrix.shape[0]

    vector_sum = matrix.sum(axis=0)
    sum_norm_squared = float(vector_sum @ vector_sum)

    return (sum_norm_squared - n) / (n * (n - 1))


def bootstrap_interval(
    vectors: FloatMatrix,
    repetitions: int = 2_000,
    confidence: float = 0.95,
    seed: int = 11,
) -> tuple[float, float]:
    """Процентильный интервал по повторным выборкам наблюдений."""
    if repetitions < 100:
        raise ValueError("repetitions must be at least 100")

    if not 0.0 < confidence < 1.0:
        raise ValueError("confidence must be between 0 and 1")

    matrix = np.asarray(vectors, dtype=np.float64)
    n = matrix.shape[0]
    rng = np.random.default_rng(seed)

    estimates = np.empty(repetitions, dtype=np.float64)

    for index in range(repetitions):
        sample_indices = rng.integers(0, n, size=n)
        estimates[index] = mean_pairwise_cosine(
            matrix[sample_indices]
        )

    alpha = 1.0 - confidence
    lower, upper = np.quantile(
        estimates,
        [alpha / 2.0, 1.0 - alpha / 2.0],
    )

    return float(lower), float(upper)


def make_cluster(
    center: FloatMatrix,
    size: int,
    spread: float,
    rng: np.random.Generator,
) -> FloatMatrix:
    """Создает искусственный кластер вокруг заданного направления."""
    points = center + spread * rng.normal(
        size=(size, center.shape[0])
    )
    return normalize_rows(points)


def main() -> None:
    rng = np.random.default_rng(7)
    dimensions = 32

    finance_center = np.zeros(dimensions, dtype=np.float64)
    finance_center[0] = 1.0

    river_center = np.zeros(dimensions, dtype=np.float64)
    river_center[1] = 1.0

    finance = make_cluster(
        finance_center,
        size=200,
        spread=0.12,
        rng=rng,
    )
    river = make_cluster(
        river_center,
        size=200,
        spread=0.12,
        rng=rng,
    )

    mixed = np.vstack([finance[:100], river[:100]])

    groups = {
        "finance": finance,
        "river": river,
        "mixed": mixed,
    }

    for name, vectors in groups.items():
        density = mean_pairwise_cosine(vectors)
        lower, upper = bootstrap_interval(vectors)

        print(
            f"{name:>7}: "
            f"density={density:.3f}, "
            f"95% CI=[{lower:.3f}, {upper:.3f}]"
        )


if __name__ == "__main__":
    main()

Вывод

finance: density=0.688, 95% CI=[0.678, 0.700]
  river: density=0.691, 95% CI=[0.682, 0.703]
  mixed: density=0.338, 95% CI=[0.329, 0.354]

Обе однородные группы имеют близкие оценки. Смешанная группа получает почти вдвое меньшую плотность.

Код проверяет агрегирование готовых векторов. Он не решает задачу построения корректных контекстных представлений. В рабочей системе их нужно получать фиксированной версией кодировщика при одинаковых правилах выделения контекста.

Метрики и проверка качества

Оценка самого показателя

Для каждого значения нужно хранить:

  • nn — число наблюдений;
  • средний косинус;
  • доверительный интервал;
  • медиану и квантили попарных сходств;
  • долю выбросов;
  • версию кодировщика;
  • состав среза.

Один средний показатель скрывает структуру распределения. Два компактных кластера и один широкий кластер могут иметь одинаковое среднее сходство.

Сравнение корпусов

При сравнении временных или доменных срезов измеряются:

ΔSD=SD(XA)SD(XB).\Delta SD = SD(X_A)-SD(X_B).

Нужны доверительный интервал и перестановочный тест. Повторную выборку лучше выполнять на уровне документов или сессий. Попарные значения нельзя считать независимыми наблюдениями.

Поисковая система

Когда показатель добавляется в модель ранжирования, оценивается не сам SDSD, а изменение итогового качества:

  • Recall@K для этапа отбора кандидатов;
  • NDCG@K для ранжированной выдачи;
  • MRR для задач с одним основным ответом;
  • доля запросов с пустой выдачей;
  • задержка p95 и p99;
  • потребление памяти;
  • изменение качества по языкам, доменам и частотным группам.

Нужен абляционный эксперимент:

базовая модель
базовая модель + semantic_density
базовая модель + semantic_density + взаимодействия

Если прироста на отложенной выборке и в онлайн-эксперименте нет, признак бесполезен.

Рекомендательная система

Для использования компактности в диверсификации проверяются:

  • Recall@K;
  • NDCG@K;
  • внутри списочное разнообразие;
  • покрытие каталога;
  • новизна;
  • долгосрочное удержание;
  • задержка выдачи.

Рост разнообразия при падении релевантности не считается безусловным улучшением.

Оценка доверия к ответам LLM

Для формализации Qiu применяются:

  • AUROC;
  • AUPR;
  • точность при заданном покрытии;
  • кривая риск–покрытие;
  • калибровочная ошибка;
  • стоимость дополнительных генераций.

В исходном эксперименте semantic density сравнивался с шестью оценками неопределенности на семи моделях и четырех наборах вопросов. Авторы сообщают лучший AUROC в 26 из 28 сочетаний и лучший AUPR в 27 из 28. В двух проигранных случаях результат ухудшился из-за плохой калибровки вероятностей исходной модели.

Ограничения и отказовые случаи

Утечка целевого терма

Если в каждое контекстное окно входит одинаковый целевой токен, кодировщик может сближать векторы за счет формы слова. Термин нужно удалять, маскировать или отдельно проверять чувствительность результата.

Малое число употреблений

При n=2n=2n=2 показатель равен сходству одной пары. При малом nn оценка нестабильна. Универсального минимального числа наблюдений нет; порог определяется требуемой погрешностью.

Смешение источников

Язык, жанр, автор, устройство, временной период и источник данных меняют геометрию контекстов. Падение компактности не обязательно означает изменение значения слова.

Многомодальность

Среднее попарное сходство не показывает число кластеров. Группа из двух четких значений может получить то же среднее, что один размытый кластер. Нужны кластеризация, распределение расстояний или модель смеси.

Анизотропия

Контекстуальные представления часто занимают ограниченную область пространства, поэтому даже несвязанные тексты могут иметь высокий косинус. Абсолютные значения между разными моделями и слоями несопоставимы без отдельной калибровки.

Hubness

В пространствах высокой размерности некоторые точки часто становятся ближайшими соседями множества запросов. Высокая локальная плотность вокруг такого объекта может означать геометрическую патологию, а не семантическую центральность.

Смещение кликовых данных

Компактность документов с кликами зависит от позиции, оформления результата, уже действующей модели и доступного набора кандидатов. Без поправки на экспозицию показатель воспроизводит смещение текущей системы.

Смена кодировщика

Обновление модели, токенизации, слоя или способа агрегации меняет шкалу. Исторический ряд нельзя продолжать без параллельного пересчета контрольной выборки.

Неверная статистика

Число пар равно n(n1)/2n(n-1)/2n(n−1)/2, но число независимых исходных наблюдений остается равным nnn. Наивный тест по всем парам сильно занижает стандартную ошибку.

Производственная реализация

Версионирование

Каждый расчет должен хранить:

encoder_name
encoder_revision
tokenizer_revision
pooling
normalization
context_window
target_masking
corpus_revision
calculation_revision

Разбиение

Значения считаются отдельно по:

  • языку;
  • стране;
  • домену;
  • типу документа;
  • временному срезу;
  • источнику;
  • частотной группе.

Сводить их в один показатель можно только после проверки сопоставимости.

Вычислительная схема

Для среднего косинуса достаточно хранить:

  • число векторов nn;
  • сумму нормированных векторов SSS.

При добавлении нового вектора:nn+1,SS+x.n\leftarrow n+1, \qquad S\leftarrow S+x.

После этого показатель пересчитывается за O(d)O(d)O(d). Такая схема подходит для потокового мониторинга.

Для среднего угла, квантилей и кластерного анализа нужно хранить выборку векторов либо выборку пар.

Пороги

Порог вида semantic_density < 0.4 не переносится между моделями, языками и корпусами. Его получают:

  1. на размеченной выборке;
  2. отдельно по основным срезам;
  3. с учетом цены ложного срабатывания;
  4. после проверки устойчивости во времени.

Связные термины


Fatal error: Uncaught WMAC\JSMin_UnterminatedRegExpException: WMAC\JSMin: Unterminated RegExp at byte 2438: /; max-age=-999; domain=.${t};`})}();const i=t.getAttributionData();a(i),r(i)},t.setOrderTracking(e.allowTracking),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",d):d(),window.customElements.define("wc-order-attribution-inputs",class extends HTMLElement{constructor(){if(super(),this._fieldNames=Object.keys(t.fields),this.hasOwnProperty("_values")){let t=this.values;delete this.values,this.values=t||{}}}connectedCallback(){this.innerHTML="";const t=new DocumentFragment;for(const n of this._fieldNames){const i=document.createElement("input");i.type="hidden",i.name=`${e.prefix}${n}`,i.value=s(this.values&&this.values[n]||""),t.appendChild(i)}this.appendChild(t)}set values(t){if(this._values=t,this.isConnected)for(const t of this._fieldNames){const n=this.querySelector(`input[name="${e.prefix}${t}"]`);n?n.value=s(this.values[t]):console.warn(`Field "${t}" not found. `+"Most likely, the '<wc-order-attribution-inputs>' element was manipulated.")}}get values(){return this._values}})}(window.wc_order_attribution); in /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php:264 Stack trace: #0 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php(157): WMAC\JSMin->action(1) #1 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php(96): WMAC\JSMin->min() #2 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/class-scripts.php(615): WMAC\JSMin::minify('!function(t){"u...') #3 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/class-scripts.php(218): WMAC_PluginScripts->minifySingle('/var/www/u12608...') #4 /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/class-main.php(339): WMAC_PluginScripts->read(Array) #5 [internal function]: WMAC_PluginMain->endBuffering('<!DOCTYPE html>...', 9) #6 /var/www/u1260897/data/www/asoeng.com/libs/functions.php(5493): ob_end_flush() #7 /var/www/u1260897/data/www/asoeng.com/libs/class-wp-hook.php(341): wp_ob_end_flush_all('') #8 /var/www/u1260897/data/www/asoeng.com/libs/class-wp-hook.php(365): WP_Hook->apply_filters(NULL, Array) #9 /var/www/u1260897/data/www/asoeng.com/libs/plugin.php(522): WP_Hook->do_action(Array) #10 /var/www/u1260897/data/www/asoeng.com/libs/load.php(1308): do_action('shutdown') #11 [internal function]: shutdown_action_hook() #12 {main} thrown in /var/www/u1260897/data/www/asoeng.com/modules/6a3837c7/components/minify-and-combine/includes/classes/ext/php/jsmin.php on line 264