web-dev-qa-db-ja.com

HuggingFace BERT `inputs_embeds`が予期しない結果を与える

HuggingFace BERT TensorFlow実装 を使用すると、BERTに固有の埋め込みルックアップの代わりに、事前計算された埋め込みをフィードできます。これは、モデルのcallメソッドのオプションパラメータinputs_embedsを使用して行われます(input_idsの代わりに)。これをテストするために、BERTの埋め込みルックアップでdidをフィードした場合、input_ids自体をフィードした場合と同じ結果が得られることを確認したいと思いました。

BERTの埋め込みルックアップの結果は、BERT構成パラメーターoutput_hidden_statesTrueに設定し、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で使用しています。

[〜#〜] edit [〜#〜]HuggingFaceコード の一部を見ると、 input_idsが指定されたときに検索される埋め込み、またはinputs_embedsが指定されたときに割り当てられる埋め込みは、位置埋め込みとトークンタイプ埋め込みに追加されてから、後続のレイヤーにフィードされます。これが事実である場合、mayから得られるのは、result[-1][0]が生の埋め込みと位置およびトークンタイプの埋め込みである可能性があります。これは、result[-1][0]を計算するためにinputs_embedsresult2としてフィードすると、誤って再度追加されることを意味します。

これが当てはまるかどうか教えていただけますか?そうである場合は、位置とトークンタイプの埋め込みを取得する方法を説明して、差し引くことができますか?以下は、与えられた方程式に基づいて位置埋め込みについて思いついたものです here (しかし BERT論文 によると、位置埋め込みは実際に学習される可能性があるため、 mこれらが有効かどうかは不明です):

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
4

追加された位置およびトークンタイプの埋め込みについての私の直感は正しいことが判明しました。 code を詳しく調べた後、次の行を置き換えました。

inputs_embeds = result[-1][0]

次の行で:

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

これで、期待どおり、差は0.0になります。

0