Время прочтения: 8 мин.

Сегодня пойдет речь о методах снижения размерности эмбеддингов для задач определения семантического сходства предложений.

Для чего это необходимо. С каждым годом растет сложность моделей, решающих вопросы семантически- и контекстно-ориентированной обработки естественного языка (NLP). Также нельзя забывать и про проблемы мультиязычности моделей. Все это сильно сказывается на увеличении их размеров и системных требований к железу для их обучения, дообучения, да и просто запуска. Задачи NLP сегодня – это прикладные задачи, их хочется решать на доступном оборудовании за доступное время.

А если поконкретней? Передо мной стояла задача найти и обобщить текстовые данные, представляющие собой массив предложений. Я точно знал, что среди них есть семантически схожие фразы. Однако прямой подход для определения семантического сходства наборов фраз требовал очень много памяти и времени. Чтобы решить эту проблему, я попытался уменьшить размерность векторов признаков предложений, но как понять, когда остановиться и что это даст?

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

Вводные

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

Для решения задачи семантического сходства между предложениями нужно преобразовать их в вектор. Для этого воспользуемся эмбеддингом: на вход подается набор предложений (в нашем случае – два для попарного сравнения), а на выходе преобразуется в числовые векторы этих предложений. Собственно, под эмбеддингом будем понимать результат преобразования текстовой сущности в числовой вектор. Модель преобразования выглядит так:

На сегодня самый простой и эффективный способ создания контекстуализированных эмбеддингов – использовать трансформер, модель машинного обучения, отлично зарекомендовавшую себя в сфере NLP.

Архитектурно трансформеры схожи с RNN (система кодировщик-декодировщик). Кроме того, они также предназначены для обработки последовательностей. Архитектура трансформера выглядит следующим образом:

В отличие от RNN, трансформеры не требуют обработки последовательностей по порядку: если входные данные это текст, то трансформеру не требуется обрабатывать конец текста после обработки его начала. Благодаря этому трансформеры распараллеливаются легче, чем RNN, и оттого выигрывают в скорости обучения. Также важной особенностью данной архитектуры является механизм внимания — он фокусируется на важных с точки зрения контекста словах и отдает их напрямую в обработку. Благодаря этому трансформеры обладают хорошей долгосрочной памятью и лучшим умением учитывать контекст.

Все это делает трансформеры выбором номер один для решения нашей задачи семантического сходства.

Таким образом, получим следующую модель определения семантического сходства:

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

где A и B – n-мерные векторы, θ – угол между ними, A∙B – скалярное произведение векторов A и B, ||A|| и ||B|| – длины векторов в евклидовом пространстве, Ai и Bi – i-ые компоненты векторов A и B соответственно.

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

Схема снижения размерности будет выглядеть следующим образом:

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

А теперь приступим к реализации описанной схемы. Посмотреть весь код можно тут.

В объятиях Hugging Face

Для подбора датасета и создания модели семантического сходства я обратился к замечательной платформе Hugging Face.

STSb Multi MT – это набор мультиязычных переводов и англоязычный оригинал классического STSbenchmark. Датасет состоит из трех колонок: первое предложение, второе предложение и метрика их схожести от 0 до 5 (далее – эталонная оценка). Датасет разбит на 3 части – train, test и dev, т.к. в рамках данной публикации вопросы точной донастройки рассматриваться не будут, то ограничимся dev сплитом в 1,5 тыс. строк русскоязычной части датасета.

# загрузим датасет
df_dev = load_dataset("stsb_multi_mt", name="ru", split="dev")

Первые пять строк датасета:

Среди моделей мой выбор пал на distiluse-base-multilingual-cased-v1 из семейства sentence-transformers.

Архитектура модели представляет собой следующее:

На вход в модель подается предложение. Оно проходит через слой трансформера (DistilBertModel) и преобразуется в эмбеддинг, который через слой пулинга попадает на полносвязный слой Dense с вектором смещения (bias) и тангенсальной активационной функцией. На выходе получаем эмбеддинг с размерностью 512.

Данная модель отображает предложения в 512-мерное векторное пространство.

# загрузим модель
model = SentenceTransformer("distiluse-base-multilingual-cased-v1")

Разберемся с датасетом

Обработаем наш датасет. Для чего нормализуем эталонную оценку, приведя ее к значениям от 0 до 1. Из модели вытащим эмбеддинги и рассчитаем косинусное сходство, которое и послужит основой для сравнения с целевой метрикой датасета и измененной метрикой после снижения размерности.

res = []
F = True
for df in df_dev:
    score = float(df['similarity_score'])/5.0 # нормализация эталонной оценки
    embeddings = model.encode([df['sentence1'], df['sentence2']])
    semantic_sim = 1 - cosine(embeddings[0], embeddings[1]) # косинусное сходство между парами предложений
    res.append([df['sentence1'], df['sentence2'], score, semantic_sim])
    if F == True:
        mas_embed = embeddings
        F = False
    else:    
        mas_embed = np.concatenate((mas_embed, embeddings), axis=0)

