Примеры использования универсального кодировщика предложений Google в рабочей среде

Перед построением любой модели глубокого обучения в обработке естественного языка (NLP) встраивание текста играет важную роль. Встраивание текста преобразует текст (слова или предложения) в числовой вектор.

Почему мы конвертируем тексты в векторы?

Вектор - это массив чисел определенной размерности. Вектор размером 5 × 1 содержит 5 чисел, и мы можем представить его как точку в пространстве 5D. Если есть два вектора размерности 5 каждый, их можно представить как две точки в пространстве 5D. Таким образом, мы можем вычислить, насколько близки или удалены эти два вектора, в зависимости от меры расстояния между ними.

Следовательно, много усилий в исследованиях машинного обучения прилагается к преобразованию данных в вектор, поскольку после преобразования данных в вектор мы можем сказать, что две точки данных похожи или нет, вычислив расстояние до них. Такие методы, как Word2vec и Glove, делают это путем преобразования слова в вектор. Таким образом, соответствующий вектор «кошка» будет ближе к «собаке», чем «орлу». Но при встраивании предложения вместе со словами в этом векторе должен быть зафиксирован контекст всего предложения. Именно здесь на сцену выходит «Универсальный кодировщик предложений».

Универсальный кодировщик предложений кодирует текст в векторы большой размерности, которые можно использовать для классификации текста, семантического сходства, кластеризации и других задач естественного языка. Предварительно обученный универсальный кодировщик предложений находится в открытом доступе в Tensorflow-hub. Он поставляется в двух вариантах: один обучен с помощью Transformer encoder, а другой - с помощью сети глубокого усреднения (DAN). У обоих есть компромисс между точностью и требованиями к вычислительным ресурсам. В то время как датчик с датчиком Transformer имеет более высокую точность, он требует больших вычислений. Тот, у которого есть кодирование ДНК, дешевле в вычислительном отношении и с немного меньшей точностью.

Здесь мы перейдем к версии кодировщика трансформатора. Он хорошо сработал для нас, когда мы запустили его вместе с 5 другими тяжелыми моделями глубокого обучения в экземпляре оперативной памяти 5 ГБ. Кроме того, мы могли бы обучить классификатор с 1,5 миллионами данных, используя эту версию универсального кодировщика предложений на уровне встраивания. Я встретил несколько примеров использования универсального кодировщика предложений:

  1. В качестве слоя встраивания в начале модели глубокого обучения.
  2. Выполнение классификации путем поиска семантически похожих предложений.
  3. Избавление от повторяющихся предложений или фраз перед анализом.

Давайте посмотрим, как использовать предварительно обученный универсальный кодировщик предложений, доступный на Tensorflow-hub, для вышеупомянутых случаев использования в python.

Во-первых, давайте импортируем необходимые библиотеки:

import tensorflow as tf
import tensorflow_hub as hub

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

#download the model to local so it can be used again and again
!mkdir ../sentence_wise_email/module/module_useT
# Download the module, and uncompress it to the destination folder. 
!curl -L "https://tfhub.dev/google/universal-sentence-encoder-large/3?tf-hub-format=compressed" | tar -zxvC ../sentence_wise_email/module/module_useT

Здесь «../sentence_wise_email/module/module_useT» - это папка, в которую загружаются файлы кодировщика предложений. Кодировщик оптимизирован для текста, длина которого превышает слово, поэтому его можно применять к предложениям, фразам или коротким абзацам.

Например (пример официального сайта):

embed = hub.Module("../sentence_wise_email/module/module_useT")
# Compute a representation for each message, showing various lengths supported.
word = "Elephant"
sentence = "I am a sentence for which I would like to get its embedding."
paragraph = (
    "Universal Sentence Encoder embeddings also support short paragraphs. "
    "There is no hard limit on how long the paragraph is. Roughly, the longer "
    "the more 'diluted' the embedding will be.")
messages = [word, sentence, paragraph]
# Reduce logging output.
tf.logging.set_verbosity(tf.logging.ERROR)
with tf.Session() as session:
    session.run([tf.global_variables_initializer(), tf.tables_initializer()])
    message_embeddings = session.run(embed(messages))
for i, message_embedding in enumerate(np.array(message_embeddings).tolist()):
        print("Message: {}".format(messages[i]))
        print("Embedding size: {}".format(len(message_embedding)))
        message_embedding_snippet = ", ".join((str(x) for x in        message_embedding[:3]))
        print("Embedding[{},...]\n".
                   format(message_embedding_snippet))

Результат, который он дает:

Message: Elephant
Embedding size: 512
Embedding: [0.04498474299907684, -0.05743394419550896, 0.002211471786722541, ...]

Message: I am a sentence for which I would like to get its embedding.
Embedding size: 512
Embedding: [0.05568016692996025, -0.009607920423150063, 0.006246279925107956, ...]

Message: Universal Sentence Encoder embeddings also support short paragraphs. There is no hard limit on how long the paragraph is. Roughly, the longer the more 'diluted' the embedding will be.
Embedding size: 512
Embedding: [0.03874940797686577, 0.0765201598405838, -0.0007945669931359589, ...]

Как можно видеть, является ли это словом, предложением или фразой, кодировщик предложений может дать вектор встраивания размером 512.

