HuggingFace BERT `inputs_embeds` дает неожиданный результат

Реализация TensorFlow HuggingFace BERT позволяет нам использовать предварительно вычисленное встраивание вместо встроенный поиск, свойственный BERT. Это делается с помощью необязательного параметра inputs_embeds метода call модели (вместо 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, кажется, что необработанные вложения, которые просматриваются, когда задано input_ids или назначается, когда задано inputs_embeds, добавляются к позиционным вложениям и вложениям типа токена перед подачей в последующие уровни. Если это так, то может быть возможным, что то, что я получаю от result[-1][0], является необработанным встраиванием плюс встраиваниями позиционного типа и типа токена. Это будет означать, что они ошибочно добавляются снова, когда я кормлю result[-1][0] как inputs_embeds, чтобы вычислить result2.

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

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

person Vivek Subramanian    schedule 02.05.2020    source источник


Ответы (1)


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

inputs_embeds = result[-1][0]

со строками:

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

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

person Vivek Subramanian    schedule 09.05.2020