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