Соберем все в единый датафрейм:

df = pd.DataFrame(res, columns=['senetence1', 'sentence2', 'score', 'semantic_sim'])

Внимание! Начинаем снижение

Теперь можем приступить к применению методов снижения размерности.

Я выбрал четыре метода, доступных в модуле scikit-learn.decomposition – Матричная декомпозиция:

  • PCA – Метод главных компонент.
  • FastICA – Быстрый алгоритм для Анализа независимых компонент.
  • Factor Analysis (FA) – Факторный анализ.
  • TruncatedSVD – Усеченное сингулярное разложение.
from sklearn.decomposition import PCA
from sklearn.decomposition import FastICA
from sklearn.decomposition import FactorAnalysis
from sklearn.decomposition import TruncatedSVD

Для сравнения методов между собой и с эталонным значением воспользуемся Евклидовым расстоянием:

где x – вектор эталонных оценок, ym – вектор приближений при сокращении размерности до m, n – число пар предложений, для которых нужно рассчитать косинусное сходство, xi и yi – i-ые элементы соответствующих векторов.

Пример таких данных из датафрейма:

Таким образом, получим вектор эталонных оценок, основной вектор приближений (семантическое сходство), векторы приближений n-ой размерности и i-го метода (например, размерность 50 и метод ICA)

Приведем пример кода для метода ICA:

eucl_dis_ica = []
for el in dims:
    ica = FastICA(n_components = el)
    mas_embed_fit = ica.fit_transform(mas_embed)
    
    # семантическое сходство
    tmp_res = []
    for i in range (0, 3000, 2):
        semantic_sim = 1 - cosine(mas_embed_fit[i], mas_embed_fit[i+1])
        tmp_res.append(semantic_sim)
    
    # евклидово расстояние 
    df[f'reduce_sim_ica_{el}'] = tmp_res
    df['eucl_dis_ica'] = (df['score'] - df[f'reduce_sim_ica_{el}'])**2
    eucl_dis_ica.append(df['eucl_dis_ica'].sum() ** 0.5)

Итого, получим функцию зависимости Евклидова расстояния и числа размерностей. Найдя локальный минимум евклидова расстояния до эталонной оценки на интересующем нас интервале [50; 450] размерностей, получим оптимальное количество размерностей, где нет существенных потерь информации.

Визуализируем рассчитанные данные.

plt.figure(figsize=(12,7.5), dpi= 80)
plt.plot(dims, eucl_dis_pca, color='tab:red', label='PCA')
plt.text(dims[-1], eucl_dis_pca[-1], 'PCA', fontsize=12, color='tab:red')
plt.plot(dims, eucl_dis_ica, color='tab:blue', label='ICA')
plt.text(dims[-1], eucl_dis_ica[-1], 'ICA', fontsize=12, color='tab:blue')
plt.plot(dims, eucl_dis_fa, color='tab:green', label='FA')
plt.text(dims[-1], eucl_dis_fa[-1], 'FA', fontsize=12, color='tab:green')
plt.plot(dims, eucl_dis_tsvd, color='tab:green', label='TSVD')
plt.text(dims[-1], eucl_dis_tsvd[-1], 'TSVD', fontsize=12, color='tab:green')
plt.plot(dims, targ, color='tab:orange', label='Target', linestyle='dashed')

plt.ylabel('Евклидово расстояние')
plt.xlabel('Размерность')
plt.legend(loc='upper right', ncol=2, fontsize=12)

plt.show()

Оранжевая пунктирная линия на графике (target) – это значение евклидова расстояния между вектором эталонных оценок и вектором семантического сходства (т.е. основным вектором приближения без уменьшения размерностей). С этим значением мы и будем сравнивать получившиеся функции методов снижения размерностей.

Из графика видно, что:

  • Алгоритмы ICA и FA отработали лучше всего и приблизились к эталонной оценке даже больше, чем target, с локальным минимумом около 200 размерностей (что в 2.5 раза меньше начальных 512)
  • Алгоритм PCA показал себя чуть хуже, но при этом при 200 размерностях уже совпал с target.
  • Алгоритм TSVC в чистом виде не позволяет эффективно снизить количество размерностей.

Результат

Таким образом, приведенный в данном материале алгоритм позволяет упростить модель за счет уменьшения числа избыточных, слабо информативных признаков, что дает:

Снижение объема обрабатываемых многомерных эмбеддингов. Это также уменьшает объем задействуемой памяти и увеличивает скорость работы дальнейшей обработки этих данных. На конкретном примере сокращение объема данных составило около 60%.

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