Создание прототипа интеллектуальной поисковой системы

Вступление

Семантический поиск - это система поиска информации, которая фокусируется на значении предложений, а не на обычном сопоставлении ключевых слов. Несмотря на то, что для этой цели можно использовать множество встраиваемых текстов, их масштабирование для создания API с низкой задержкой, которые могут извлекать данные из огромной коллекции данных, редко обсуждается. В этой статье я расскажу, как реализовать минимальную семантическую поисковую систему, используя вложения предложений SOTA (преобразователь предложений) и FAISS.

Трансформаторы предложений

Это структура или набор моделей, которые дают плотное векторное представление предложений или абзацев. Эти модели представляют собой сети-преобразователи (BERT, RoBERTa и т. Д.), Которые настроены специально для задачи семантического текстового сходства, поскольку BERT не справляется с этими задачами. Ниже приведены характеристики различных моделей в тесте STS.

Мы видим, что модели преобразователей Sentence намного превосходят другие модели.

Но если вы посмотрите на таблицу лидеров по бумагам с кодом и GLUE, вы увидите много моделей выше 90. Так зачем нам трансформеры предложений ?.

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

  1. Когда вам нужно выполнить поиск, скажем, в 10k документах, вам нужно будет выполнить 10k отдельных вычислений вывода, невозможно вычислить вложения отдельно и вычислить только косинусное сходство. См. Объяснение автора.
  2. Максимальная длина последовательности (общее количество слов / токенов, которые модель может принять за один проход) распределяется между двумя документами, что приводит к размыванию представлений из-за разбиения на фрагменты.

FAISS

Faiss - это библиотека на основе C ++, созданная Facebook AI с полной оболочкой на Python, для индексации векторизованных данных и выполнения эффективного поиска по ним. Faiss предлагает различные индексы, основанные на следующих факторах

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

Таким образом, выбор правильного индекса будет компромиссом между этими факторами.

Загрузка модели и выполнение логического вывода для набора данных

Для начала установим и загрузим необходимые библиотеки.

!pip install faiss-cpu
!pip install -U sentence-transformers
import numpy as np
import torch
import os
import pandas as pd
import faiss
import time
from sentence_transformers import SentenceTransformer

Загрузка набора данных с миллионом точек данных

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

df=pd.read_csv("abcnews-date-text.csv")
data=df.headline_text.to_list()

Загрузка предварительно обученной модели и выполнение логического вывода

model = SentenceTransformer('distilbert-base-nli-mean-tokens')
encoded_data = model.encode(data)

Индексирование набора данных

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

Определим индекс и добавим в него данные

index = faiss.IndexIDMap(faiss.IndexFlatIP(768))
index.add_with_ids(encoded_data, np.array(range(0, len(data))))

Сериализация индекса

faiss.write_index(index, 'abc_news')

Затем сериализованный индекс можно экспортировать на любой компьютер для размещения поисковой системы.

Десериализация индекса

index = faiss.read_index('abc_news')

Выполнение поиска семантического сходства

Давайте сначала создадим функцию-оболочку для поиска

def search(query):
   t=time.time()
   query_vector = model.encode([query])
   k = 5
   top_k = index.search(query_vector, k)
   print('totaltime: {}'.format(time.time()-t))
   return [data[_id] for _id in top_k[1].tolist()[0]]

выполнение обыска

query=str(input())
results=search(query)
print('results :')
for result in results:
   print('\t',result)

Результаты на CPU

Теперь посмотрим на результаты поиска и время отклика.

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

Результаты на GPU

Во-первых, давайте удалим версию Faiss для процессора и переустановим версию для графического процессора.

!pip uninstall faiss-cpu
!pip install faiss-gpu

Затем выполните ту же процедуру, но в конце переместите индекс в GPU.

res = faiss.StandardGpuResources()
gpu_index = faiss.index_cpu_to_gpu(res, 0, index)

Теперь поместим это в функцию поиска и выполним поиск с помощью графического процессора.

Правильно, вы можете получить результаты в течение 0,02 секунды с помощью графического процессора (в этом эксперименте используется Tesla T4), что в 75 раз быстрее, чем серверная часть процессора.

Но почему я не могу просто сериализовать массив закодированных данных NumPy вместо их индексации и использовать косинусное сходство, если я могу подождать несколько секунд?

Поскольку NumPy не имеет встроенных функций сериализации, единственный способ - преобразовать его в JSON, а затем сохранить объект JSON, но тогда размер увеличится в пять раз. Например, миллион точек данных, закодированных в 768-мерное векторное пространство с нормальной индексацией, будет составлять около 3 ГБ, преобразование в JSON сделает его 15 ГБ, что обычная машина не может удержать в своей оперативной памяти. Таким образом, нам придется запускать миллион вычислений каждый раз, когда мы выполняем поиск, что непрактично.

Последние мысли

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

использованная литература

[1] Нильс Реймерс и Ирина Гуревич. « Делаем одноязычные вложения предложений многоязычными с помощью дистилляции знаний . arXiv (2020): 2004.09813.

[2] Джонсон, Джефф и Дуз, Маттис и Джей Гоу, Херв {е} . « Миллиардный поиск сходства с графическими процессорами » препринт arXiv arXiv: 1702.08734 .