HUGGINGFACE BERT `input_embeds` дает неожиданный результат - PullRequest
0 голосов
/ 03 мая 2020

Реализация HuggingFace BERT TensorFlow позволяет нам вводить предварительно вычисленное вложение вместо поиска встраивания, который является родным для BERT. Это делается с использованием необязательного параметра метода call inputs_embeds (вместо input_ids). Чтобы проверить это, я хотел убедиться, что, если бы я сделал подачу в поиске встраивания BERT, я получил бы тот же результат, что и подача в самих input_ids.

Результат поиска встраивания BERT можно получить, установив для параметра конфигурации BERT output_hidden_states значение True и извлекая первый тензор из последнего вывода метода call. (Остальные 12 выходов соответствуют каждому из 12 скрытых слоев.)

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

import tensorflow as tf
from transformers import BertConfig, BertTokenizer, TFBertModel

bert_tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

input_ids = tf.constant(bert_tokenizer.encode("Hello, my dog is cute", add_special_tokens=True))[None, :]
attention_mask = tf.stack([tf.ones(shape=(len(sent),)) for sent in input_ids])
token_type_ids = tf.stack([tf.ones(shape=(len(sent),)) for sent in input_ids])

config = BertConfig.from_pretrained('bert-base-uncased', output_hidden_states=True)
bert_model = TFBertModel.from_pretrained('bert-base-uncased', config=config)

result = bert_model(inputs={'input_ids': input_ids, 
                            'attention_mask': attention_mask, 
                             'token_type_ids': token_type_ids})
inputs_embeds = result[-1][0]
result2 = bert_model(inputs={'inputs_embeds': inputs_embeds, 
                            'attention_mask': attention_mask, 
                             'token_type_ids': token_type_ids})

print(tf.reduce_sum(tf.abs(result[0] - result2[0])))  # 458.2522, should be 0

Опять, вывод call метод является кортежем. Первым элементом этого кортежа является вывод последнего слоя BERT. Таким образом, я ожидал совпадения result[0] и result2[0]. Почему это не так?

Я использую Python 3.6.10 с tensorflow версии 2.1.0 и transformers версии 2.5.1.

РЕДАКТИРОВАТЬ : Глядя на некоторые из HuggingFace code , кажется, что необработанные вложения, которые ищутся, когда input_ids задано или назначено, когда inputs_embeds добавлены, добавляются в позиционные вложения и вложения типа токена перед подачей в последующие слои. Если это так, то возможно возможно, что то, что я получаю от result[-1][0], - это необработанные встраивания плюс вложения позиционного и токенового типов. Это будет означать, что они ошибочно добавляются снова, когда я кормлю result[-1][0] как inputs_embeds, чтобы вычислить result2.

Может кто-нибудь сказать, если это так, и если поэтому, пожалуйста, объясните, как получить вложения позиционного и токенового типов, чтобы я мог их вычесть? Ниже приведено то, что я придумал для вложения в позициях, основанные на уравнениях, приведенных здесь (но согласно бумага BERT , позиционные встраивания могут быть действительно изучены, поэтому я не уверен, что они действительны):

import numpy as np

positional_embeddings = np.stack([np.zeros(shape=(len(sent),768)) for sent in input_ids])
for s in range(len(positional_embeddings)):
    for i in range(len(positional_embeddings[s])):
        for j in range(len(positional_embeddings[s][i])):
            if j % 2 == 0:
                positional_embeddings[s][i][j] = np.sin(i/np.power(10000., j/768.))
            else:
                positional_embeddings[s][i][j] = np.cos(i/np.power(10000., (j-1.)/768.))
positional_embeddings = tf.constant(positional_embeddings)
inputs_embeds += positional_embeddings

1 Ответ

0 голосов
/ 10 мая 2020

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

inputs_embeds = result[-1][0]

на строки:

embeddings = bert_model.bert.get_input_embeddings().word_embeddings
inputs_embeds = tf.gather(embeddings, input_ids)

Теперь разница равна 0,0, как и ожидалось.

...