Реализация 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