Как использовать в Rest API

При использовании в Rest API вам придется вызывать его несколько раз. Вызов модуля и сеанса снова и снова займет очень много времени. (~ 16 с на каждый звонок из нашего тестирования). Одно можно сделать, это вызвать модуль и создать сеанс в начале, а затем продолжить его повторное использование. (Первый вызов занимает ~ 16 секунд, а затем последовательные вызовы ~ 0,3 секунды).

#Function so that one session can be called multiple times. 
#Useful while multiple calls need to be done for embedding. 
import tensorflow as tf
import tensorflow_hub as hub
def embed_useT(module):
    with tf.Graph().as_default():
        sentences = tf.placeholder(tf.string)
        embed = hub.Module(module)
        embeddings = embed(sentences)
        session = tf.train.MonitoredSession()
    return lambda x: session.run(embeddings, {sentences: x})
embed_fn = embed_useT('../sentence_wise_email/module/module_useT')
messages = [
    "we are sorry for the inconvenience",
    "we are sorry for the delay",
    "we regret for your inconvenience",
    "we don't deliver to baner region in pune",
    "we will get you the best possible rate"
]
embed_fn(messages)

На выходе получается матрица размером 5 * 512. (каждое предложение представляет собой вектор размером 512). Поскольку значения нормализованы, внутренний продукт кодировок можно рассматривать как матрицу подобия.

encoding_matrix = embed_fn(messages)
import numpy as np
np.inner(encoding_matrix, encoding_matrix)

Результат:

array([[1.        , 0.87426376, 0.8004891 , 0.23807861, 0.46469775],
       [0.87426376, 1.0000001 , 0.60501504, 0.2508136 , 0.4493388 ],
       [0.8004891 , 0.60501504, 0.9999998 , 0.1784874 , 0.4195464 ],
       [0.23807861, 0.2508136 , 0.1784874 , 1.0000001 , 0.24955797],
       [0.46469775, 0.4493388 , 0.4195464 , 0.24955797, 1.0000002 ]],
      dtype=float32)

Как видно здесь, сходство между фразами «извините за неудобства» и «извините за задержку» составляет 0,87 (строка-1, столбец-2), в то время как сходство между «извините за неудобства» »И« мы предложим вам наилучшую возможную цену »составляет 0,46 (строка-1, столбец-5), что потрясающе. Есть и другие способы найти оценку сходства по кодировкам, таким как косинусное сходство, манхэттенское расстояние и т. Д. (Код есть в моем репозитории Github, упомянутом в конце статьи).

Удаление повторяющихся текстов

При работе над системой проверки ответов на вопрос одной из основных проблем было повторение формулировок ответов. Просматривая имеющиеся у нас данные, мы обнаружили, что предложения с показателем семантического сходства ›0,8 (вычисленным с помощью внутреннего продукта кодировок, как указано выше) на самом деле являются дублирующими утверждениями, поэтому мы удалили их.

#It takes similarity matrix (generated from sentence encoder) as input and gives index of redundant statements
def redundant_sent_idx(sim_matrix):
    dup_idx = [] 
    for i in range(sim_matrix.shape[0]):
        if i not in dup_idx:
            tmp = [t+i+1 for t in list(np.where( sim_matrix[i][i+1:] > 0.8 )[0])]
            dup_idx.extend(tmp)
    return dup_idx
#indexes of duplicate statements.
dup_indexes  = redundant_sent_idx(np.inner(encoding_matrix,
                                           encoding_matrix))
unique_messages = np.delete(np.array(messages), dup_indexes)

Теперь уникальными сообщениями были:

array(['we are sorry for the inconvenience',
       "we don't deliver to baner region in pune",
       'we will get you the best possible rate'], dtype='<U40')

По сути, он отказался от заявлений «приносим извинения за задержку» и «мы сожалеем о ваших неудобствах», поскольку они являются дубликатом предложения 1.

Выполнение классификации путем поиска семантически похожих предложений

При построении системы оценки ответов мы столкнулись с проблемой выявления последующих утверждений. Но данных для обучения статистической модели было недостаточно. Мы смогли решить проблему благодаря универсальному кодировщику предложений. Мы использовали тот подход, который мы использовали, чтобы создать матрицу кодировок всех доступных данных. Затем получите кодировку пользовательского ввода и посмотрите, похожа ли она более чем на 60% на какие-либо из доступных данных, примите ее как продолжение. Простая идентификация приветствия может быть:

greets = ["What's up?",
 'It is a pleasure to meet you.',
 'How do you do?',
 'Top of the morning to you!',
 'Hi',
 'How are you doing?',
 'Hello',
 'Greetings!',
 'Hi, How is it going?',
 'Hi, nice to meet you.',
 'Nice to meet you.']
greet_matrix = embed_fn(greets)
test_text = "Hey, how are you?"
test_embed = embed_fn([test_text])
np.inner(test_embed, greet_matrix)
sim_matrix  = np.inner(test_embed, greet_matrix)
if sim_matrix.max() > 0.8:
    print("it is a greetings")
else:
    print("it is not a greetings")


Https://tfhub.dev/google/universal-sentence-encoder-large/3